How to render a stream view like in twitter of facebook with emberjs arraycontroller? - ember.js

To render a content of an array with emberjs we usually do the following
<ul>
{{#each controller}}
<li>{{name}} by {{artist}}</li>
{{/each}}
</ul>
How to make a live stream view like we have with twitter (of facebook) where a new stream is added on the top of the streams list ?

On the controller you can set the sortProperties see here to specify on which property the array should be sorted and you can set sortAscending (which is a boolean) to specify which direction the array should be sorted.
When you change the array the view will automatically update.
see this fiddle: http://jsfiddle.net/ZnMFK/2/
or this fiddle: http://jsfiddle.net/KfzFE/ to show the DOM gets updated when the array is changed.
HTML:
<script type="text/x-handlebars" data-template-name="index">
<div class="patient-view extended">
{{#each controller}}
<p>Name: {{name}}</p>
{{/each}}
</div>
</script>
App:
window.App = Em.Application.create();
App.Patient = Em.Object.extend({
order: undefined,
name: undefined
});
App.IndexView = Em.View.extend({
click: function() {
this.get('controller')
.set('sortAscending', !this.get('controller').get('sortAscending'));
}
});
App.IndexController = Em.ArrayController.extend({
sortProperties: ['order'],
sortAscending: false
});
App.IndexRoute = Em.Route.extend({
model: function() {
return Em.A([App.Patient.create({
name: "Bert",
order: 2
}), App.Patient.create({
name: "Ernie",
order: 1
})]);
}
});

Related

Using computed property in Ember to get model data and display extra text

In my Ember template, I want to be able to loop over each item coming from the model (an array) and if the value is 'blue', display some text next to the value.
My template looks like this:
<script type="text/x-handlebars" data-template-name="index">
<h2>Loop over colors</h2>
<ul>
{{#each color in model}}
<li>{{color}} {{#if isBlue}} - Its Blue!{{/if}} </li>
{{/each}}
</ul>
</script>
And my app.js file looks like this:
App = Ember.Application.create({});
App.Router.map( function() {
this.resource( 'about');
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return ['red', 'yellow', 'blue'];
}
});
App.IndexController = Ember.ArrayController.extend({
isBlue: function() {
return this.get('content') == 'blue';
}.property()
});
I'm using this.get('content') because I thought that was supposed to be a reference to the actual model data.
I've tried numerous variations of the code but I'm now blocked. Hope someone can help.
You are defining the isBlue property on the IndexController, which is an ArrayController, and not on each item in the content. You can instruct the {{each}} helper to use an itemController for each item in the loop. By doing that you are able to define additional computed properties, that are not present in the original objects, and make them available within the each loop:
<script type="text/x-handlebars" data-template-name="index">
<h2>Loop over colors</h2>
<ul>
{{#each color in model itemController="color"}}
<li>{{color}} {{#if isBlue}} - Its Blue!{{/if}}</li>
{{/each}}
</ul>
</script>
App.ColorController = Ember.ObjectController.extend({
isBlue: function() {
return this.get('content') === 'blue';
}.property('content')
});
You can also check out JSBIN.
ArrayController means that the content property is an array, not just an object. Also, you don't want to access content directly. Controllers proxy their models, so use the controller as if it was an array. So your isBlue function is wrong in a few ways. It's probably possible to do what you want using the isBlue property, but I would use something like this:
colorItems: Em.computed.map('#this', function(color) {
return {
color: color,
isBlue: color === 'blue'
};
})
Then, in your template:
{{#each colorItems}}
<li>
{{color}}
{{#if isBlue}}
- It's Blue!
{{/if}}
</li>
{{/each}}

Ember.js: Loop over data from ArrayController in unassociated View

I have a route set up which pulls Account information from a REST endpoint:
Social.Router.map(function() {
this.resource('accounts');
});
Social.AccountsRoute = Ember.Route.extend({
model: function() {
return Social.Account.find();
}
});
Social.Account = DS.Model.extend({
name: DS.attr('string'),
username: DS.attr('string')
});
Social.AccountsController = Ember.ArrayController.extend();
I can loop over that data in my template like so:
<script type="text/x-handlebars" data-template-name="accountItem">
{{#each account in controller}}
<div class="avatar-name">
<p>{{account.name}}</p>
<p>{{account.username}}</p>
</div>
{{/each}}
</script>
I have another template in which I'd like to use the same account data. How would I retrieve the information associated with the Account model from within a View so that I can make it available in the corresponding template?
Social.NewPostView = Ember.View.extend({
tagName: 'div',
accounts: function(){
// return Account data here?
}
});
Update 1
Here's a quick view of my UI
On the left is "live" account data. On the right is static HTML. I'd like to reuse the data from the left on the right. Make sense?
You should take a look to the needs property for controllers. This allow you to specify a list of controllers you will be able to access from the current one.
More information on the emberjs guides
NewPostController
Social.NewPostController = Ember.ObjectController.extend({
needs: ['accounts'],
init: function(){
this._super();
console.log(this.get('controllers.accounts'))
}
});
Then, from your template:
<script type="text/x-handlebars" data-template-name="newPost">
{{#each account in controller.controllers.accounts}}
<div class="avatar-name">
<p>{{account.name}}</p>
<p>{{account.username}}</p>
</div>
{{/each}}
</script>

Ember.js: replacing simple linkTo helper with a view

I've got an app with basic functionality built out. I'm not going through and adding additional features. In this case I need to convert a simple button, currently using linkTo, to a View. Problem is that I'm not sure how to convert one to the other and still keep the link intact.
How do I do this conversion? Here's the code I have now:
<script type="text/x-handlebars" data-template-name="accountItem">
{{#each account in controller}}
{{#linkTo "account" account}}
<img {{bindAttr src="account.icon"}} />
{{/linkTo}}
{{/each}}
</script>
and here's the code I'm going to have:
<script type="text/x-handlebars" data-template-name="accountItem">
{{#each account in controller}}
{{#view "Social.AccountButtonView"}}
<img {{bindAttr src="account.icon"}} />
{{/view}}
{{/each}}
</script>
Social.AccountButtonView = Ember.View.extend({
tagName: 'a',
classNames: ['item-account'],
click: function(){
// do something
}
});
I would assume that I'd be building on top of the click handler in the View, but I'm not sure how to pass the reference to item being iterated over, nor how to reference the correct route within the View.
Assistance please?
Update 1
The first version renders an href attribute with a value of #/accounts/4 based on the Router I have set up:
Social.Router.map(function() {
this.resource('accounts', function(){
this.resource('account', { path: ':account_id'});
});
});
When I convert the current code to a view, how do I mimic the functionality that linkTo provides?
You can define a property binding for account in your handlebars template.
This binding works like this:
<script type="text/x-handlebars">
<h1>App</h1>
{{#each item in controller}}
{{#view App.AccountView accountBinding="item"}}
<a {{bindAttr href="view.account.url"}} target="_blank">
{{view.account.name}}
</a>
{{/view}}
{{/each}}
</script>
Note that I added accountBinding, so the general rule is propertyName and Binding as a suffix. And remember that when you add a property to a view, you will not be able to access it directly, instead you will have to access it with view.propertyName as shown above.
Just keep in mind that you must have a View class when using the {{view}} helper:
window.App = Em.Application.create();
App.AccountView = Em.View.extend(); // this must exist
App.ApplicationRoute = Em.Route.extend({
model: function() {
return [
{id: 1, name: 'Ember.js', url: 'http://emberjs.com'},
{id: 2, name: 'Toronto Ember.js', url: 'http://torontoemberjs.com'},
{id: 3, name: 'JS Fiddle', url: 'http://jsfiddle.com'}];
}
})
Working fiddle: http://jsfiddle.net/schawaska/PFxHx/
In Response to Update 1:
I found myself in a similar scenario, and ended up creating a child view to mimic the {{linkTo}} helper. I don't really know/think it's the best implementation tho.
You can see my previous code here: http://jsfiddle.net/schawaska/SqhJB/
At that time I had created a child view within the ApplicationView:
App.ApplicationView = Em.View.extend({
templateName: 'application',
NavbarView: Em.View.extend({
init: function() {
this._super();
this.set('controller', this.get('parentView.controller').controllerFor('navbar'))
},
selectedRouteName: 'home',
gotoRoute: function(e) {
this.set('selectedRouteName', e.routeName);
this.get('controller.target.router').transitionTo(e.routePath);
},
templateName: 'navbar',
MenuItemView: Em.View.extend({
templateName:'menu-item',
tagName: 'li',
classNameBindings: 'IsActive:active'.w(),
IsActive: function() {
return this.get('item.routeName') === this.get('parentView.selectedRouteName');
}.property('item', 'parentView.selectedRouteName')
})
})
});
and my Handlebars looks like this:
<script type="text/x-handlebars" data-template-name="menu-item">
<a {{action gotoRoute item on="click" target="view.parentView"}}>
{{item.displayText}}
</a>
</script>
<script type="text/x-handlebars" data-template-name="navbar">
<ul class="left">
{{#each item in controller}}
{{view view.MenuItemView itemBinding="item"}}
{{/each}}
</ul>
</script>
I'm sorry I can't give you a better answer. This is what I could come up with at the time and haven't touched it ever since. Like I said, I don't think this is the way to handle it. If you are willing to take a look into the {{linkTo}} helper source code, you'll see a modular and elegant implementation that could be the base of your own implementation. I guess the part you're looking for is the href property which is being defined like so:
var LinkView = Em.View.extend({
...
attributeBindings: ['href', 'title'],
...
href: Ember.computed(function() {
var router = this.get('router');
return router.generate.apply(router, args(this, router));
})
...
});
So I guess, from there you can understand how it works and implement something on your own. Let me know if that helps.

How to pass Model route name item to linkTo in template in ember.js

How do I pass a route name to a {{linkTo}} dynamically?
For example, given this code:
App.Router.map(function() {
this.resource('anon', {path: '/main'},
function() {
this.route('home', {path:'/home'});
this.route('about', { path: '/about' });
this.route('contact', { path: '/contact' });
});
});
App.NavController = Ember.ArrayController.extend({
selectedNav:'',
setNav:function(value){
var nav = App.Nav.find(value);
var items = nav.get('navItems');
this.set('content', items);
}
});
these templates:
<script type="text/x-handlebars" data-template-name="nav">
<ul class="nav">
{{#each in controller}}
{{ partial "basicNav"}}
{{/each}}
</ul>
</script>
<script type="text/x-handlebars" data-template-name="_basicNav">
<li>{{#linkTo navItemPath}}{{navItemName}}{{/linkTo}}</li>
</script>
and these models with the following fixture data:
App.Nav = DS.Model.extend({
navItems:DS.hasMany('App.NavItem'),
name:DS.attr('string')
});
App.NavItem = DS.Model.extend({
nav:DS.belongsTo('App.Nav'),
navItemName:DS.attr('string'),
navItemPath:DS.attr('string')
});
App.Nav.FIXTURES = [
{
id: 10,
name: 'Anon',
navItems: [100,200,300]
}
];
App.NavItem.FIXTURES = [
{
id:100,
nav:10,
navItemName:'Home',
navItemPath:'anon.home'
},
{
id:200,
nav:10,
navItemName:'Contact',
navItemPath:'anon.contact'
},
{
id:300,
nav:10,
navItemName:'About',
navItemPath:'anon.about'
}
];
How do I pass navItemPath to the {{linkTo}} helper? In this code snippet:
{{#linkTo navItemPath}}{{navItemName}}{{/linkTo}}
ember complains that it can't find the "navItemPath" route, like it's looking for it literally. If I replace that with a valid literal route like:
{{#linkTo 'anon.home'}}{{navItemName}}{{/linkTo}}
ember will render the linkTo with the navItemName as expected, so I know the controller is passing it the right data, but of course all the routes are goofy. Am I missing something obvious?
You can't do that with LinkTo helper, you need to bind the href of your link to navItemPath using bindAttr
<a {{bindAttr href="navItemPath"}}>{{navItemName}}</a>
Make sure the the logic rending navItemPath's value takes into account the location API

{{#each loop}} not working. What would be the right way to get it going

I am following an example at "emberjs.com" which isn't going too well. I have a "GuestController" and "GuestView" within my application. I would like to use the "{{#view}} & {{#each}} to output an object called "guests" from the "GuestView". I am following this online example:
http://emberjs.com/documentation/#toc_displaying-a-list-of-items
fiddle: http://jsfiddle.net/exciter/MjA5A/8/
Here is the code:
APP CODE:
$(function(){
App = Ember.Application.create({
ready: function(){
//alert("APP INIT");
}
});
App.ApplicationController = Ember.Controller.extend();
App.ApplicationView = Ember.View.extend({
templateName: "application",
classNames: ['']
});
App.GuestController = Ember.Controller.extend();
App.GuestView = Ember.View.extend({
guests: [{name:"The Doctor" },
{name:"The Scientist" },
{name:"The Maestro"}]
});
App.initialize();
});
HTML:
<script type="text/x-handlebars" data-template-name="application">
{{#each App.GuestController}}
{{#view App.GuestView}}
{{guests}}
{{/view}}
{{/each}}
</script>
First of all, we use {{each}} block helper to iterate over an array of items, now when you say {{#each GuestController}} the controller should be of type Ember.ArrayController, and the {{#each GuestController}} looks for the content property inside the GuestController which will be used to iterate over, As per the example I think this is what you are trying to implement...Instead if you want to iterate over an Array inside a view check this