Meteor aldeed-tabular - meteor

i'm trying to get the list of all users using aldeed-tabular but it doesn't work , any help please !
lib/Users.js
TabularTables = {};
Meteor.isClient && Template.registerHelper('TabularTables', TabularTables);
TabularTables.UserList = new Tabular.Table({
name: "Users List",
collection: Meteor.users,
columns: [
{data: "email()", title: "firstName", class: "col-md-1"},
{data: "lastName", title: "lastName", class: "col-md-3"},
]});
**Client/UsersList.js**
<template name="tabular">
{{> tabular table=TabularTables.UserList class="table table-striped table-bordered table-condensed"}}
</template>

Check out this Meteor forum where they discuss displaying users with specific roles. Your case is easier since you just want to get all users.

Related

Meteor, publish:composite. how to access joined data in the template?

So I used publishComposite to do a collection join in Meteor. I have a parent collection (Subscriptions) with a user_id foreign key. I look up the user name in the Meteor.users collection to get the actual username, but how do I actually print this in the html template. My subscription data is there but how do I actually refer to the username?
Here is the publish code:
//publish subscriptions course view
Meteor.publishComposite('adminCourseSubscriptions', function(courseId){
return {
//get the subs for the selected course
find: function(){
return Subscriptions.find(
{course_id: courseId}
);
},
children:
[
{
//get the subscriber details for the course
find: function(sub){
return Meteor.users.find({_id:sub.user_id});
}
}
]
};
});
here are the template subdcriptions:
Template.adminCourseDetail.helpers({
courseDetail: function(id){
var id = FlowRouter.getParam('id');
return Courses.findOne({ _id: id });
},
courseSubscriptions: function(){
var id = FlowRouter.getParam('id');
return Subscriptions.find({course_id:id})
},
users: function(){
return Meteor.users.find();
}
});
and the template (which is garbage) ps the course details come from a separate collection. It was easier and I think more performant to get the details separately and this works fine. It's just the username that I cannot display correctly:
<template name="adminCourseDetail">
<h1>Course Details</h1>
<p>Title: {{courseDetail.title}}</p>
<p>Description: {{courseDetail.description}}</p>
<p>Start Date: {{courseDetail.startDate}}</p>
<p>Number of sessions: {{courseDetail.sessions}}</p>
<p>Duration: {{courseDetail.duration}}</p>
<p>Price: {{courseDetail.price}}</p>
<p>{{userTest}}</p>
edit
delete
<h2>Course Subscriptions</h2>
{{#each courseSubscriptions}}
<div class="row">
<div class="col-md-3">{{username}}</div>
<div class="col-md-3">{{sub_date}}</div>
</div>
{{/each}}
</template>
Thanks in advance for any suggestions!
As far as I understand your question, documents of the Subscriptions collection contain only the attribute user_id, referencing the corresponding user document in the Meteor.users collection. If this is the case, then you need to add an additional template helper which returns the username:
Template.adminCourseDetail.helpers({
// ...
getUsername: function() {
if (this.user_id) {
let user = Meteor.users.find({
_id: this.user_id
});
return user && user.username;
}
return "Anonymous";
}
// ...
});
After that, just replace {{username}} with {{getUsername}}:
<template name="adminCourseDetail">
<!-- ... -->
<h2>Course Subscriptions</h2>
{{#each courseSubscriptions}}
<div class="row">
<div class="col-md-3">{{getUsername}}</div>
<div class="col-md-3">{{sub_date}}</div>
</div>
{{/each}}
<!-- ... -->
</template>
Probably you misunderstood the concept of the reywood:publish-composite package. Using Meteor.publishComposite(...) will just publish a reactive join, but it will not return a new set of joined data.
For anyone else having a similar issue and looking at my specfic example. In my case the following code worked. Based on Matthias' answer:
In the template helper:
getUsername: function() {
let user = Meteor.users.findOne({
_id: this.user_id
});
return user;
}
and then in the template:
{{getUsername.username}}
My each block is looping through the cursor returned from the subscriptions collection rather than the course collection which is why it is simpler than the code Matthias provided.

Dynamic paths in handlebars

I searched through a lot of questions but hasn't found answer.
I use handlebars templates and have data structure:
{
privileged_users: [ "user1", "user2" ],
users: {
user1: { name: "N1" },
user2: { name: "N2" },
user3: { name: "N3" }
}
}
I wan't to output all privileged users with some template. Something like this:
<table>
{{#each privileged_users}}
<tr><td>{{../users.[this].name}}</td></tr>
{{/each}}
</table>
Is it possible without additional helpers?
If it isn't how can I write block helper with changing context to ../users.[this] ?
Register following helper:
Handlebars.registerHelper('lookupProp', function (obj, key, prop) {
return obj[key] && obj[key][prop];
});
Then modify the template like:
<table>
{{#each privileged_users}}
<tr><td>{{lookupProp ../users this 'name'}}</td></tr>
{{/each}}
</table>
Here is the working fiddle.
Previous one is just a simple expression helper.
Now here is a working jsfiddle according to question.
Handlebars has a built-in lookup helper since version 3.0.3.
An alternate to block helper could be Handlebars Partial Context approach i.e. define/register a partial and use it with different context in main template.

Meteor collections: how to connect to different collections referring to each other?

I have two collections:
Contracts = new Mongo.Collection('contracts');
Reminders = new Mongo.Collection('reminders');
These are structured in the database more or less like this:
Contracts:
{
"id": "4432234",
"contract": "C-42432432",
"description": "Description of contract",
"counterpart": "Company name",
"status": "awarded"
},
etc.
Reminders:
{
"name": "Contract expiring",
"type": "expiring",
"contract": "C-42432432",
"reminderDate": "2015-06-01",
"urgency": 3
},
etc.
The "contract"-name here are referring to a "contract"-name in the contract-collection. We can ha multiple reminders connected to the same contract. Therefore I want them in two different collections.
To get the contract-data I use:
<template name="example">
{{#each contracts}}
{{description}}
{{/each}}
</template>
corresponding js:
Template.example.helpers({
contracts: function() {
return Contracts.find();
}
});
This works ok and the result in this instance is Description of contract.
But what do I do if I want to display the reminder-collection and get the corresponding data from the Contract-collection? In other words: I want to loop the reminder-collection and get the same output.
<template name="notworking">
{{#each reminder}}
{{description}}
<!-- Here I want the description from the Contract-collection -->
{{/each}}
</template>
corresponding js:
Template.notworking.helpers({
reminder: function() {
//?
}
});
You might be better off using Contracts._id to refer to a contract from the Reminders collection that way if the contract name and description change at some point you won't need to update all the related reminders.
Contract:
{
"_id": "tPN5jopkzLDbGypBu",
"contract": "C-42432432",
"description": "Description of contract",
"counterpart": "Company name",
"status": "awarded"
},
Reminder:
{
"name": "Contract expiring",
"type": "expiring",
"contractId": "tPN5jopkzLDbGypBu",
"reminderDate": "2015-06-01",
"urgency": 3
},
Then if you want to list reminders and show related contract information you'd have:
HTML:
<template name="remindersList>
{{#each reminder}}
Date: {{reminderDate}} Urgency: {{urgency}}
{{#with relatedContract}}
Description: {{description}} Counterpart: {{counterpart}} Status: {{status}}
{{/with}}
{{/each}}
</template>
JS:
Template.remindersList.helpers({
reminder: function(){
return Reminders.find(); // or whatever query you need to run
},
relatedContract: function(){
return Contracts.findOne({_id: this.contractId}); // 'this' will be the Reminder document
}
});
OR - if you want to keep your denormalized schema, then the relatedContract function would simply need to return Contracts.findOne({contract: this.contract})
I'm assuming you need to loop over each reminder inside the loop iterating over each contract :
<template name="contracts">
{{#each contracts}}
<p>{{description}}</p>
{{#each reminders}}
<p>{{description}}</p>
{{/each}}
{{/each}}
</template>
The current data context when evaluating the reminders will be the currently iterated over contract, so we can access its contract name using this.contract.
As a result you simply need to return every reminders document having the same contract name.
Template.contracts.helpers({
reminders: function(){
return Reminders.find({
contract: this.contract
});
}
});
https://atmospherejs.com/reywood/publish-composite
This package provides flexible way to publish a set of related documents from various collections using a reactive join. Hope it helps.

Collections not showing on page

I have published a countries collection which I want to show in a table in the countriesList template. So I have added this to the router:
Router.route('/countries_list', {
name: 'countriesList',
waitOn: function() {
return Meteor.subscribe('countries');
},
data: function() {
return Countries.find();
}
});
And this is how the template looks like:
{{#each countries}}
<tr>
<td>{{name}}</td>
</tr>
{{/each}}
But the page stays empty. However, the collection is filled on the client, if I check the browser console and do Countries.findOne();, I get this result:
Object {_id: "WWJhMBne4CiEdbbdg", name: "England"}
So what am I doing wrong here?
You template is making the assumption that the cursor is being stored in coutries. It isn't. It would be if your data hook looked like:
return {countries: Countries.find()};
As your code is written, the cursor is the context for your template so this should work:
{{#each this}}
<tr>
<td>{{name}}</td>
</tr>
{{/each}}

How do I get templates inserted from custom block helpers to be individually rerendered in Meteor?

When I use the built-in block helper #each, book templates are rerendered individually when changed:
users =
_id: 'foo'
books: [
{name: 'book1'}
{name: 'book2'}
]
<template name="user">
{{#each books}}
{{> book}}
{{/each}}
</template>
<template name="book">
<div>{{name}}</div>
</template>
When the data is changed - the first book name is set to 'bookone' instead of 'book1' - only the book template (the div containing 'book1') is rerendered. This is the desired behavior. When I use a custom block helper, the behavior is different:
<template name="user">
{{#each_with_id}}
{{> book}}
{{/each}}
</template>
<template name="book">
<div data-id="{{_id}}">{{name}}</div>
</template>
Templates.user.each_with_id = (options) ->
html = "
for book, i in this.books
this.name = book.name
html += Spark.labelBranch i.toString(), ->
options.fn this
html
Now when the name of the first book changes, the whole user template is rerendered.
It does not work as you expect, because the implementation of built-in each is based on the cursor.observeChanges feature. You will not be able to achieve the same exact result without using an auxiliary collection of some sort. The idea is quite simple. It seems that you don't have a "books" collection but you can create a client-side-only cache:
Books = new Meteor.Collection(null);
where you will need to put some data dynamically like this:
Users.find({/* probably some filtering here */}).observeCanges({
added: function (id, fields) {
_.each(fields.books, function (book, index) {
Books.insert(_.extend(book, {
owner: id,
index: index,
}));
}
},
changed: function (id, fields) {
Books.remove({
owner:id, name:{
$nin:_.pluck(fields.books, 'name')
},
});
_.each(fields.books, function (book, index) {
Books.update({
owner : id,
name : book.name,
}, {$set:_.extend(book, {
owner : id,
index : index,
})}, {upsert:true});
}
},
removed: function (id) {
Books.remove({owner:id});
},
});
Then instead of each_with_id you will be able to the built-in each with appropriate cursor, e.g.
Books.find({owner:Session.get('currentUserId')}, {sort:{index:1}});
You may also look at this other topic which basically covers the same problem you're asking about.

Resources