How to set specific view in emberJS with itemcontroller - ember.js

I have the following code:
{{#each categories.items itemController="item"}}
When I open the Ember inspector, it shows the view to be "virtual". I want to set the view to be "item" so that it follows the ember view I set out called itemView. I know we can set an itemController: is it possible to set an item view?

Yes it is possible using the optional 'itemViewClass' parameter.
{{#each categories.items itemController="item" itemViewClass="otherView"}}
Though I would recommend discontinuing using that and itemController as the latest Best Practice is to use a component within the each block.
So for your example:
// Ember 1.10+
{{#each categories.items as |item|}}
{{some-component item=item}}
{{/each}}
// Ember 1.9-
{{#each item in categories.items}}
{{some-component item=item}}
{{/each}}
Then you put your logic needed in the component object instead of the item controller.

Related

How do I render a collection of models each with variable view and controller?

I have a collection of models in my Ember.js app, which I would like to render. The catch is that I want to be able to specify a specialized view and controller for each of the models.
The controller part seems to be easy: I would just wrap the array in an ArrayController and implement itemController method. The view part is where it gets tricky. I don't see an obvious idiomatic way of doing this.
The best way we came up with is the combination of ArrayController and CollectionView with an overridden createChildView. For instance:
createChildView: function(viewClass, attrs) {
var viewInstance,
widgetType = attrs.content.get('type');
// lookup view, if found, use it, if not, pass empty view
var viewDefined = this.container.lookup('view:' + widgetType);
var createWidgetType = viewDefined ? widgetType : 'empty';
// create view instance from widgetType name
// it causes lookup in controller
viewInstance = this._super(createWidgetType, attrs);
// if `attrs.content` is controller (item of `ComponentsController`)
// set it as controller of newly created view
if(attrs.content.get('isController')) {
viewInstance.set('controller', attrs.content);
}
return viewInstance;
}
This feels unnecessarily convoluted, I don't like that I have to connect the view with the controller manually like that. Is there a cleaner way?
You can create a component, which will act as controller and have a view associated with it:
App.XItemComponent = Ember.Component.extend({
controllerProperty: '!',
tagName: 'li'
});
Then, you can just do:
<script type="text/x-handlebars" data-template-name="index">
<ul>
{{#each model }}
{{ x-item item=this }}
{{/each}}
</ul>
</script>
http://emberjs.jsbin.com/wehevixolu/1/edit?html,js,output
I'd use the {{render}} helper. It'll create a view and controller for each instance.
{{#each item in model}}
{{render "item" item}}
{{/each}}
Example: http://emberjs.jsbin.com/vuwimu/2/edit?html,js,output
Render helper guide: http://emberjs.com/guides/templates/rendering-with-helpers/#toc_the-code-render-code-helper
Additionally:
In your comment you mentioned you want different controller/view types for particular model types. This could be done like this:
{{#each item in model}}
{{#if item.typeX}}
{{render "itemX" item}}
{{/if}}
{{#if item.typeY}}
{{render "itemY" item}}
{{/if}}
{{/each}}
or if you'd choose to go with components:
{{#each item in model}}
{{#if item.typeX}}
{{component-x item=item}}
{{/if}}
{{#if item.typeY}}
{{component-y item=item}}
{{/if}}
{{/each}}
Without knowing what you are trying to accomplish in more detail it’s hard to tell what the best solution is.

how to set an itemView for an itemController in Ember?

I have successfully implemented an ArrayController an defined an ItemController for it like this:
export default Ember.ArrayController.extend(InboxTab, {
itemController: 'messages.message-list-item'
});
then in the template for the array controller I just do
{{#each}}
<li {{action 'someActionFromItemController'}}>{{someComputedPropertyFromItemController}}</li>
{{/each}}
This works great and I can handle a lot of actions and computed for each item, but I'm running into difficulties associating a view to each item. The docs are not helpful. The only instance of itemView is in this article:
http://emberjs.com/api/classes/Ember.CollectionView.html#sts=Specifying itemViewClass
and here the example seems to revolve around adding the view to the template and specifying the content from there and I'm not sure how that applies to the way I'm doing it.
A different way you could do it:
{{#each itemController='messages.message-list-item'}}
{{#view your-view}}
{{action}}{{computed property from view/controller}}
{{/view}}
{{/each}}
specify the item controller in the loop and wrap the actions and properties in the view - which means either of these could also be called or set in the view.
I think way to do this best is with itemViewClass, see the api for the full details.
{{#each message itemController='messageListItem' itemViewClass='messageListItem'}}
{{! Assumes you have view defined in an App.MessageListItemView defined in your JavaScript}}
{{action}}
{{/each}}

dynamic outlet name in ember js

Requirement: There will be few buttons, and on clicking every button the immediate outlet will be rendered with a view. (not the other outlets present in the page)
Suppose I'm creating outlets in #each.
{{#each item in model}}
<#link-to 'passenger' item.id>Open Corresponding Outlet </link-to>
{{outlet item.id}}
{{/each}}
and from back i'm rendering the outlet:
model: function (params) {
return [
{ id: params.passenger_id}
]
},
renderTemplate: function () {
this.render({ outlet: this.get('controller.model')[0].id });
},
This just doesn't work.
Can anyone help me to do that?
You can't create dynamic named outlets, and it would break the url representing the page you are viewing concept which the core members preach heavily.
You should just render in the template using the render helper, and use
{{#each item in model}}
{{render 'something' item}}
{{/each}}
And inside the something controller/template you can add additional logic for how you'd like it to interact.
Additionally you could just add another resource under your passenger route and ad an outlet which hosts that information when it's clicked.
Not sure if you're still looking for a solution, as #kinpin2k mentioned you can't use dynamic outlets, but perhaps a dynamic partial would help.
Like so:
{{partial someTemplateName}}
Where someTemplateName can be any computed property in your controller. If the property returns falsy then nothing will be displayed.
Here's the reference: http://emberjs.com/api/classes/Ember.Handlebars.helpers.html#toc_bound-template-names

itemController in ArrayController vs #each

Following along with the Getting Started Guide I have this http://jsbin.com/enutit/2/edit
My question is how come I can't remove the itemController from this each helper
<ul id="todo-list">
{{#each controller itemController="todo"}}
<li {{bindAttr class="isCompleted:completed isEditing:editing"}}>
and then add
itemController: 'todo',
to Todos.TodosController and have it work?
Because the controller's properties are not the same as the {{each}} helper's properties.
{{each}} internally creates an instance of Ember.Handlebars.EachView to display each item in the Todos.TodosController's content property. It is this view that needs the itemController property so that it can create a new Todos.TodoController (note the singular form) instance for each child view.

Pass context to a controller from the view in Ember

Template controlled by someParentController
{{#each post in content}}
{{view App.PostView postBinding="post"}}
{{/each}}
Setting an instance of the controller on the view
App.PostView = Ember.View.extend
post: null # set when the view is created
controller: App.PostController.create()
templateName: 'post.handlebars'
Now my view instance has the context instead of my controller instance. Is there a cleverer way to handle this? I would use an {{outlet}} if I was routing to a particular post, but the main template is displaying all posts. I want each to post to have its own controller though. It doesn't seem right to create an outlet for every post since you cannot namespace a dynamic number of outlets.
You can bypass the view entirely by using the following syntax on your action helpers in post.handlebars.
{{action someMethodOnController context="post" target="controller"}}