I have this jsbin, is a relly littl modification of the todos example
http://jsbin.com/ubimes/1/
I'm trying to find the way to limit a model to the last 5 entries
return Todos.Todo.find({limit:5});
But that query returns nothing
Any idea how to limit collections ?
You can use slice instead of splice
this.get('content').slice(0, 2);
A way you could do it could be limiting your collection's content in your respective controller holding the data, like this:
App.IndexController = Ember.ArrayController.extend({
limitedContent: function() {
// in this case '2' is the limit parameter
return this.get('content').splice(0, 2);
}.property('content')
});
The .property('content') defines a binding to the controller's content and takes care that the #each helper rerenders when the content changes.
And then in your template you loop over the limitedContent rather then the content:
<script type="text/x-handlebars" data-template-name="index">
{{#each color in limitedContent}}
{{color.name}}
{{/each}}
</script>
And here a working jsbin that show the concept mentioned.
Hope it helps
Related
I have a Model called Course that has an array variable called users. Is there a way to just limit the results of, let say, IDs from users rather than the {{#each}} rendering every single id?
Such that
<script type="text/x-handlebars" data-template-name="course">
{{#each user in users 5}}
{{user.name}}
{{/each}}
</script>
Will show the first 5 IDs rather than every single id stored in users?
Here is the Ember-data model that I am using
App.Course = DS.Model.extend({
//Attributes
name: DS.attr('string'),
description: DS.attr('string'),
//Relations
users: DS.hasMany('App.User'),
});
I tried multiple times at creating a registerHelper, but where it always seems to go wrong is when an 'a in b' is present.
Any help or ideas would be appreciated. Thank you very much!
Rather than modifying the #each helper you could take a different approach to show only a limited amount of items when looping over the array with the #each helper.
See this for a possible implementation:
App.IndexRoute = Ember.Route.extend({
model: function() {
return [
Ember.Object.create({id:1, name:'Red'}),
Ember.Object.create({id:2, name:'Green'}),
Ember.Object.create({id:3, name:'Blue'})
];
}
});
App.IndexController = Ember.ArrayController.extend({
limitedContent: function() {
return this.get('content').splice(0, 2);
}.property('content')
});
The .property('content') defines a binding to the controller's content and takes care that the #each helper rerenders when the content changes.
And then in your template you loop over the limitedContent rather then the content:
<script type="text/x-handlebars" data-template-name="course">
{{#each color in limitedContent}}
{{color.name}}
{{/each}}
</script>
And here a working jsbin that show the concept mentioned.
Hope it helps
I was using the method in the accepted method, but ran into an issue with it.
The problem lay in that using splice in ember modifies the underlying data:
filtered: function() {
return this.get('content').splice(0,2);
}.property('content'),
splice removes the elements from the underlying content.
A better method is to use Ember's built-in slice function, changing the above code into
filtered: function() {
return this.get('content').slice(0,2);
}.property('content'),
And that's it, now it doesn't modify the underlying array, because slice returns a new array and leaves the underlying data untouched.
JSBin showing that splice modifies underlying data:
http://emberjs.jsbin.com/ridixawimugo/1/
JSBin with the fixed solution:
http://emberjs.jsbin.com/voceye/1/
I got two question about the Meteor framework
First of all, how can I put an array inside a Meteor collection? And how can I push values into it?
Second of all, when I have a button and I click on it, how can I change the current view? Is this by hiding and showing templates?
Thanks!
Use $addToSet to push values into an array:
var coll = new Meteor.Collection;
coll.insert({myArray: []});
coll.update({}, {$addToSet: {myArray: "myNewValue"}});
There are many ways to change views, but an easy one is to use Session and check whether it has a value in your template:
<template name="mytemplate">
<button>Say hello</button>
{{#if sayHello}}<p>Hello</p>{{/if}}
</template>
Template.mytemplate.events({
"click button": function() {
Session.set("sayHello", true);
}
});
Template.mytemplate.sayHello = function() {
return Session.equals("sayHello", true);
}
The documentation has an example of using an ArrayController with this template:
{{#each MyApp.listController}}
{{firstName}} {{lastName}}
{{/each}}
This is how the ArrayController is used:
MyApp.listController = Ember.ArrayController.create();
$.get('people.json', function(data) {
MyApp.listController.set('content', data);
});
How would this work differently than using a plain array like this instead?
MyApp.listController = [];
$.get('people.json', function(data) {
MyApp.set('listController', data);
});
If you don't need the behavior of a controller, you can use a plain array.
An ArrayController wraps an array, with some other properties added, such as the sortable mixin.
You can see it here:
https://github.com/emberjs/ember.js/blob/master/packages/ember-runtime/lib/controllers/array_controller.js
in the ember.js documentation says:
(http://docs.emberjs.com/symbols/Ember.ArrayController.html)
The advantage of using an ArrayController is that you only have to set
up your view bindings once; to change what's displayed, simply swap
out the content property on the controller.
it uses an Array in background, only helps with methods to work with the array:
Although you are binding to the controller, the behavior of this
controller is to pass through any methods or properties to the
underlying array
I would like to store different objects in the same controller content array and render each one using an appropriate view template, but ideally the same view.
I am outputting list objects using the code below. They are currently identical, but I would like to be able to use different ones.
<script type="text/x-handlebars">
{{#each App.simpleRowController}}
{{view App.SimpleRowView class="simple-row" contentBinding="this"}}
{{/each}}
</script>
A cut-down version of the view is below. The other functions I haven't included could be used an any of the objects, regardless of model. So I would ideally have one view (although I have read some articles about mixins that could help if not).
<script>
App.SimpleRowView = Em.View.extend({
templateName: 'simple-row-preview',
});
</script>
My first few tests into allowing different object types ended up with loads of conditions within 'simple-row-preview' - it looked terrible!
Is there any way of dynamically controlling the templateName or view used while iterating over my content array?
UPDATE
Thanks very much to the two respondents. The final code in use on the view is below. Some of my models are similar, and I liked the idea of being able to switch between template (or a sort of 'state') in my application.
<script>
App.SimpleRowView = Em.View.extend({
templateName: function() {
return Em.getPath(this, 'content.template');
}.property('content.template').cacheable(),
_templateChanged: function() {
this.rerender();
}.observes('templateName'),
// etc.
});
</script>
You can make templateName a property and then work out what template to use based on the content.
For example, this uses instanceof to set a template based on the type of object:
App.ItemView = Ember.View.extend({
templateName: function() {
if (this.get("content") instanceof App.Foo) {
return "foo-item";
} else {
return "bar-item";
}
}.property().cacheable()
});
Here's a fiddle with a working example of the above: http://jsfiddle.net/rlivsey/QWR6V/
Based on the solution of #rlivsey, I've added the functionality to change the template when a property changes, see http://jsfiddle.net/pangratz666/ux7Qa/
App.ItemView = Ember.View.extend({
templateName: function() {
var name = Ember.getPath(this, 'content.name');
return (name.indexOf('foo') !== -1) ? 'foo-item' : 'bar-item';
}.property('content.name').cacheable(),
_templateChanged: function() {
this.rerender();
}.observes('templateName')
});
I understand how to get a collection together, or an individual model. And I can usually get a model's data to display. But I'm not clear at all how to take a collection and get the list of models within that collection to display.
Am I supposed to iterate over the collection and render each model individually?
Is that supposed to be part of the collection's render function?
Or does the collection just have it's own view and somehow I populate the entire collection data into a view?
Just speaking generally, what is the normal method to display a list of models?
From my experience, it's the best to keep in your collection view references to each model's view.
This snippet from the project I'm currently working on should help you get the idea better:
var MyElementsViewClass = Backbone.View.extend({
tagName: 'table',
events: {
// only whole collection events (like table sorting)
// each child view has it's own events
},
initialize: function() {
this._MyElementViews = {}; // view chache for further reuse
_(this).bindAll('add');
this.collection.bind('add', this.add);
},
render: function() {
// some collection rendering related stuff
// like appending <table> or <ul> elements
return this;
},
add: function(m) {
var MyElementView = new MyElementViewClass({
model: m
});
// cache the view
this._MyElementViews[m.get('id')] = MyElementView;
// single model rendering
// like appending <tr> or <li> elements
MyElementView.render();
}
});
Taking this approach allows you to update views more efficiently (re-rendering one row in the table instead of the whole table).
I think there are two ways to do it.
Use a view per item, and manipulate the DOM yourself. This is what the Todos example does. It's a nice way to do things because the event handling for a single model item is clearer. You also can do one template per item. On the downside, you don't have a single template for the collection view as a whole.
Use a view for the whole collection. The main advantage here is that you can do more manipulation in your templates. The downside is that you don't have a template per item, so if you've got a heterogeneous collection, you need to switch in the collection view template code -- bletcherous.
For the second strategy, I've done code that works something like this:
var Goose = Backbone.Model.extend({ });
var Gaggle = Backbone.Collection.extend({
model: Goose;
};
var GaggleView = Backbone.View.extend({
el: $('#gaggle'),
template: _.template($('#gaggle-template').html()),
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
}
};
var g = new Gaggle({id: 69);
g.fetch({success: function(g, response) {
gv = new GaggleView({model: g});
gv.render();
}});
The template code would look something like this:
<script type="text/template" id="gaggle-template">
<ul id="gaggle-list">
<% _.each(gaggle, function(goose) { %>
<li><%- goose.name %></li>
<% }); %>
</ul>
</script>
Note that I use the _ functions (useful!) in the template. I also use the "obj" element, which is captured in the template function. It's probably cheating a bit; passing in {gaggle: [...]} might be nicer, and less dependent on the implementation.
I think when it comes down to it the answer is "There are two ways to do it, and neither one is that great."
The idea of backbone is that view rendering is event driven.
Views attach to Model data change events so that when any data in the model changes the view updates itself for you.
What you're meant to do with collections is manipulate a collection of models at the same time.
I would recommend reading the annotated example.
I've also found this a confusing part of the Backbone framework.
The example Todos code is an example here. It uses 4 classes:
Todo (extends Backbone.Model). This represents a single item to be todone.
TodoList (extends Backbone.Collection). Its "model" property is the Todo class.
TodoView (extends Backbone.View). Its tagName is "li". It uses a template to render a single Todo.
AppView (extends Backbone.View). Its element is the "#todoapp" . Instead of having a "model" property, it uses a global TodoList named "Todos" (it's not clear why...). It binds to the important change events on Todos, and when there's a change, it either adds a single TodoView, or loops through all the Todo instances, adding one TodoView at a time. It doesn't have a single template for rendering; it lets each TodoView render itself, and it has a separate template for rendering the stats area.
It's not really the world's best example for first review. In particular, it doesn't use the Router class to route URLs, nor does it map the model classes to REST resources.
But it seems like the "best practice" might be to keep a view for each member of the collection, and manipulate the DOM elements created by those views directly.