Emberjs action in collectionView - ember.js

I have managed to use the collectionView like this :
App.GroupList = Ember.ArrayController.create({
content: this.getGroupList(),
update: function() {
this.setProperties({content: getGroupList()});
}
})
App.GroupListView = Ember.CollectionView.extend({
tagName: 'tbody',
contentBinding: "App.GroupList"
})
<table class="table-history">
{{#collection App.GroupListView}}
<td class="col-left">
{{view.content.groupName}}
<span class="col-number-contact">{{view.content.participantCount}}</td>
</td>
<td>
<div class="ph-img"></div>
</td>
<td>
<a {{action "editGroup" view.content}}>
<div class="ed-img"></div>
</a>
</td>
{{/collection}}
</table>
And as you can see I have put an action helper {{action "editGroup" view.content"}} and this is working well to handle the action in the controller. But I need to handle this action in the view.
When I put the helper like this {{action "editGroup" view.content target=view}} it doesn't work. I have no error, but I also can't catch the event in my view.
My template is called home, and my view App.HomeView = Ember.View.extend({}).
I really don't understand...
I have create a fiddle to show you that it doesn't work : http://jsfiddle.net/NQKvy/902/
If you remove the target=view in the action helper it will trigger the controller handler but nothing happend in the view..
[edit]: Otherwise, is it possible to access a view variable from a controller ? to edit or update a view variable from a controller.

In your case target="view" will target anonymous view created for each GroupListView's item. You'll probably want something like:
App.GroupListView = Ember.CollectionView.extend({
tagName: 'tbody',
contentBinding: "App.GroupList",
actions: {
editGroup: function() {
alert("You Clicked - in GroupListView");
}
}
})
<a href="#" {{action "editGroup" view.content target="view.parentView"}}>
LINK
</a>
Then you can handle your clicks in GroupListView. If you really need it to be in IndexView, you can add another layer of indirection: target="view.parentView.parentView". However, code becomes really brittle then, so I wouldn't recommend it.
At this point, I should warn you that the way you're using views and controllers is not following Ember's guidelines (controller naming, manual controller creation and then binding to a hard-coded instance, ...). Before digging yourself deeper, I would suggest you go through Ember's docs once again and reexamine your code before it calcifies in its current shape.

Related

How to send a click even to a view (it already works but I think it is not the Ember way)

I want to style a button for file upload. I already have a working, but it feels like I dont do it the Ember way.
My view:
App.FileUploadView = Ember.TextField.extend({
(....)
});
template:
{{view App.FileUploadView name="big-image" file=big-image id="big-image"}}
<a {{action clickUploadView}} href="#">Upload file</a>
controller action:
clickUploadView: function(){
Ember.$('#big-image').click();
},
I have fiddled around to use the viewName property like this in my template and call it from the controller: {{view App.FileUpload ... viewName="big-image"}} but could not get the click fired.
Any thoughts or is this just correct?
Wrap your anchor tag in the view helper and add the action to the view, then target the view.
{{#view App.SomethingView}}
<a {{action daClick target="view"}} href="#">Upload file</a>
{{/view}}
App.SomethingView = Em.View.extend({
actions:{
daClick: function(){
alert('hello');
}
}
});
http://emberjs.jsbin.com/eHAsexI/1/edit
Or if you just want it to hit the click event of the view, you can just rip out the action all together and leave the anchor tag in the view and it will hit the click event of the view.
http://emberjs.jsbin.com/eHAsexI/2/edit

Selected item in a template, is there any solution for a context aware bindAttr?

The problem is as follows:
In our application we have several buttons, navigation icons etc., which we want to be 'selected' when they have been clicked. We can have multiple elements marked at the same time.
The secondary reason for me wanting to do this is that when I read the new Guides on emberjs.com I get the feeling that templates should be used more than stated before and that templates should have the responsibility of rendering the DOM, while the views should be used to handle sophisticated events (if any) or to create common/shared components to be reused in the application.
Currently the view is handling this:
app.NavView = Ember.CollectionView.extend({
...
itemViewClass: Ember.View.extend({
...
classNameBindings: ['isSelected:selected']
isSelected: function () {
return this.get('controller.selected') === this.get('content');
}.property('controller.selected')
})
});
But that is all the View basically is doing, I would like to drop the entire View and just use a template for this
I have tried with a template approach, and dropped the entire View concept.
<div id="main-menu">
{{#each content}}
<div {{bindAttr class="controller.isSelected:selected"}}>
{{{iconsvg}}}
{{name}}
</div>
{{/each}}
</div>
But my problem here of course is that bindAttr doesn't know about the context it’s in, and cannot 'send' this to the isSelected property on the controller to evaluate if it is this element that is selected or not.
Is there a good solution to do this without a view, or am I forced to use a view?
Or am I thinking the design part and responsibility of Templates/views/controllers wrong?
Any response is appreciated!
In the current documentation: http://emberjs.com/guides/templates/displaying-a-list-of-items/ there is a mention explaining how to use the {{each}} helper which doesn't override the current context.
In your case, this would be something like:
<div id="main-menu">
{{#each item in controller}}
<div {{bindAttr class="isSelected:selected"}}>
{{{item.iconsvg}}}
{{item.name}}
</div>
{{/each}}
</div>
Note I have remove the reference to 'controller' in the {{bindAttr}} since I assume it's an ember controller, then it's the current context, so basically isSelected is equivalent to controller.isSelected

Assigning 'active' class to selected list item in EmberJS

I have a list and I'd like to set one item as class="active" automatically. Given the following bootstrap code:
<ul class="nav">
<li {{bindAttr class="atIndex:active"}}>{{#linkTo "index"}}Index{{/linkTo}}</li>
<li {{bindAttr class="atAbout:active"}}>{{#linkTo "about"}}About{{/linkTo}}</li>
<li {{bindAttr class="atLogin:active"}}>{{#linkTo "login"}}Login{{/linkTo}}</li>
</ul>
atIndex, atAbout and atLogin reside in my ApplicationController.
To render as:
<ul class="nav">
<li class="active"><a...>Index{{/linkTo}}</li>
<li><a...>About<a></li>
<li><a...>Login<a></li>
</ul>
What's the best way to do this with Ember 1.0 pre4? I'd rather not add special code to every view or controller.
PS - atIndex: true works, but atIndex: function() {return true; }.property().volatile() does not. Which makes me think I'm doing something wrong.
Thank you!
{{#link-to "dashboard" tagName="li" href=false}}
<a {{bind-attr href="view.href"}}>
Dashboard
</a>
{{/link-to}}
By far the cleanest way to solve this is by taking advantage of the linkTo helper's built-in support for setting the active class when rendering links. AFAIK this is not yet documented other than in the source code:
implementation: https://github.com/emberjs/ember.js/blob/master/packages/ember-routing/lib/helpers/link_to.js#L46
example: https://github.com/emberjs/ember.js/blob/master/packages/ember/tests/helpers/link_to_test.js#L120
To take advantage of this feature just adjust your css to style based on having an active class on the link instead of the li element. If you really need to style the li you can create a custom view and helper that extends Ember.LinkView and uses an li but changing css will be far easier.
--- UPDATE ----
Since we all love twitter bootstrap just changing the css is perhaps not such a great option. In that case, the following will do the trick:
App.ApplicationView = Ember.View.extend({
currentPathDidChange: function() {
Ember.run.next( this, function() {
this.$("ul.nav li:has(>a.active)").addClass('active');
this.$("ul.nav li:not(:has(>a.active))").removeClass('active');
});
}.observes('controller.currentPath')
});
Working example using ember linkTo helper with bootstrap pills: http://jsbin.com/ekobod/5/edit (requires ember-1.0.0-pre.4)
the active route's path is updated automatically in the ApplicationController via currentPath so I did something like that in my App...
In ApplicationController added properties like so:
isProductsActive: function(){
if ( this.get('currentPath') === 'products' ) return 'active';
else return '';
}.property('currentPath')
and in my ApplicationView template:
<li {{bindAttr class="isProductsActive"}}>
{{#linkTo "products"}}Products{{/linkTo}}
</li>
I made an ember-cli addon that handles this:
https://github.com/alexspeller/ember-cli-active-link-wrapper
EDIT:
Finally, the best way I've found to use the activate class of bootstrap li element using ember.js of the link.
{{#linkTo "dives" tagName="li"}}
<a {{bindAttr href="view.href"}}>Dives</a>
{{/linkTo}}
--------------8<--------------
DEPRECATED:
I guess the previous answers were relevant before Ember.js introduced the activeClass attribute for linkTo helper.
Now I would solve the problem like this :
<ul class="nav">
<li >{{#linkTo "index" activeClass="active"}}Index{{/linkTo}}</li>
<li >{{#linkTo "about" activeClass="active}}About{{/linkTo}}</li>
<li >{{#linkTo "login" activeClass="active}}Login{{/linkTo}}</li>
</ul>
Enber will automatically add the class when relevant.
If I may suggest another solution that requires nothing but Handlebars:
<li {{bind-attr class="view.innerLink.active:active"}}>
{{#link-to "path" viewName="innerLink"}}Click{{/link-to}}
</li>
This sets the LinkView object as a member of the parent view, which's active attribute you can then reference.
I found a pretty simple Solution using linked items in a list group(http://getbootstrap.com/components/#list-group-linked).
<div class="list-group">
{{#each thing in list}}
{{#link-to "details" thing.id tagName="a" href="view.href" class="list-group-item" {{thing.name}} {{/link-to}}
{{/each}}
</div>
Works with Bootstrap v3.1.1 and Ember v1.7.0
Just nest the {{link-to}} with a tagName on the outer one. I'm doing this on EmberJS 2.0.
{{#link-to "admin.websocket" tagName="li"}}
{{#link-to "admin.websocket"}}WebSocket{{/link-to}}
{{/link-to}}
If you want to use Bootstrap navigation in Ember then you can use Bootstrap for Ember that has out of the box support for this:
Github: https://github.com/ember-addons/bootstrap-for-ember
Demo: http://ember-addons.github.io/bootstrap-for-ember/dist/#/show_components/tabs
A lot of these answers are outdated. Here is a much cleaner (and DRY) version for Bootstrap and Ember 2.x:
{{#link-to "index" tagName="li" as |link|}}
Index Page
{{/link-to}}
I solved a similar problem by creating a view for each item and using classNameBindings (I have to say that i don't have a HTML list, i.e.<a>...</a> in my app, just list of <div>).
Here is the way it works for me:
In tasklist.handlebars i iterate over my custom view
{{#each tasks}}
{{view App.TaskListItemView contentBinding="this"....}}
{{/each}}
Ember will insert a view (i. e. <div>) for each item.
The view class for each item is defined in task_list_item_view.js as
App.TaskListItemView = Ember.View.extend({
controller: null,
classNameBindings: ['isSelected', 'isClosed'],
isClosed: function() {
var content = this.get('content');
return content && !content.isOpen(new Date);
}.property('controller.content.#each'),
isSelected: function() {
return (this.get('controller').isSelectedTask(this.get('content')));
}.property('controller.taskSelection.#each'),
....
});
Finally the template for the view just renders my link in tasklistitem.handlebars
<a {{action "selectTask" view.content target="view"}} rel="tooltip"
{{bindAttr title="view.content.comment"}} class="taskListLink">
....
</a>
AFAIK you have to specify the source data in the property() call to let ember know when to (re-) evaluate the property.
Hope that helps
I went with:
Ember.LinkView.reopen({
didInsertElement:function(){
if(this.$().hasClass('active')){
this.$().parent().addClass('active');
}
}
});
I didn't want to use the accepted answer as I wanted to keep my li elements as plain old html. There might be a better way to check the active state but I couldn't get access to the right property.

Ember.js : How to set context of an action helper?

I added the example application.
http://jsfiddle.net/Sly7/amG56/
Js:
App = Ember.Application.create();
App.ApplicationController = Ember.ArrayController.extend({
selectedBook: null
});
App.ApplicationView = Ember.View.extend({
actions: {
selectBook: function(book) {
this.get('controller').set("selectedBook", book);
},
cancel: function(book) {
alert(book);
}
}
});
App.Book = Em.Object.extend({
name: null
});
Template:
<script type="text/x-handlebars">
{{#each book in books}}
<a {{action "selectBook" book target="view"}} href="#">select {{book.name}}</a><br />
{{/each}}
<hr />
Selected Book: {{selectedBook.name}}
<br />
<a {{action "cancel" selectedBook target="view"}} href="#">cancel selected book</a>
</script>
​
Select one of the books. You will see that name of the book will be displayed. But the "cancel selected book" link does not work.
I think the problem is context of the action helper does not change when a book is selected.
How do I implement an action helper which has a changing context? Or is it a bug?
The answer is in the guides
http://emberjs.com/guides/templates/actions/#toc_action-parameters
And the context is lazily evaluated, so the problem does not occur anymore
DEPRECATED ANSWER BELOW
The problem here is that the action helper is interpreted with the selectedBook context. But at this time, selectedBook is null. So when clicking on the link, even if you previously select a book, it's too late, for the registered action, the context is still null.
As a workaround, you can enclose this with a {{with}} block.
{{#with selectedBook}}
Selected Book: {{name}}
<br />
<a {{action cancel this target="view"}} href="#">cancel selected book</a>
{{/with}}
see: http://jsfiddle.net/x82dr/17/
BTW, you can see the code of the ApplicationView, where I access the application controller, using the controller property. With Ember.js convention, the controller is injected to the view when the application initialize
UPDATE: The use of the {{with}} helper seems to be not mandatory now, see: https://github.com/emberjs/ember.js/issues/1150

ember.js - how to apply action to entire {{#view}}

I'd like to specify my action (and 'target' and 'on') in the {{#view}} instead of as below on a contained within
{{#view App.Views.List
contentBinding="this"
classNames="item"
classNameBindings="content.type content.selected:selected"
}}
<div {{action "select"}}>text</div>
{{/view}}
So that the click applied to the whole area of the App.Views.List instance. Is this possible?
How about just defining the method click straight on the View instead of using an {{action}}?
App.Views.List = Ember.View.extend({
click: function() {
alert('clicked');
}
});
See this fiddle for an example.