Emberjs - handlebars-actions inside CollectionView - ember.js

I got a strange Problem here. I want to catch an Action inside a rendered Ember.CollectionView, in the "ParentView".
{{#collection contentBinding="view.content"}}
{{view.content}}
{{/collection}}
but the parentView inside a CollectionView is still a Subclass of a View, and not the ParentView itself (in my Example the ApplicationView). So, how can i catch the Handlebars-Action inside my ApplicationView?
Here is a fiddle to see the Problem by yourself: http://jsfiddle.net/smgMt/3/
Any Ideas?

When using the collection helper, there is an implicit view created for each content in the block. So here, if you want to access the ApplicationView, you have to call twice parentView.
The second hint is you must prefix this chain by the keyword view to access the current view properties.
<script type="text/x-handlebars" data-template-name="application">
<h1>Hello from Ember.js</h1>
<button {{action test target="view"}}>Click to Test</button>
{{#collection contentBinding="view.content" tagName="ul"}}
<a href="#" {{action insideAction target="parentView.parentView"}}>{{view.content}}</a>
{{/collection}}
</script>​
http://jsfiddle.net/smgMt/14/

Related

Porting a view hierarchy from ember-0.9.8.1 to ember-1.0.0-rc.6

I can not get my head around this. I have started from this jsfiddle (a showcase for image preview + upload), which is working with ember-0.9.8.1, and I am trying to get it working with ember-1.0.0-rc.6 in this jsbin.
This is the relevant part causing problems:
<script type="text/x-handlebars">
{{#view Ember.View contentBinding="App.myModel"}}
{{#view App.PreviewUploadImage name="logo_image" contentBinding="content"}}
{{view fileField}}
{{view previewImageView width="200" height="100" srcBinding="content.myModel_src"}}
{{/view}}
{{/view}}
</script>
Together with this js:
App.PreviewUploadImage = Ember.View.extend({
fileField: Ember.TextField.extend({...}),
});
As you can see in the console errors:
Assertion failed: Unable to find view at path 'fileField'
Assertion failed: You must pass a view to the #view helper, not fileField ()
Uncaught TypeError: Cannot read property 'proto' of undefined
But fileField is a Ember.TextField (so indeed a view) and is defined in the context where it is used (the view PreviewUploadImage).
So where is the problem then?
I guess the problem you are running into is due to the fact that the fileField view is not created automatically so the lookup fails.
Try to create the view instead of extending:
App.PreviewUploadImage = Ember.ContainerView.extend({
childViews: ['fileField'],
fileField: Ember.TextField.create({...}),
});
Update
edited my answer I forgot something substantial, to make the outer view a ContainerView and define fileField as a child view in the childViews array.
Hope it helps.
this does not reference the view, but your controller content. You will have to reference the view using view, like do this:
<script type="text/x-handlebars">
{{#view Ember.View contentBinding="App.myModel"}}
{{#view App.PreviewUploadImage name="logo_image" contentBinding="content"}}
{{view view.fileField}}
{{view view.previewImageView width="200" height="100" srcBinding="content.myModel_src"}}
{{/view}}
{{/view}}
</script>

Reference model properties inside {{#view}} within {{#each}} block

I'm trying to reference a model property inside a handlebars {{#view}} which is itself inside a handlebars {{#each}} block. In the example below, the {{title}} property (inside the <a> tags) is output correctly, but the item property of the view ends up just being the string, 'title'.
{{#each}}
{{#view view.PostNavItemView item=title}}<a href="#" {{action goToRoute 'post' this}}>{{title}}</a>{{/view}}
{{/each}}
Can I pass the {{#each}} model's properties into the view helper, somehow?
Instead of item you should use itemBinding.
{{#view view.PostNavItemView itemBinding='title'}}

Ember.js {{linkTo}} to itself does not render

I have a problem with a linkTo that points to itself. The actual problem is in an application that has a sidebar with a set of links. The first time a click the link the model and view is loaded and displayed properly. However on the second click the view and/or model disappear.
I've condensed the problem in this jsfiddle. The index route displays a list of links and when clicking on one of the links it will display the detail. In the detail page I have a link to itself but when I click it the model does not show.
The detail template shows the content fine but note the {{firstName}} or {{lastName}}
<script type="text/x-handlebars" data-template-name="item">
<h2>Item Content:</h2>
{{#linkTo "item" id}}Reload me{{/linkTo}}
{{content}}
<ul>
<li>{{firstName}} {{lastName}}</li>
</ul>
</script>
I think the issue is similar to Ember.js - linkTo error on second call
Updated sample
Here is a new example With linkTo at the top levelwhere the {{linkTo}} item is in the top level application template
<script type="text/x-handlebars" data-template-name="application">
{{#linkTo "test" 0}}Test 0{{/linkTo}}
{{outlet}}
</script>
Following #intuitivepixel solution using an action gives me the same result
<script type="text/x-handlebars" data-template-name="application">
<a href="#" {{action reloadMe "test" 0}}>Test 0</a>
{{outlet}}
</script>
Not everything can be done with the {{linkTo}} helper, IMO for this kind of feature (like a reload) you should go for a {{action}} helper instead.
First define the action that does the routing for you when called:
App.TestRoute = Ember.Route.extend({
events: {
reloadMe: function(route, content) {
this.transitionTo(route, content);
}
},
serialize: function(model) {
return {id: model.id};
}
});
And then in your template:
<a href="#" {{action reloadMe "test" content}}>Reload me</a>
See here how you could implement it: http://jsfiddle.net/FSd6H/4/
Hope it helps.

Ember.js: Passing model into view

I have a controller with data about user accounts (icon, name, provider, etc.). Within the output of the each loop I have a view that will build a CSS class dynamically based on the provider passed in via that specific model.
<script type="text/x-handlebars" data-template-name="accountItem">
{{#each account in controller}}
{{#view App.AccountView}}
<h4>{{account.name}}</h3>
<img {{bindAttr src="account.icon"}} />
<i {{bindAttr class="account.provider"}}></i>
{{/view}}
{{/each}}
</script>
App.AccountView = Ember.View.extend({
tagName: 'a',
classNames: ['avatar-image'],
providerClass: function(el) {
// do something
}
});
The question I have is two-fold.
How do you pass in "account", or the currently iterated item, into the view?
After you pass it in, how do you reference it?
I'm sure this is something that happens quite often but I can't seem to find any examples. Can anyone offer some input on this please?
Views has a special content property in a view which allows a more simple approach: you just use a name of the model's property without the view.content. part.
Also, when you're iterating over controller, you can omit the name of loop variable and use this instead, like in this guide. This is not necessary but can make the code a bit cleaner.
Also, from within view's template you generally don't need to reference the outside variables although you can if you like..
{{#each controller}}
{{#view App.IndexView contentBinding="this"}}
<h4>{{name}}</h4>
<img {{bindAttr src="icon"}} />
<i {{bindAttr class="provider"}}></i>
<i> {{icon}} </i>
<i>{{provider}}</i>
{{/view}}
{{/each}}
And you can always access the content property from within the view with:
this.get('content');
The currently iterated item can be passed into the view with the help of property bindings and it can be refered as "{{view.property}}" in the template. For example:
{{#each account in controller}}
{{#view App.IndexView itemBinding="account"}}
<h4>{{view.item.name}}</h3>
<img {{bindAttr src="account.icon"}} />
<i {{bindAttr class="account.provider"}}></i>
<i> {{view.item.icon}} </i>
<i>{{view.item.provider}}</i>
{{/view}}
{{/each}}
I have created a simple jsfiddle for the above case. Do check it and let me know if you were able to resolve the issues.
Fiddle url : http://jsfiddle.net/nCyn6/3/

Ember.js and handlebars each helper, with subviews

I'm triyng to use the view helper inside my {{#each}} template blocks without using global paths (my controllers create and destroy their own views).
Examples. Given a view with a myList array property, and an itemButton child view:
This will work
<script type="text/x-handlebars" name="my-list-view">
{{#each myList}} <!-- App.myListView.myList -->
{{view App.myListView.itemButton}} {{title}}
{{/each}}
</script>
This will not:
<script type="text/x-handlebars" name="my-list-view">
{{itemButton}} <!-- works fine outside the each -->
{{#each myList}}
{{view itemButton}} {{title}} <!-- itemButton view not found -->
{{/each}}
</script>
I do not appear to be able to access the parent view from the each view helper (or in fact access anything other than the properties of the objects being iterated).
The hacky workarounds I've come up with are:
Add the view I want to use to the items I'm iterating over.
or
Creating a collectionView in App.myListView
Create an itemViewClass view in that collection view class
Move the itemButton view inside the itemViewClass
Replace {{#each}} with {{#collection}}
or
Create a custom handlebars helper for iteration.
Both of these options seem horrible.
Surely there's a better alternative than creating 2 new classes (and nesting 4 views deep) just to iterate over a list, though. Is there a replacement handlebars helper I can use instead?
Workaround implementations
Option #1 : Modifing the content array
http://jsfiddle.net/FQEZq/3/
Disadvantages: Having to add the view to each model instance just for iteration.
Option #2 : Custom collection view
http://jsfiddle.net/ST24Y/1/
Disadvantages: Now you have two additional views that you do not need / want, and less control of markup. References from the child view to the parent instance now requires parentView.parentView.parentView.
#each is too limited for your requirements. You can make it work if you're willing to use a global path to the view you want to nest within the #each. Otherwise, your collection view approach is best. Adding the view to the model instance is likely to muddy your app design something fierce, so I would avoid that.
One idea to keep your templates clean is to take advantage of Ember.View properties like:
collectionView - Return the nearest ancestor that is an Ember.CollectionView
itemView - Return the nearest ancestor that is a direct child of an Ember.CollectionView
contentView - Return the nearest ancestor that has the property content.
The big thing here - options.
Hooks for how you wish to use a template are available. These are:
<-- render templates/name.js -->
{{partial 'name'}}
<-- render views/name.js -->
{{view 'name'}}
<-- render views/name1.js with controllers/name2.js -->
{{render 'name1' 'name2'}}
<!-- see also: -->
{{output}}
{{output 'named'}}
{{yield}}
An example variant of your initial template showing 4 different options:
<script type='text/x-handlebars' data-template-name='my-list-view'>
<h2>Option 1</h2>
{{#each myList}}
{{! the 2nd parameter will look for itemButtonController }}
{{render 'itembutton' itemButton}}
{{/each}}
<h2>Option 2</h2>
{{#each myList}}
{{! using an Ember Component }}
{{#item-button }}
some static text OR {{dynamic}}
{{/item-button}}
<!-- in component/item-button.hbs add {{yield}} for where you want the static content to output -->
{{/each}}
<h2>Option 3</h2>
{{#each myList}}
{{! if the button is to be a link }}
{{#link-to 'route' parameter tagName='button' classNames='btn'}}
{{title}}
{{/link-to}}
{{/each}}
<h2>Option 4</h2>
<p>Ludicrous example showing almost every reference option!</p>
{{! focus the context on subview data }}
{{#with someArrayOrCollectionOfView}}
{{! prepend type to add context - here returning back up to this view's properties }}
{{#each view.myList}}
{{! this is equivalent to someArrayOrCollectionOfView[x].name }}
{{name}}
{{! a button that hooks in route, model, and 2 controllers, and applies a target for the output when clicked }}
{{#link-to 'page' controllers.page.nextPage target='outlet' tagName='button' disabledWhen=controller.disableNext }}
{{model.buttonName}}
{{/link-to}}
{{/each}}
{{/with}}
{{! generic default target (automatic) }}
{{outlet}}
{{! example of a named target }}
{{outlet 'footerlinks'}}
</script>
Mmmm... reference for further reading:
Ember.Handlebars.helpers