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

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

Related

Why is itemController not set in the child view?

I want App.IndexRowController to be the controller for the three row views. Instead Ember sets them to plain Objects. I believe I'm properly setting itemController in the DataIndexController. I a version of this code without the nested route working as expected. Do I need to do something special when working with nested routes/needs?
JSBin: http://jsbin.com/sazafi/edit?html,css,js,output
To see the behavior go to #/data/index. Notice there are three li elements but no corresponding text (from getName). The getName controller property isn't accessible from the row template. Ember docs say that setting the itemController in the ArrayController should make that controller available to the template specified in itemViewClass. Take a look at the Ember Inspector and see that the controller for the three views is an Ember.Object, not App.IndexRowController.
JavaScript:
App = Ember.Application.create();
App.Router.map(function() {
this.resource("data", function() {
this.route("index")
});
});
App.DataIndexRoute = Ember.Route.extend({
model: function() {
return(
[
Ember.Object.create({name: 'row 1'}),
Ember.Object.create({name: 'row 2'}),
Ember.Object.create({name: 'row 3'})
]);
}
});
App.DataController = Ember.ArrayController.extend({
filter: ''
});
App.DataIndexController = Ember.ArrayController.extend({
needs: ['data'],
itemController: 'indexRow',
filter: Ember.computed.alias("controllers.data.filter"),
filteredContent: function(){
var filter = this.get('filter');
var list = this.get('arrangedContent');
return list.filter(function(item) {
return item.get('name').match(filter);
});
}.property('content', 'filter')
});
App.IndexRowController = Ember.ObjectController.extend({
// This method isn't accessible from the row template
getName: function() {
return(this.get('content').get('name'));
}.property()
});
App.DataIndexView = Ember.CollectionView.extend({
tagName: 'ul',
content: function(){
return this.get('controller.filteredContent')
}.property('controller.filteredContent'),
itemViewClass: Ember.View.extend({
controllerBinding: 'content',
templateName: 'row'
})
});
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Collection View</title>
<script src="http://code.jquery.com/jquery-2.0.2.js"></script>
<script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.1.2.js"> </script>
<script src="http://builds.emberjs.com/release/ember.js"></script>
<script src="app.js"></script>
</head>
<body>
<script type="text/x-handlebars" data-template-name="application">
<h1>CollectionView With Item View</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="data">
{{input type="text" placeholder='row 1' value=filter}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="row">
{{getName}}
</script>
</body>
</html>
EDIT: I have a working example of how to set the controller in a child view of a Ember.ContainerView and how to filter the contents here: https://github.com/mkolenda/ember-listview-with-filtering. ListView is a descendent of ContainerView.
Simple solution is to use an {{each}} instead of a CollectionView.
This is a well-known "feature", aka design bug, in the Ember design for array controllers, item controllers, and collection views. It shouldn't be too hard to find references on the web about the problem and some suggested hacks/workarounds. You might start with https://github.com/emberjs/ember.js/issues/4137 or https://github.com/emberjs/ember.js/issues/5267.

itemControllers and custom Views

I am working on a small app that animates different iframes in and out of view. Right now I am just trying to start simple with two iframes for my data.
App = Ember.Application.create();
App.IndexRoute = Ember.Route.extend({
model: function() {
return [
{current: true, url:'http://www.flickr.com'},
{url:'http://bing.com'}
];
}
});
App.IndexController = Ember.ArrayController.extend({
itemController: 'iframe',
now: function() {
return this.filterBy('isCurrent').get('firstObject');
}.property('#each.isCurrent')
});
App.IframeController = Ember.ObjectController.extend({
isCurrent: Ember.computed.alias('current')
});
App.IframeView = Ember.View.extend({
classNameBindings: [':slide', 'isCurrent'],
templateName: 'iframe'
});
And my templates:
<script type="text/x-handlebars" data-template-name="index">
<button {{action "next"}}>Next</button>
{{#each}}
{{view "iframe"}}
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="iframe">
<iframe {{bind-attr src=url}}></iframe>
</script>
Why can't my IframeView access my isCurrent property of my itemController? I am also unsure if this is the right way to do this, or if there is an easier way to have my each use my IframeView
Here is a jsbin: http://emberjs.jsbin.com/vagewavu/4/edit
isCurrent lives on the controller. The controller property will be in scope in the view, but the properties under the controller aren't in scope of the view. You just need to reference controller first.
App.IframeView = Ember.View.extend({
classNameBindings: [':slide', 'controller.isCurrent'],
templateName: 'iframe'
});
Additionally your next action isn't doing anything, just creating some local variables, maybe you weren't finished implementing it. Either way I tossed together an implementation.
next: function() {
var now = this.get('now'),
nowIdx = this.indexOf(now),
nextIdx = (nowIdx + 1) % this.get('length'),
next = this.objectAt(nextIdx);
now.toggleProperty('current');
next.toggleProperty('current');
}
http://emberjs.jsbin.com/vagewavu/10/edit

Emberjs 1.0.0-RC3: Can't use send to call controller method from view

Normally, we can access a method declared in the controller from the views using: this.get('controller').send('method');. In this simple jsfiddle, that is failing with: Uncaught TypeError: Cannot read property 'send' of undefined.
Here is the entire code in the jsfiddle.
App = Ember.Application.create();
App.YesController = Ember.ArrayController.extend({
cow: function(){
console.log('say mooooo') ;
}
});
App.YesView = Ember.View.extend({
templateName: 'yes',
um: function(){
this.get('controller').send('cow');
}
});
The entire view:
<script type='text/x-handlebars' data-template-name='application'>
{{render yes}}
{{outlet}}
</script>
<script type='text/x-handlebars' data-template-name='yes'>
<h1> Can't use send to call controller method from view</h1>
<button {{action 'um' target='view.content'}}> can't call controller from view</button>
<button {{action 'cow'}}> controller action works</button>
</script>
As far as I know it's possible to call the cow() method in your controller like this:
App.YesView = Ember.View.extend({
templateName: 'yes',
um: function() {
this.get('controller').cow();
//this.controller.cow() // <-- this should work also
}
})
Try target=view rather that target=view.content, otherwise you're trying to call send on the model, which is undefined.

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 render a stream view like in twitter of facebook with emberjs arraycontroller?

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
})]);
}
});