I have a dropdown box which will change number of items of a collection to be displayed on the page...
{{#each myCollection}}
<div id="{{this.Name}}">{{this.Name}} </div>
{/each}}
<select id="ddpFilter" >
<option value="10">Show 10</option>
<option value="50">Show 50</option>
</select>
Check out the first() Underscore method baked into Backbone Collections. Here's an example of how you might combine first() with your template:
Backbone.View.extend({
// Number of items to show from the collection. Set this and re-render when you
// want to show 50.
limit: 10,
// Notice I switched myCollection to items. Backbone collections aren't directly
// iterable, but their underscore methods (like first(n)) return vanilla
// iterable Arrays
template: Handlebars.compile("{{#each items}} {{this.Name}} {/each}}"),
render: function() {
this.$el.html(this.template({
// Pass a truncated Array into the template to keep it logicless and Mustachy
items: myCollection.first(this.limit)
}));
}
});
Related
I'm trying to study Meteor with a simple example to populate two dropdowns where one is a list of fruit and another one is the color according to the fruit chosen:
collection (ProductList):
a = new Array();
a.push({color:"orange"});
a.push({color:"red"});
ProductList.insert({ product: "Orange", a });
a = new Array();
a.push({color:"green"});
a.push({color:"red"});
ProductList.insert({ product: "Apple", a });
a = new Array();
a.push({color:"green"});
a.push({color:"yellow"})
ProductList.insert({ product: "Banana", a });
html (dropdown menu):
<template name="prodlist">
<select id="category-select">
<option disabled="disabled" selected="selected">Please Select</option>
{{#each prodlist}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</template>
js:
Template.prodlist.events({
"change #category-select": function (event, template) {
var category_prod = $(event.currentTarget).val();
//this return the correct element selected in the dropdown(i.e Orange Apple Banana)
console.log("category : " + category_prod);
var productID = ProductList.findOne({product: category_prod })._id
console.log("current ID: " + productID);
}
});
Please see the ****** above for my first question This has been solved by chrisklaussner. Code updated.
Also I'm trying to feed the ._id (saved to productID) found to the second dropdown:
html:
<template name="colorlist">
<select id="category-select">
<option disabled="disabled" selected="selected">colors</option>
{{#each colorlist}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</template>
js:
Template.colorlist.helpers({
colorlist: function () {
const ans = productID.get();
const product = ProductList.findOne({ _id: ans});
if (product) {
return product.a.map(function (doc) {
return doc.color;
});
}
}
});
Please see the ****** above for my second question This has been resolved.
EDIT: This question has been resolved and the Original post contains the correct answer
You have a mistake in the findOne calls. The first parameter (where you pass {}) is the selector that specifies what you want to fetch. The second one is for options.
For example, ProductList.findOne({ product: category_prod })._id should give you the right product ID. In your case, the selector {} matches all products in the collection and findOne returns the first of them. That's why you always get the same ID.
Following an example from a book, I have in my .js file
lists = new Mongo.Collection("Lists");
if (Meteor.isClient) {
Template.categories.helpers ({
'Category': function(){
return lists.find({}, {sort: {Category: 1}});
}})
and in my html file:
<body>
<div id="categories-container">
{{> categories}}
</div>
</body>
<template name="categories">
<div class="title">my stuff</div>
{{#each lists}}
<div>
{{Category}}
</div>
{{/each}}
</template>
I have input data this data into lists which uses Category for a field using the console:
> lists.insert({Category:"DVDs", items: {Name:"Mission Impossible"
,Owner:"me",LentTo:"Alice"}});
> lists.insert({Category:"Tools", items: {Name:"Linear Compression
Wrench",Owner:"me",LentTo: "STEVE"}});
but it doesn't output the data.
I think the issue lies in the context of your {{#each}}, you seem to be attempting to access the mongo collection itself rather specific fields through a helper function.
Check out this tutorial on spacebars, it’s a pretty good read http://meteorcapture.com/spacebars/ and also the Meteor Docs section http://docs.meteor.com/#/full/livehtmltemplates. I'm fairly new to Meteor myself but hope that helps.
In your example, you use name Category for 2 different things.
It is the name of template helper function that return an array of list items.
and it is the name of a field item inside your list.
Do the {{#each Category}} to run on the array returned by the helper
Inside the each, you should access the {{Category}} or {{item}} list items
You just need to call your helper lists instead of Category and your code will work:
Template.categories.helpers({
lists: function () {
return lists.find({}, {sort: {Category: 1}});
}
});
{{#each lists}} means: Iterate over a collection cursor (the result of a find) or an array which we get by calling the helper lists. The naming here is somewhat confusing because lists also happens to be the name of the collection your are iterating over.
Inside of the each, the context is a lists document. Each document contains a Category field, which you are outputting inside of a div.
Recommended reading:
A Guide to Meteor Templates & Data Contexts
check to run command : meteor list
it is given to you list of packages which is you used
if autopublish package not in the list you want to publish and subscribe your data here's simple example:
//in server
Meteor.publish('lists',function(){
return lists.find({}, {sort: {Category: 1}});
})
//in client
Meteor.subscribe('lists');
For More Deatails : publish subscription
First: The function in your 'categories' helper shouldn't be between quotes (not sure if that may couse problems).
Second: You are using the {{#each}} loop wrong.
You are calling the 'Lists' collection, but you should call the 'Categories' helper.
<template name="categories">
<div class="title">my stuff</div>
{{#each Category}}
<div>
{{Category}}
</div>
{{/each}}
</template>
Its very confusing your helper has the same name as the field you are calling in the #each loop.
I am trying to run the query shown below through a server method, unfortunately the data inside the array returned by this query is not displayed in my template, what am I missing / doing wrong here?
/server/methods.js
Meteor.methods({
'getCategoriesEx': function () {
return Categories.find({children: {$size: 0}},{sort: {name: 1}}).fetch();
}
});
/client/categories.js
Template.Categories.rendered = function() {
};
Template.Categories.helpers({
categories: function() {
Meteor.call('getCategoriesEx', function(error, list){
if(error)
return alert(error.reason);
return list;
});
}
})
/client/categories.html
<div class="col_half">
<label for="categories"> categories </label>
<select id="categories" name="categories" class="sm-form-control">
<option value="">-- Select One --</option>
{{#each categories}}
<option value="{{_id}}">{{name}}</option>
{{/each}}
</select>
</div>
Data returned by the query:
[Object, Object, Object, Object]
0: Object
_id: "Pwmd9wFTf8zs8nbWn"children: Array[0]title: "A1"__proto__: Object
1: Object
2: Object
3: Object
You are not using a reactive data source in your helper. Restructure it to use client side reactive mini mongo queries by publishing your data.
So something like this:
Server:
Meteor.publish("categories", function() { return Categories.find(); }
Client:
Template.Categories.created = function() {
Meteor.subscribe("categories");
};
Template.Categories.helpers({
categories: function() {
return Categories.find({children: {$size: 0}},{sort: {name: 1}});
}
})
Chris's answer is the standard way to do things, however you've specifically stated that you're trying to use a method so I assume there's a reason for that.
The problem is that your helper is returning nothing at the point that it's called (it called the method and returns immediately). At the point that your callback is called back there's nothing to return the data to.
One solution would be to update a reactive variable instead. For example, using the Sessions object.
Also note that helpers can be called frequently. You probably don't really want to be calling the server method each time an helper is called. A better place to put that would be the rendered code. So (untested):
Template.Categories.rendered = function() {
Meteor.call('getCategoriesEx', function(error, list){
if(error)
alert(error.reason);
Session.set('categories', list);
});
}
Template.Categories.helpers({
categoriesResult: function() {
return Session.get('categories')
})
and
<div class="col_half">
<label for="categories"> categories </label>
<select id="categories" name="categories" class="sm-form-control">
<option value="">-- Select One --</option>
{{#each categoriesList}}
<option value="{{_id}}">{{name}}</option>
{{/each}}
</select>
</div>
I am starting to use Iron Router in my Meteor app and its yields for templating.
I've recently run into a problem where I can't start a named yield with a context, as follows:
{{#with context}}
{{yield 'subtemplate'}}
{{/with}}
and get this error Sorry, couldn't find a yield named "subtemplate". Did you define it in one of the rendered templates like this: {{yield "subtemplate"}}?
If I remove the {{#with}} block expression, I am able to render the yield.
Does anyone know of a good way to pass the context to a named yield?
I have posted my problem as an issue on the iron-router github project, but haven't gotten any solution yet.
Would appreciate any help.
EDIT 1/1/2014:
So my code looks like this:
// main template for the route
<div class="form-container">
<div class="form">
{{yield 'section'}}
</div>
</div>
The logic to get the yield section to display
// router.js
ApplyController = RouteController.extend({
before: function() {
var section = this.params.section || 'personal-info';
Session.set('current', section);
},
action: function() {
var section = Session.get('current');
this.render();
this.render(section, {
to: 'section'
});
},
data: function() {
return {
app: Applications.findOne({user: Meteor.userId()})
}
}
});
Example of one of the section template:
<template name="education">
{{#with app}}
<form id="education" name="education" class="fragment" method="post" action="">
<h2>Education</h2>
<div class="form-group">
<label for="college" class="control-label">College/ University</label>
<select class="form-control" id="college" name="college" placeholder="Select a College/ University">
<option value="">Select a College/ University</option>
{{#each colleges}}
<option value="{{slug}}" {{selected slug ../college}}>{{name}}</option>
{{/each}}
</select>
</div>
<!-- other content here -->
</form>
{{/with}}
</template>
Using the {{#with app}} block is how I currently get around this issue, but because I have 10 different section templates, I have to put that in all of them.
You pass a data context in the router using ironrouter. You can't pass it this way because if you pass a route in the router it would override the route's data context.
It might however work with the shark branch of ironRouter which is based off Meteor UI since it uses {{>yield}} instead of {{yield}}.
You can use this though:
Route specific data context
Router.map(function() {
this.route('template', data: function() { return Something.find() });
});
You basically pass the context using the data param. It might be easier to do it this way than to use {{#with context}} because you can use more dynamic data which is different for each route.
You might have tried this, I'm a bit unsure on whether it would go to a named yield's template.
Using an ordinary Template helper for the template
Template.templateInYieldName.helper = function() {
return Something.find();
}
Then you can use something like {{helper.name}} in your named yield.
Global data context with handlebars helper
If you intend to use data for all the routes you can use a Handlebars global helper. i.e
Handlebars.registerHelper('todaysDate', function() {
return (new Date).toString();
});
then just use {{todaysDate}} in any of your templates. You can use your data instead of a date instead.
I am trying to figure out how to pass a parameter into a sub-template that is in an each block and use the parameter in the sub-template as well as sub-template helper. Here is what I tried so far:
template:
<template name="parent">
{{#each nodes }}
{{> child myParam}}
{{/each}}
</template>
<template name="child">
{{ paramName }}
</template>
js:
Template.parent.nodes = function() {
//return a list
};
Template.parent.myParam = function() {
return {"paramName" : "paramValue"};
};
Template.child.someOtherHelper = function() {
//How do I get access to the "paramName" parameter?
}
So far, it hasn't been working, and it seems somehow mess up my input node list also.
Thanks for help.
When you use {{> child myParam}}, it's calling the child template and associates myParam as current template data context, meaning that in the template you can reference {{paramName}}.
In someOtherHelper you could use this.paramName to retrieve "paramValue".
However, when you're using {{#each nodes}}{{> child}}{{/each}}, it means that you pass the content of the current list item (fetched from a LocalCursor or directly an array item) as the template data of child, and you can reference the list item properties using {{field}} in html or this.field in js.
What's happening here is when you call {{> child myParam}}, the myParam helper content OVERWRITES the current node item as template data, that's why it's messing your node list.
A quick (dirty) trick would be to simply extend the myParam helper so that it also contains the template data from the {{#each}} block.
Template.parent.helpers({
nodes:function(){
// simulate typical collection cursor fetch result
return [{_id:"A"},{_id:"B"},{_id:"C"}];
},
myParam:function(){
// here, this equals the current node item
// so we _.extend our param with it
return _.extend({paramName:"paramValue"},this);
}
});
Template.child.helpers({
someOtherHelper:function(){
return "_id : "+this._id+" ; paramName : "+this.paramName;
}
});
<template name="parent">
{{#each nodes}}
{{> child myParam}}
{{/each}}
</template>
<template name="child">
{{! this is going to output the same stuff}}
<div>_id : {{_id}} ; paramName : {{paramName}}</div>
<div>{{someOtherHelper}}</div>
</template>
Depending on what you're precisely trying to achieve, there might be a better approach but this one gets the job done at least.