Ember.js controller needs and child controller - ember.js

I'm having trouble targeting a child controller from the parent controller.
Having this:
<script type="text/x-handlebars" data-template-name="parent">
<h1>Parent controller</h1>
{{control "child"}}
</script>
<script type="text/x-handlebars" data-template-name="child">
<h2>Child controller</h2>
{{controller}}
</script>
App.ParentController = Ember.Controller.extend({
needs: ["child"],
applyActionOnChild:function(){
this.get('controllers.child').someAction();
}
});
App.ParentView = Ember.View.extend({});
App.ChildController = Ember.Controller.extend({
someAction:function(){
//Called but not the right instance
};
});
App.ChildView = Ember.View.extend({});
The needs feature of ember seems to work when a child controller calls the parent controller but not for the other way around...
If I place a breakpoint in the someAction method, I can see I have a different object reference than the one displayed on the child view...
Anyone knows how this could work?
Thanks in advance

In your example you specify {{controller "child"}}, did you mean {{control "child"}}? If not hard to tell what you're trying to do.
The needs feature is for wiring together controller singletons, but The control helper renders template in context of a new controller/view pair. You can't use needs to access these controllers, since there'd be no way for needs to know which instance you meant.
Sounds like instead you want to render "child" template in context of the one true childController singleton. In that case use the render helper: {{render "child"}}

Related

Obtaining component elementId in parent template

I would like to obtain component auto-generated elementId in parent template, e.g.
<script type="text/x-handlebars" data-template-name="index">
my-component elementId: {{myComponentElementId}}
{{my-component}}
</script>
Is there a simple way to do this without altering parent controller or my-component?
elementId is being generated during initialization of the component. You can be sure that the ID is set after the component is fully inserted into DOM. Therefore, you can use didInsertElement action like this:
some-example elementId: {{myComponentElementId}}
{{some-example elementIdProxy=myComponentElementId}}
didInsertElement: function() {
this.set("elementIdProxy", this.get("elementId"));
}
Thus, you proxy the elementId via some other binding (elementIdProxy in this situation). I strongly recommend not to set up the ID explicitly, as it must be unique within the app. Ember takes care about that, so it's not a good idea to reinvent the wheel.
Fully working example in JSBin is here
In your bin you are trying to pass an id but not specified the value on the controller. Set the id value in the setupCotroller hook on the controller and that value will be used as the id.
Otherwise you will need to pass the id up via actions. Here is the link.
The problem (ie the source of your error message) is you're trying to override the component's elementId property via attribute binding. I have run into this issue when trying to pass in id. A simple work around is to use _elementId or any other word that isn't elementId. Check the JSBin
I was able to access elementId by:
explicitly naming component using its viewName attribute
accessing component by its name from parent template view.[componentName].elementId
Example:
<script type="text/x-handlebars" data-template-name="index">
my-component elementId: {{view.myComponent.elementId}}
{{my-component viewName="myComponent"}}
</script>
JsBin
NOTE
With introduction of Glimmer rendering engine (Ember version >=1.13), accessing elementId via viewName is not supported any more.
Instead, component's elementId should be explicitly set to specific value, e.g.
Example:
<script type="text/x-handlebars" data-template-name="index">
my-component elementId: index-my-component
{{my-component id="index-my-component"}}
</script>
In this case, it is up to developer to make sure that elementId is unique within page DOM. If template is to be rendered multiple times, elementId should be concatenated from parent component elementId and some predefined id (see JsBin)

Unable to link non-standard view with Ember Routing (RC 3) and with Ember AnimatedView

Does anyone know how to link a route say PostRoute (names changed from original app), that has an equivalent controller PostController with a non-standard view say BlogPostView, in RC 3? I am using Ember Animated outlet and view, but did nothing extra as far as routes/animations are concerned for RC 1, but RC 3 gives this error). In RC 1, just declaring something like this worked, how to modify route definition to make it work with RC 3? I tried re-building ember-animated-outlet.js with RC 3, but same result (hoping that it may have been built with pre-RC 3 ember version)?
App.Router.map(function(){
...
this.route('post');
...
}
along with
<script type="text/x-handlebars" data-template-name="application">
{{animatedOutlet name="main"}}
</script>
<script type="text/x-handlebars" data-template-name="post">
{{view App.BlogPostView}}
</script>
I get this error
Ember.AnimatedContainerView can only animate non-virtual views. You need to explicitly define your view class.
I tried calling the view within renderTemplate as this.render('blogPost') so that it finds BlogPostView, but on debugging such a call returns as "undefined".
I have found that the view for the post route is identified as virtual. How to tell Ember that for this route, use this view, which is non-virtual so that it stops complaining. My BlogPostView looks like this:
App.BlogPostView = Ember.View.extend({
template: $.template('blog_post'),
didInsertElement: function() {
...
}
...
});
Thanks,
Paddy
I guess the error comes from exact this line template: $.template('blog_post').
As statet in the docs:
All child views of an App.AnimatedContainerView need to be explicitly defined, since the animations only work with non-virtual views. This means that if you have a route called invoices.show and you expect to animate into it, you need to define the view for it: App.InvoicesShowView = Ember.View.extend()
What the docs do not explicitly state is that you need to define also a template for your view, so therefore since you can't use precompiled template for the animated outlets, you have to define them early on. This means that if you have a BlogPostView you need a blogPost template already defined to backup the BlogPostView.
Your view would then result in something like:
App.BlogPostView = Ember.View.extend({
template: 'blog_post'
});
and a template to backup the view like:
<script type="text/x-handlebars" data-template-name="blog_post">
...
</script>
Let me know if it helps

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...

Uviews are not getting connected to controller

Ember has a Application which has ApplicationView, ApplicationController and 'application' named template and 'main' named outlet and all these connect automatically.
eg.
App.ApplicationView = Ember.View.extend();
and
App.ApplicationController = Ember.Controller.extend();
so whenever my application template renders its default controller is an automatic instance of
App.ApplicationController
whose properties i can access in the template. But why does it not works with other views and controllers, i.e. if I have
App.SongView = Em.View.extend()
and
App.SongController = Em.Controller.extend()
these two do not connect. I can use any property of
App.SongController
in my song template.
I can use like :
{{view App.SongView}}
and in the template:
<script type='text/x-handlebars' data-template-name='song'>
{{name}}
</script>
and if i have a name property in App.SongController then it wont get picked up because its not connected to the View.
although i can do it like this
{{view App.SongView controllerBinding='App.songController'}}
but this requires the instance of App.songController in my js file and also using this approach we are hard-coding the controller to the template.
What is the best way for this?
Update
I am also attaching a js fiddle for my problem here:
http://jsfiddle.net/anshulguleria/K6KPJ/
If you want to render a template and its associated controller and view, you can use the {{render}} template. It works similarly to this.render in the router.
{{render "song" song}}
This example will render the song template with an instance of App.SongController and App.SongView. It will set the song controller's model to the value of song in the current context.
Here is a working JSBin that illustrates how this works.

Controller Strategy / Garbage Collection (destroy)

Trying to figure out the "ember best practices" for my app, regarding MVC. also for reference, I'm using ember-data, ember-layout, and ember-route-manager.
I'll use User as an example:
what I feel like I want to do is to get a User model from the database... then wrap it in a UserController, and set the model on a "content" property... then in a View, I want to bind to the controller for some functionality, and to the controller.content for model-level data. so a controller might look something like:
App.UserViewController = Em.Object.create({
content: userRecord,
isFollowingBinding : 'content.you_follow',
toggleFollow: function() {
make server call to change following flag
}
});
then the view could bind to the {{controller.content.name}}, or {{#if controller.isFollowing}}, or {{action "toggleFollowing" target="controller"}}
but say I get a list of User models back from the database... I feel like what should happen is that each of those models should be wrapped with a controller, and that should be returned as a list... so the view would have a list of UserControllers
Incidentally, I've done this... and it is working nicely.... except that everytime I reload the list, I wrap all of the new model objects with new controllers... and over time, the # of controllers in memory get larger and larger. on my base Controller class, I'm logging calls to "destroy", and I dont see it ever happening
when it comes to Em.View... I know that everytime it is removed from the screen, .destroy() gets calls (I am logging those as well). so if I were to move my code into a view, i know it will get destroyed and recreated everytime... but I dont feel like the functionality like toggleFollow() is supposed to be in view...
SO QUESTIONS:
is this how MVC is supposed to work? every instance of a model wrapped in a controller for that model? where there could be lots of controller instances created for one screen?
if I go down this approach, then I'm responsible for destroy()ing all of the controllers I create?
or is the functionality I've described above really meant for a View, and them Ember would create/destroy them as they are added/removed from the screen? also allowing template designers to decide what functionality they need (if they just need the {{user.name}}, theres no need to instantiate other controller/view classes... but if they need a "toggle" button, then they could wrap that part of the template in {{#view App.UserViewController contentBinding="this"}} )
I re-wrote this a few times... hopefully it makes sense....
I wouldn't wrap every user into an own controller.
Instead I would bind the user to a view, say App.UserView and handle the action toggleFollow on that view. This action will then delegate it's action to a controller which will handle the server call, see http://jsfiddle.net/pangratz666/hSwEZ/
Handlebars:
<script type="text/x-handlebars" >
{{#each App.usersController}}
{{#view App.UserView userBinding="this" controllerBinding="App.usersController"}}
{{user.name}}
{{#if isFollowing}}
<a {{action "toggleFollowing"}} class="clickable" >stop following</a>
{{else}}
<a {{action "toggleFollowing"}} class="clickable" >start following</a>
{{/if}}
{{#if user.isSaving}}saving ...{{/if}}
{{/view}}
{{/each}}
</script>​
JavaScript:
App.usersController = Ember.ArrayProxy.create({
content: [],
toggleFollowing: function(user) {
user.set('isSaving', true);
Ember.run.later(function() {
user.toggleProperty('you_follow');
user.set('isSaving', false);
}, 1000);
}
});
App.UserView = Ember.View.extend({
isFollowingBinding: 'user.you_follow',
toggleFollowing: function() {
var user = this.get('user');
var controller = this.get('controller');
controller.toggleFollowing(user);
}
});
​