Instantiate a controller class using the {{view}} helper? - ember.js

While connectOutlet("basename") automatically creates an instance of BasenameController, I was wondering if there's a way to do the same using the {{view}}-helper.
I have tried several things I've seen in examples, but non of them seem to work:
{{view controllerBinding=App.BasenameController}}
{{view controllerBinding=App.basenameController}}
{{view controllerBinding="App.BasenameController"}}
{{view controllerBinding="App.basenameController"}}
I have also tried to do the same using controller instead of controllerBinding, unfortunately without success, and I was also unable to find out where exactly the difference is between the two of them.
Does anybody know how to achieve my goal?

You probably want to use an outlet. The connectOutlet/outlet functions are meant for rending other controller/view pairs.
Lets say we have a person view, but inside that view we want to have another controller/view pair. For this, we need to use a named outlet, our template would look like this.
Person View!
{{name}} = the person's name!
{{controller}} = PersonController!
{{outlet other}} = our outlet
Then inside the router when you want to attach another controller/view to that outlet you can simple use connectOutlet on the personController.
router.get('personController').connectOutlet('other', 'other');
That will wire OtherController and OtherView together and display them in your template. The first param is the outlet name, the 2nd is the controller/view.
This allows you to easily swap different controllers and views onto that outlet. For example, using a different connectOutlet api, we could
router.get('personController').connectOutlet({
outletName: 'other',
controller: router.get('carsController'),
viewClass: App.CarsView
});
...
Btw, to answer you original question. You can get access to other controllers from your view by doing this: {{view controllerBinding="controller.target.otherController"}}. Every controller will have a target property that points back to the router. However, I do not recommend using this code. It's brittle, hard to test, hard to debug, and will come back and bite you in the future.

Related

Ember.js: Passing a template's current context object to an action

I'm working on an Ember.js app similar to this:
http://jsfiddle.net/rzb2m/
Now this does pretty much what I want except for one crucial problem. The setFavorite action at the bottom has a hard-coded 0. This means that if you change Carol's favorite activity, it actually changes Alice's favorite activity. I haven't been able to figure out how to structure the application so that it has the correct behavior. How do I pass the correct object to be changed (this.content.people[i] in this case) to the setFavorite function?
Note: My real application is actually a bit more complicated than this because I have two separate lists of people on the page. So the solution shouldn't just involve passing an index into setFavorite because that won't scale to the more complex situation with multiple lists. This is also why the person template looks unused. In the real app, there is complexity that justifies it.
You are relying on implicit binding resolution from controllers to views. But this breaks down because when you separate things out into partials you have no means to lookup the parent object in the #each iteration.
While #each controller works, you are better off with #each person in controller. Then you can access the properties from the view in the partial view, and pass it forward as needed.
Change the main #each in index template to,
{{#each person in people}}
<li>{{view App.PersonView contentBinding=person}}</li>
{{/each}}
Then in person template,
{{view App.PersonDetailsView contentBinding=view.content}}
And in personDetails template.
{{#each fave in view.content.getActivities}}
All this gives you the ability to pass the person to the setFavorite method,
<a {{action setFavorite view.content fave}} href="#">
So the setFavorite switches to a simple assignment on that person.
setFavorite: function(person, fave) {
person.set('favoriteActivity', fave.name);
},
Here's the updated jsfiddle.

Ember JS - Reference view inside linkTo

I have an Ember JS confusion. First of all I have to admit I'm a bit of an Ember newbie so apologies if this is obvious.
I have the following template:
<script type="text/x-handlebars" data-template-name="threadListItem">
{{#linkTo show thread.receiverLogin}}
<h4>{{thread.receiver.login}}</h4>
<span>{{view.prettyTime}}</span>
{{/linkTo}}
</script>
There's basically a linkTo helper with some elements inside of it.
The weird thing is the {{view.prettyTime}} doesn't display anything when it's inside the linkTo.
However, if I move the span outside the linkTo it finds the view variable ok and it works.
Is there anyway to get access to the view object inside the linkTo?
Christopher is correct that you can access the prettyTime property with:
{{view.parentView.prettyTime}}
Since you're new to ember, it's maybe worth mentioning that in most cases it is best to avoid accessing properties of the view. Instead, consider adding a prettyTime property to your controller. The controller will be the default context for sub-views like the one created by the linkTo helper, so you'll be able to access it with just
{{prettyTime}}
linkTo creates its own view, so the view you want to get access to is the parentView of view.
{{view.parentView.prettyTime}}

Different rendering techniques in emberjs handlebars template

I've been reading a lot on emberjs lately but something isn't really clear to me: I have a feeling that there are different ways of rendering a template. Can someone explain the differences between these:
{{render}}
{{partial}}
{{template}}
{{outlet}}
I'm using pre4, so if some of these keywords are obsolete, please notify.
You can search the Ember.JS source for all of these by searching for: Ember.Handlebars.registerHelper('?'. For example, to find the part where template is defined, search for: Ember.Handlebars.registerHelper('template'
{{template}}
Is similar to the {{partial}}, but looks for templates that you define in the Ember.TEMPLATES hash. From the source code we can see an example: Ember.TEMPLATES["my_cool_template"] = Ember.Handlebars.compile('<b>{{user}}</b>'); and then we can render it that way.
I heard a whisper that {{template}} is #deprecated, but I can't find where I found that information at the moment. However, it's worth mentioning that I've never found myself using this one. Instead I prefer {{partial}}.
Edit: It appears as though it isn't #deprecated as of
3df5ddfd4f. My mistake!
{{partial}}
This is different to the {{render}} approach in that the controller and view are inherited from the context that called it. For example, if you're in the UserRoute, and you load in a partial in your user template, then the UserView and UserController will both be passed to your partial, so they can access exactly the same information as its current parent.
Partial names, when defined, start with an underscore. For instance, a Profile partial will have the data-template-name of: data-template-name="_profile" but is inserted into your view as {{partial "profile"}}.
{{outlet}}
You'll probably find yourself using this one a lot. It's predominantly used in cases where the outlet changes frequently, based on user interactions. By transitioning to (this.transitionTo/{{#linkTo}}) another page, Ember inserts the view into the {{outlet}} and attaches the relevant controller and view.
As an example, if you're transitioning into /#/pets then, by default, Ember will load the PetsView into the {{outlet}}, and attach the PetsController, all of this after initialising the PetsRoute to take instructions before initialising the view and finding the controller.
{{render}}
This is a mixture of an {{outlet}} and a {{partial}}. It's used for static pages that don't switch out for other pages (as an outlet does), but it doesn't inherit the controller and view (as a partial does).
It's better with an example. Let's say you've got a navigation. Usually you'll only have one navigation, and it won't change for another one, but you want the navigation to have its own controller and view, and not to be inherited from the context (probably ApplicationRoute). Therefore when you insert the navigation ({{render "navigation"}}), Ember will attach App.NavigationController and App.NavigationView.
Summary
template: Consults a global hash and inserts the view when it finds it (possibly soon to be #deprecated);
partial: Used to split up complicated views, and inherits the controller/view from the parent (if you're in the UserController, then the partial will also have access to this, and its associated view).
outlet: Most widely used, and allows you to quickly switch out pages for other pages. Relevant controller/view attached.
render: Similar to an outlet, but is used for pages that are persistent across the entire application. Assumes the relevant controller/view, and doesn't inherit them.
Did I explain them well?
Just to clarify:
Partial: Inherited controller, inherited view, non-switchable;
Outlet: Relevant controller, relevant view, switchable;
Render: Relevant controller, relevant view, non-switchable;
The guide also provides some useful information here! Below is a quick summary:
I wanted to post another answer here that really helped me to clarify when to use the various template techniques -
Route
Using a route is when you need a full-blown route. A 'route' has a unique URL and consists of generated or user defined classes of the following type -
Route (Ember.Route)
Controller (Ember.Controller || Ember.ArrayController || Ember.ObjectController)
View (Ember.View)
Template (Handlebars template)
{{render}}
Use the {{render}} helper when you need a view but still need to provide some functionality with a controller. {{render}} does not have a unique URL and consists of the following -
Controller (Ember.Controller || Ember.ArrayController || Ember.ObjectController)
View (Ember.View)
Template (Handlebars template)
{{component}}
Use the {{component}} helper when you are building a commonly re-used template which exists independent of the context it is rendered within. An decent example may be if you were building a retail website and wanted to have a product view agnostic of where it is rendered. {{component}} does not have a unique URL nor a controller but instead has a component class and consists of the following -
Component (Ember.Component)
Template (Handlebars template)
{{partial}}
Use the {{partial}} helper when you are simply re-using some mark-up. {{partial}} does not have a unique URL nor any special backing like a component and consists of the following -
Template (Handlebars template)

Nested Views in Ember

I've got the need for a recursive nested set of views in Ember. Basically think of it as a file structure with folders.
I thought I could do it with render, but that didn't work. Inside structures/show view:
{{#each child in structures}}
{{ render 'structures/show' child }}
{{/each}}
I get the error:
Uncaught Error: assertion failed: This view is already rendered
I read in another SO Question about using the {{view}} helper:
{{#each child in structure.structures}}
{{ view App.StructuresShowView structureBinding='child'}}<br>
{{/each}}
But this doesn't render properly. Possibly because the example is too old?
How do I properly render the same view (and controller?) with a different object in order to build a nested/recursive view? Is there a better way than the object directions?
Thanks.
Update. Looks like itemControllerClass might be what I'm looking for? Still trying to figure it out.
I think you cannot call {{render}} multiple times on a single route. If you need to create something many times, you probably need to use {{control}}, which is the same as {{render}} with a difference that {{render}} has one controller (singelton) and {{control}} has a new controller every time it's called.
Here's an open issue related to your question.

How can we load multiple instances into a template?

We're trying to put together a portal, where a layout can have any number of core widgets in any sequence in the main layout.
To simulate this, we've got a number of outlets:
<h1>{{title}}</h1>
{{outlet pos1}}
{{outlet pos2}}
{{outlet pos3}}
{{outlet pos4}}
{{outlet pos5}}
{{outlet pos6}}
{{outlet pos7}}
{{outlet pos8}}
{{outlet pos9}}
{{outlet pos10}}
And in the router, we're attempting to load them in one by one:
connectOutlets: function(router, group) {
router.get('applicationController').connectOutlet('group', group);
router.get('groupController').connectOutlet('pos9', 'toDo', App.ToDo.find(41));
router.get('groupController').connectOutlet('pos3', 'toDo', App.ToDo.find(15));
However, when there are more than one, the final context is used. So in this example, we get two instances of the toDo object, both of which are for id #15.
Am I approaching this in the right way and is it possible to do this programatically, rather than having a fixed layout of outlets?
Thanks,
Dan
Edit: My answer is based on the assumption that this complex solution is really needed in your case. Based on your simple example one could also say, that you could use an ArrayController for all your ToDo items. But here is my try on the answer to the complex problem:
the problem are the following 2 lines:
router.get('groupController').connectOutlet('pos9', 'toDo', App.ToDo.find(41));
router.get('groupController').connectOutlet('pos3', 'toDo', App.ToDo.find(15));
What you basically do there is:
Connect the outlet with name pos9 with the Controller named 'todo'. Set the content of this controller to ToDo with Id 41.
Connect the outlet with name pos3 with the Controller named 'todo'. Set the content of this controller to ToDo with Id 15 (so you are overriding the content of the same controller).
The result is that you end up with both outlets connected to the same instance of a controller. And you the same ToDos since you have set the content property of this single instance twice. The core problem is from my point of view: EmberJS uses single instances of controllers by default.
So my solution approach would be to instantiate a new Controller for each outlet you have. Unfortunately this also requires modification to the lookup of the View. As you likely know, Controller and View are matched by name. So roughly the algorithm would be in pseudocode:
Create new instance of Controller, e.g.: var newController = App.ToDoController.create();
Inject this controller into the router with the appropriate name, e.g. router.set('todoControllerForPos9', newController);
Based on this name, you must enable Ember to find the matching view, e.g. App.set('TodoControllerForPos9View', App.ToDoView);
Finally call connectOutlet on the router, e.g.: router.get('groupController').connectOutlet('pos9', 'todoControllerForPos9', App.ToDo.find(41));
As you might guess, i ran into this problem myself. I did ask this question before and this is the solution, i came up with. I think, this is a missing feature i ember. I call it dynamic outlet names. See my original question here: How to implement a list of multiple elements, where an element can be expanded by click? (Dynamic Outlet Names?)
Have a look at the Update section and the fiddle provided there and you will recognize my pseudo code provided here.
It would be still great, if someone could have a look at my solution, because it is still hacky at the moment, but seems valueable to me. Hope this will gain some attention now with this big answer :-)