handling action in view not router - ember.js

I have a view that has expandable/collapsable content that I'd like to be able to toggle by clicking the on the table row. Before pre1.0, I had this in the template:
<tr {{action "expand"}}>
which was previously handled on my view:
App.ContentRowView = Em.View.extend({
templateName: 'ember/templates/content/row',
expand: function() {
this.set('isExpanded', !this.get('isExpanded'));
},
isExpanded: false
});
However, after upgrading to pre1.0 the action is now fielded directly by the router. This makes sense in a lot of situations, but in this case the expansion is really a view concern. I've tried just replacing this with a click event handler without luck.
Is there a best practice on how to handle a view concern event like this with pre1.0?

Deprecated Answer
Even if the answer of #outside2344 works, I think it's not exactly right.
Indeed parentView does not represent the view, but the parentView of its parentView.
Since 1.0-pre, views preserve their context, so in the template, this represents the parentView, parentView represents parentView.parentView, and view represents the current view.
Here is a fiddle to illustrate this: http://jsfiddle.net/Sly7/cnmJa/
For me the answer is {{action expand target="view"}}
EDIT (answering to #Gal Ben-Haim)
Action helpers behave little different in a router-based application. Quote from the documentation:
In Router-driven applications, if an action is not intercepted by a view, that event will bubble up to the Route in which that view was rendered. If that Route is a sub-route of another Route the transition will be sought there all the way up to the top-level Route definition, our über-container: root.
This bubbling effect allows certain actions to remain private. If certain transitions should only be available for certain sub-sub-states, put the transition on the sub-state and you've achieved a type of scoping.
Basically, for me that means in Router-driven apps if you don't explicitly define a target in the action helper, it is sent to the router.
Updated answer
I think now the guides answer very well to this question. see http://emberjs.com/guides/templates/actions/#toc_specifying-a-target

In pre1.0 you can make the view field the action by adding target="parentView" to the action:
{{action "expand" target="parentView"}}

Events doesn't bubble through the view-hierarchy by default. You can change this (though I can't say I'd recommend it):
(function() {
Ember.View.reopen({
// Let actions bubble to parentView by default.
target: function() {
return this.get('parentView');
}.property('parentView')
});
})();

Related

How do I bind a controller to the view when the controller is create by Ember.js

this question is slightly related to How to display the “content” of an ObjectController?
However, in the provided solution and all other examples I can find the controllers are always created explicitly. The nice thing about Ember.js is that the Route takes care of mostly everything. So I don't want to create the controller but want to bind it to a view:
{{view App.myview controllerBinding="App.applicationController"}}
You can see the complete example in this fiddle. The example is not that great because Ember usually sets the controller of a child view to its parent view.
In the end I need to know, how I can access a controller which is created by Ember from a view.
Thanks for any help!
Update:
I provided the wrong fiddle or it did not save my changes. Here is the link to the right version: http://jsfiddle.net/ncaZz/1/
What should I provide in line 9 in the templates?
From the view you can access the controller with
this.controller
If you need other controllers than your view controller you can use the needs in the viewcontroller:
App.DatasetEditController = Ember.ObjectController.extend({
needs: ['mappingIndex']
});
and then use:
this.controller.mappingIndex
You don't really need to bind to it. You can access the controller from the view by calling it like this.
this.get('controller');
Updated Answer:
You really should not have your click event inside your view. Your actions should either be in your controller or your router.
Your template should become
<span style="background-color: green" {{action doStuff}}>
Click
</span>
and you should have a controller that should have this
App.MyController = Em.Controller.extend({
needs: ['application'],
doStuff: function(){
this.get('controllers.application').foo();
}
});
Also, the MyView and MyController should be capitalized, because when extending these items from ember that are not instances, and the capitalization is required. The view should only really have stuff in the didInsertElement that handles special things like any kind of jquery animations or initializing a date picker. But, the "ember way" is to have action in the router or controller.

{{outlet}}, {{view}}, {{render}}, and {{control}} helpers

I am trying to put together a simple master-details Ember app. Directory tree on one side and file list on another.
Ember offers few helpers to render context into a view. Which of them I can use for:
Subtrees of the directory tree.
Details list.
In fact, would be very helpful if someone can point me to any docs I can read about the difference between {{render view}}, {{view view}} and {{control view}} helpers and how to use them properly.
Thanks a lot!
{{view "directory"}} renders the view within the context of the current controller.
{{render "directory"}} renders the view App.DirectoryView with template directory within the context of the singleton App.DirectoryController
{{control directory}} behaves the same way as render only it creates a new instance of App.DirectoryController every time it renders (unlike render which uses the same controller instance every time).
Update 18 Feb 2014: {{control}} has been removed.
The last two helpers are relatively new, so there isn't much documentation about them. You can find {{view}} documentation here.
Now looking at your use case, I don't think you need any of these helpers. Just use nested routes and the {{outlet}} helper and it should just work.
App.Router.map(function(){
this.resource('directories', function() {
this.resource('directory', { path: '/:directory_id'}, function() {
this.route('files');
});
});
});
You can build on that following this guide.
UPDATE: {{render}} now creates a new instance every time if you pass a model.
For a very good explanation of the helpers render, partial, outlet and template have a look at this question.
Just as a rough a summary, how one might use those helpers:
{{render "navigation"}} -> Renders the NavigationController and NavigationView at this place. This is helper is good for places, where the Controller and View do not change, e.g. a navigation.
{{outlet "detailsOutlet"}} -> This will provide a stub/hook/point into which you can render Components(Controller + View). One would use this with the render method of routes. In your case you will likely have a details route which could look like this. This would render the DetailsController with DetailsView into the outlet 'detailsOutlet' of the index template.
App.DetailsRoute = Ember.Route.extend({
renderTemplate: function() {
this.render('details', { // the template/view to render -> results in App.DetailsView
into: 'index', // the template to render into -> where the outlet is defined
outlet: 'detailsOutlet', // the name of the outlet in that template -> see above
});
}
});
{{view App.DetailsView}} -> This will render the given view, while preserving the current context/controller. One might change the context, e.g. using your master entity and pass its details to a view like this:
{{view App.DetailsView contextBinding="masterEntity.details"}}
This is helper is useful, when you want to encapsulate certain parts of a component in subviews, that have own custom logic like handling of actions/events.
{{control}} I know that control instantiates a new controller every time it is used, but I cannot see a good fit for your, nor have i a good example for using it.
To Understand the difference between ember {{render}},{{template}},{{view}},{{control}}
you can refer this article
http://darthdeus.github.io/blog/2013/02/10/render-control-partial-view/

binding context to action in ember textfield

I've got an ember application that needs to manage multiple chat windows. A window for each active chat is created within an {{#each}} loop. This is straightforward enough. The place that I'm having trouble is sending the chat message when the user presses enter.
The window looks like this
{{#each chats}}
... stuff to display already existing chats...
{{view Ember.TextField valueBinding="text" action="sendChat"}}
<button {{action sendChat this}}> Send </button>
{{/each}}
This works fine for the button, since I can pass this to it. By default the function defined in the textfield view action just gets the text within that textfield, which is not enough in this case. Since there can be multiple chat windows open, I need to know which window the message was typed into. Is it possible to pass this to the textfield action function? (or can you suggest a different way to solve this problem?)
Add contentBinding="this" to the definition of the view, like:
{{view Ember.TextField valueBinding="text" action=sendChat contentBinding="this"}}
EDIT
Ember master already has this change, but the official downloadable verstion still don't.. so you will need to subclass the Ember.TextField and change its insertNewline to achieve required functionality:
App.ActionTextField = Em.TextField.extend({
insertNewline: function(event) {
var controller = this.get('controller'),
action = this.get('action');
if (action) {
controller.send(action, this.get('value'), this);
if (!this.get('bubbles')) {
event.stopPropagation();
}
}
}
});
After that, the action handler will receive additional argument, the view:
{{view App.ActionTextField valueBinding="text" action=sendChat myfieldBinding="this"}}
and in controller:
sendChat: function (text, view) {
var myField = view.get('myfield');
//do stuff with my field
}
You may use ember master instead of subclassing Ember.TextField..
I hope the ember guys will release the next version soon..
I know this question has been answered but I said let me add some information that may help out someone in the situation of actions and TextField. One word "Component". TextField in Ember is a Component so if you think of TextField from that perspective it may help when it comes to sending actions and using TextField in an application.
So when you say App.SomeTextField = Ember.TexField.extend({...});App.SomeTextField is subclassing Ember.TextField (remember which is a component). You could add your logic inside and that works and you could access it from your template such as {{view App.SomeTextField}}
You may be thinking I see the word 'view' this guy sucks, TextField is a View. Well, it is sort of a View because Ember Components are a subclass of Ember.View so they have all that Views have. But there are some important things to keep in mind Components un-like Views do not absorb their surrounding context(information/data), they lock out everything and if you want to send something from the outside surrounding context you must explicitly do so.
So to pass things into App.SomeTextField in your template where you have it you would do something like {{view App.SomeTextField value=foo action="sendChat"}} where you are passing in two things value, and action in this case. You may be able to ride the fine line between View/Component for a bit but things come crashing why is your action not sending?
Now this is where things get a little trippy. Remember TextField is a Component which is subclassed from View but a View is not a Component. Since Components are their own encapsulated element when you are trying to do this.get('controller').send('someAction', someParam), "this" is referring to the Component its self, and the controller is once again the component its self in regards to this code. The action that you are hoping will go to the outside surrounding context and your application will not.
In order to fix this you have to follow the protocol for sending actions from a Component. It would be something like
App.SomeTextField = Ember.TextField.extend({
//this will fire when enter is pressed
insertNewline: function() {
//this is how you send actions from components
//we passed sendChat action in
//Your logic......then send...
this.sendAction('sendChat');
}
});
Now in the controller that is associated with where your SomeTextField component/view element is you would do
App.SomeController = Ember.Controller.extend({
//In actions hash capture action sent from SomeTextField component/view element
actions: {
sendChat: function() {
//Your logic well go here...
}
}
});
Now I said to think of TextField as a Component but I have been riding the tail of the view and declaring {{view AppSomeTextField...}}. Lets do it like a component.
So you would have in your template where you want to use it
//inside some template
`{{some-text-field}}`
Then you get a specfic template for the component with the name:
//template associated with component
<script type="text/x-handlebars" data-template-name="components/some-text-field">
Add what you want
</script>
In your JS declare your component:
//important word 'Component' must be at end
App.SomeTextFieldComponent = Ember.TextField.extend({
//same stuff as above example
});
Since we on a role you could probably get the same functionality using Ember input helpers. They are pretty powerful.
{{input action="sendChat" onEvent="enter"}}
Welp hopefully this information will help someone if they get stuck wondering why is my action not sending from this textField.
This jsBin is a sandBox for Components/Views sending actions etc....Nothing too fancy but it may help someone..
http://emberjs.jsbin.com/suwaqobo/3/
Peace, Im off this...

designing a custom component/view on emberjs

I'm having trouble doing things on Ember, and I'm sure it's because I still haven't fully grasped "the Ember way" of doing things, and I'm a trying to do a couple of things that get out of the scope of standard tutorials out there.
I'm developing some sort of textfield-with-suggestions component that will appear in every single page of my web app. I won't ask here all the details on how to do it, but just a couple specific things I am having trouble accomplishing from the start. The following are the relevant code fragments of what I have so far.
// handlebars template: searchbar.hbs
{{view App.SearchField viewName="searchField"}}
<div class="results" {{bindAttr class="searchField.hasFocus"}}>
This is where the results for whatever the user has typed are shown.
</div>
// coffeescript source of the views: searchbar.coffee
App.SearchBar: Ember.View.extend
templateName: 'searchbar'
App.SearchField: Ember.TextField.extend
placeholder: 'Search'
hasFocus: false
eventManager: Ember.Object.create
focusIn: (event, view) ->
#set('hasFocus', true)
focusOut: (event, view) ->
#set('hasFocus', false)
// somewhere in the layout of the pages in my app
<div id="header">
{{App.SearchBar}}
</div>
This will probably also need a controller, but I haven't developed it yet, because I don't know where it fits within this setup yet.
First, I want the suggestions popup panel to appear as soon as the search field obtains focus. That's the reason of my attempt above to implement a hasFocus property on the searchfield. But how do I achieve making my div.results panel react to the focus state of the input field?
In general, and this is the core of my question here, how do I connect all things to develop this component? If the answer is by attaching it to a controller, then how do I setup a controller for this component, and how do I specify that it is the controller for it, so that it acts as a context for everything?
I think you have to clearly separate concerns. Stuff related to the view (ie manipulating the DOM with jquery) should stay in the view. Stuff related to the application state should be in the controller. Though,in your case, I think you can simply bind an observer on the hasFocus property, and show the suggestions. Something like:
App.SearchField: Ember.TextField.extend
placeholder: 'Search'
hasFocus: false
eventManager: Ember.Object.create
focusIn: (event, view) ->
#set('hasFocus', true)
focusOut: (event, view) ->
#set('hasFocus', false)
focusDidChange: (->
if #hasFocus
$('.results')... // here I let you do the suggestion stuff
// based on data retrieved from the controller
else
// probably hide the results div.
).observes('hasFocus')

Ember Views, Handlebars and jQuery Effects

I would like to incorporate jQuery effects (fadeIn, fadeOut, etc...) in parts of my handlebar templates. I think that this can more or less be accomplished with a separate view in which the view's isVisible property is initially false and its didInsertElement method calls something like this.$().fadeIn().
However, what I'd like to do is add a jQuery effect to just a small part of a view - say for purposes of displaying a small block of content that is initially hidden by an {{#if}} statement that evaluates to false and later through user feedback gets toggled to true. See the following http://jsfiddle.net/YeGbF/2/.
Any suggestions?
You could use a view for the stuff which shall be shown faded in, see http://jsfiddle.net/pangratz666/dJMwC/
Handlebars:
{{#view App.FadeInView contentBinding="this"}}
<div>{{content.someAdditionalDetail}}</div>
{{/view}}
JavaScript:
App.FadeInView = Ember.View.extend({
didInsertElement: function(){
this.$().hide().show('slow');
}
});
Also have a look at Deferring removal of a view so it can be animated