How do you use Ember.js addons? - ember.js

I'm evaluating Ember.js for possible use in an upcoming project. I want to see now it fares with a long list of items, so I tried to installed the ember-list-view.
I ran the command:
ember install:addon ember-list-view
The syntax seems to have changed, so I ran
ember install ember-list-view
That activates npm, which downloaded the package successfully. I can see it in node_modules. Then per the documentation I created the following:
templates/test.hbs:
{{#view 'list-view' items=model height=500 rowHeight=50 width=500}}
{{name}}
{{/view}}
routes/test.js
import Ember from 'ember';
// define index route and return some data from model
export default Ember.Route.extend({
model: function() {
var items = [];
for (var i = 0; i < 10000; i++) {
items.push({name: "Item " + i});
}
return items;
}
});
I added the route in router.js. When I go to the page, nothing shows up. According to Ember Inspector, the right template was being used and the data was there. A check on Ember.ListView in the console yield undefined.
Is there something more that needs to be done to bring in the code? Searches in the Ember and Ember-CLI documentation yielded no answer.

It looks like a problem with the list-view documentation.
There are two issues here:
The {{view}} helper is not a block helper (as far as I know. FYI a block helper is a helper that begins with {{#some-key-word}} and ends with {{/some-key-word}}) - You can't wrap content in it and have that content displayed in the view.
The list-view expects the property content not items
When you change the code to the following:
{{view 'list-view' content=model height=500 rowHeight=50 width=500}}
It works a little bit better (e.g. you can inspect the page and see item views being created) - but still not what you're expecting.
When you change the view keyword to the ember-list keyword (which ember-list-view registers as a helper) - everything works.
{{#ember-list items=model height=500 rowHeight=50 width=500}}
{{name}}
{{/ember-list}}

Related

Ember onLoad Without Views

I'm building a very simple Emberjs site with no logic or models (for the purpose of learning). I have a simple router:
MyApp.Router.map(function() {
this.route("page-one");
this.route("page-two");
this.route("page-three");
this.route("page-four");
});
I have four .hbs files with names that correspond to each route. This works well.
However, I'd like to execute some jQuery onLoad for each of the four pages. I understand I need to use Views and didInsertElement, but my App does not use any views.
Is it possible to use didInsertElement without views? Or is there a master view I can reference?
Based on #Kingpin2k's link I found this solution:
MyApp.IndexView = Ember.View.extend({
setupSomething: function(){
console.log("index elements inserted.");
}.on('didInsertElement')
});
This only results in the console.log entry being added when the index template is rendered.

Ember, my template doesn't update on property change

Why doesn't my template get updated when I change the property it is rendering? The documentation states that {{each}} is bindings-aware, but there's obviously something to it that I am not aware of.
Like everything in Handlebars, the {{#each}} helper is bindings-aware.
If your application adds a new item to the array, or removes an item,
the DOM will be updated without having to write any code.
Here's my controller
App.MaintemplateController = Ember.Controller.extend({
alist: ['foo', "bar"],
actions : {
addel: function(){
this.alist.push('xpto');
console.log(this.alist);
}
}
});
In my template I have the following code.
{{#each alist}}
<li>{{.}}</li>
{{/each}}
<button {{action 'addel'}}>Add element</button>
The data is properly rendered, and when I click the button it does append elements to the property, but the template doesn't refresh. Why? How do I keep it in sync with my data?
Your template is not updating because you are using plain vanilla javascript .push instead of the by ember provided .pushObject and .removeObject respectively. Ember by default extends the array prototype to make it play nicely in a binding aware environment.
So to make your template update you should do:
App.MaintemplateController = Ember.Controller.extend({
alist: ['foo', "bar"],
actions : {
addel: function(){
this.get('alist').pushObject('xpto');
console.log(this.get('alist'));
}
}
});
The important line here is this.get('alist').pushObject('xpto');
Furthermore you should always use .get() and .set() to access objects in ember otherwise the binding mechanism will not be aware of changes made to the object.
Hope it helps.

Ember.js how to design different representations of Data (with TodoMVC as an example)?

I would like to know what's the best way of designing the display of different representations of the same data model in Ember.js. To ask my question, I'll use the TodoMVC of Ember.JS, which has 3 representations of todo-data:
any todo, i.e. the entire todo list (TodosIndexRoute)
todos that are still active and incomplete (TodosActiveRoute)
todos that have been completed (TodosCompletedRoute)
Currently, you can see each of the 3 by clicking on the words at the bottom of the list, directing to a different URL each time. Since currently each representation has a route, it makes sense that each representation gets its unique URL. The main page displays the entire todo list (1.).
A. What is the best ember.js design to make the main page display all 3 representations (i.e. under just one URL)?
B. How about the best design that displays all 3 on the main page as well as on separate pages?
Currently I only figured out a clumsy way and made this modified TodoMVC app that shows incomplete and completed lists at the bottom of the page.
In index.html, I added new named lists:
{{#each todosactive itemController="todo"}}
{{ title }},
{{/each}}
In the js router, I copied TodosActiveRoute and TodosCompletedRoute into TodoIndexRoute, which is code duplication, very bad.
Todos.TodosIndexRoute = Ember.Route.extend({
setupController: function () {
var todos = Todos.Todo.find();
this.controllerFor('todos').set('filteredTodos', todos);
var todos_active = Todos.Todo.filter(function (todo) {
if (!todo.get('isCompleted')) {
return true;
}
});
this.controllerFor('todos').set('todosactive', todos_active);
...
});
I feel like I'm missing an elegant way of doing this, but my current ember.js knowledge is very limited. Should I use {{partial}}, {{render}}, render, or something else?
I tried {{ partial }} and {{ render }}, but I can't get them to display any data .
Thanks for helping out.
A) Ember tries to work really closely with urls. This is a good thing since if you want to share a url, the view should be consistent. The url is a powerful tool and each unique url should link to the same unique page. Having one url that links to multiple views isn't great, and certainly not shareable. If you have some time listen to some talks by Tom Dale and Yehuda Katz for an interesting overview of ember and what they're trying to do.
B) You can include different views on one page. Have a look at the guides, most notably on rendering templates and using helpers for more information on including different views under one url.
A) To display all 3 representations in one view, we actually just need the basic model in the single route. The key is for the controller to give you flavors of that model. The other important thing is to use data binding in the handlebars template. You can see the running version here.
In the IndexRoute, add a model that gets the plain todos list:
Todos.TodosIndexRoute = Ember.Route.extend({
model: function(params) {
return Todos.Todo.find();
},
...
Make a TodosListView that doesn't need to have anything:
Todos.TodoListView = Ember.View.extend();
In the controller, add 2 computed properties that returns the desired arrays:
Todos.TodosController = Ember.ArrayController.extend({
...
todosActive: function() {
return this.filterProperty('isCompleted', false);
}.property('#each.isCompleted'),
todosCompleted: function() {
return this.filterProperty('isCompleted', true);
}.property('#each.isCompleted'),
...
Finally, in the HTML template:
<script type="text/x-handlebars" data-template-name="todos">
Active:
{{#view Todos.TodoListView lalaBinding="todosActive"}}
{{#each view.lala}}
{{title}},
{{/each}}
{{/view}}
Completed:
{{#view Todos.TodoListView dataBinding="todosCompleted"}}
{{#each view.data}}
{{title}},
{{/each}}
{{/view}}
</script>
note the dataBinding.
Thanks to the folks on #emberjs irc for helping out.

How do I setup an Ember View class to be appended to a particular container?

Ember.View has a nice method called .appendTo("#container") which would allow me to specify a container div for the view. However, when I use the router and .connectOutlet method, an instance of my view is created automatically based on convention and is added to the page body element by default. Is there a way to configure the class definition of the view so that upon creation it will be inside my desired #container. Here is my view:
Jimux.BuildsView = Em.View.extend({
templateName: 'builds',
appendTo: '#jimux-header', #this was just a guess and did not work. but does some propery like this exist for the view?
tagName: 'div',
listVisible: true,
...
Another way to ask this question is: how do I tell Ember router to append a view to a particular item in the dom? By default the router appends the view to the body.
And here is the router bit:
# Connect builds controller to builds view
router.get('applicationController').connectOutlet("builds","builds", Jimux.buildsController)
To clarify, I dont want to put my whole Ember app in a container. I have several views in my application, and most of them are fine directly in the body. But there are a couple like the one mentioned in this question, which I want to put inside "#application-header" div.
You can specify the root element for your application object.
window.App = Ember.Application.create({
rootElement: '#ember-app'
});
Edit:
Having re-read your question, I think you should look into named outlets, so you could do something like:
<div id="application-header">
{{outlet builds}}
</div>
{{outlet}}
well..after understanding your question, i remember having same trouble. Also, thing is i didn't find any way to do this even after going through the Ember code. But later i understood that its for good purpose only. I know you already might have come across with handlebars with which we can acheive this. If we give a view a ID to get appended, we are constraining the application and the whole use of ember becomes useless. Ok coming to you question, as far as i know, we can acheive that appending mustache templates in you div element of HTML.
<div id="jimux-header">
{{view Jimux.BuildsView}}
</div>
This way we can use the Jimux.BuildsView where ever you want and as many times possible. The Beauty of Ember you have to say...
Just add rootElement in the application object.
var App = Ember.Application.create({
rootElement: '#container'
});

infinite scroll with ember.js (lazy loading)

I have a view where there can be a large number of items for the user to scroll through and I'd like to implement infinite scrolling to enable progressive loading of the content.
It looks like some folks have done pagination but Google doesn't bring up anyone discussing how they've done infinite lists with Ember/Ember Data. Anyone already worked through this and have a blog post/example code to share?
I've implemented an infinite scroll mechanism at the GitHub Dashboard project, I'm currently developing. The feature is added in commit 68d1728.
The basic idea is to have a LoadMoreView which invokes the loadMore method on the controller every time the view is visible on the current viewport. I'm using the jQuery plugin inview for this. It allows you to register for an inview event, which is fired when the element of the specified selector is visible on screen and when it disappears.
The controller also has properties which indicate whether there are more items to load and if there are currently items fetched. These properties are called canLoadMore and isLoading.
The LoadMoreView basically looks like this:
App.LoadMoreView = Ember.View.extend({
templateName: 'loadMore',
didInsertElement: function() {
var view = this;
this.$().bind('inview', function(event, isInView, visiblePartX, visiblePartY) {
if (isInView) Ember.tryInvoke(view.get('controller'), 'loadMore');
});
}
});
where the loadMore template is defined as follows:
{{#if isLoading}}
fetching some more stuff <img width="10" src="img/ajax-loader.gif" >
{{else}}
{{#if canLoadMore}}
<a {{action "loadMore" target="controller" }}>click to load more items</a>
{{else}}
<i>no more items</i>
{{/if}}
{{/if}}
The controller which handles the fetching of more items is then implemented as follows. Note that in the loadMore method a query on the store is performed, which loads a specific page of of entries for a model.
App.EventsController = Ember.ArrayController.extend({
currentPage: 1,
canLoadMore: function() {
// can we load more entries? In this example only 10 pages are possible to fetch ...
return this.get('currentPage') < 10;
}.property('currentPage'),
loadMore: function() {
if (this.get('canLoadMore')) {
this.set('isLoading', true);
var page = this.incrementProperty('currentPage');
// findQuery triggers somehing like /events?page=6 and this
// will load more models of type App.Event into the store
this.get('store').findQuery(App.Event, { page: page });
} else {
this.set('isLoading', false);
}
}
});
The only thing left is to initially set the content of the controller to the result of a filter function, so the content is updated when new models are loaded into the store (which happens due to the findQuery method in the loadMore of the controller). Also, a query hash is added when the filter is invoked. This ensures that an initial query to the server is made.
App.eventsController = App.EventsController.create({
content: []
});
var events = App.store.filter(App.Event, { page: 1 }, function(data) {
// show all events; return false if a specific model - for example a specific
// type of event - shall not be included
return true;
});
Were you aware of the newly released Ember.ListView component?
https://github.com/emberjs/list-view
It was announced at the February San Francisco Ember Meetup. Here's a slidedeck from Erik Bryn, one of the Ember Core developers about using it:
http://talks.erikbryn.com/ember-list-view/
I'm writing an infinite pagination plugin for Ember based on #pangratz's work.
Please fire any issues on there if you have questions or improvements that you'd like.
I would recommend using Ember Infinity addon. It supports Ember 1.10 through to 2.0+. It's relatively easy to setup. You only need to modify your route and template.
Route (Product is example model):
import InfinityRoute from 'ember-infinity/mixins/route';
export default Ember.Route.extend(InfinityRoute, {
model() {
/* Load pages of the Product Model, starting from page 1, in groups of 12. */
return this.infinityModel('product', { perPage: 12, startingPage: 1 });
}
});
Template:
{{#each model as |product|}}
...
{{/each}}
{{infinity-loader infinityModel=model}}
When {{infinity-loader}} component becomes visible it sends an action to your route, so it knows to update model array with new (fetched) records.
First request will be sent to:
/products?per_page=12&page=1
So you also need to prepare your backend API to handle these query params. It's obviously customizable, take a look at Advanced Usage section of Readme.
Note:
Both using ListView (#commadelimited's answer) and views with ArrayController (#pangratz's answer) is deprecated/removed as of Ember 2.0 being stable version.