accessing the model from the template - ember.js

Playing around with ember, I found that sometimes the model is stored on the controller's content property, sometimes the model is directly available on the controller as well. I do not understand however, when this is the case.
Let me explain it by an example which I found when assembling my ember MVC.
Setup A - The start
I defined a custom Member object, corresponding MemberRoute, MemberView classes and a template with the name member.
The Member object had some attributes such as id, nickname, etc.
NOTE: no controller of the form MemberController was defined, thus by ember's convention, it provides the controller on its own.
Setup B - The customization
Same as setup A, but now there is a MemberController defined that contains some action methods that are triggered from within the template.
The strange behaviour (resp. what I do not completely understand)
in setup A, I can refer to the Member's attributes directly with {{id}} or {{nickname}}.
in setup B, I have to use {{content.id}} or {{content.nickname}}
As documented in ember's documentation, MemberView does
setupController : function(controller, member) {
controller.set('content', member);
},
So, could somebody help me to understand why the difference and where the difference is? Currently, my guess would be either
that the context of the template is different (possibly there is a code piece missing in the setup of the controller?)
or
the default controller that is provided by ember automatically, has some additional magic that is not directly avaiable for customized controllers.
Any help to understand this is highly appreciated. It already took my quite a while to come as far as this. I first thought it could be the modularization introduced by the project setup with requireJS (well, I still think that could have a influence). Ember is v1.0pre4.
Thanks in advance!
Patrick

So, could somebody help me to understand why the difference and where the difference is? Currently, my guess would be either
that the context of the template is different (possibly there is a code piece missing in the setup of the controller?)
or
the default controller that is provided by ember automatically, has some additional magic that is not directly avaiable for customized controllers.
It's hard to say for sure without seeing your code, but my best guess is that your MemberController extends Ember.Controller. The default provided by ember (in this scenario) would have been an Ember.ObjectController. If that's what you want, change your MemberController definition to:
App.MemberController = Ember.ObjectController.extend({
myProperty: 'value'
});
An objectController acts as a proxy to it's content property, typically that is an ember model. So if things are wired up correctly you should never need to access a model via the 'content` property. If you ever see something like:
{{content.id}} or {{content.nickname}}
it's a sign that you should change to an ObjectController. See EMBER GUIDES: REPRESENTING A SINGLE MODEL! for a more detailed explanation.

an ObjectController acts as proxy to the object set to the controller's content. When no controller is defined, Ember will create a controller for you and set its content by default to whatever object is returned by the model() function, if defined, in the route. The behaviour should be the same whether you define your own controller or let Ember define one for you
The default context in the template is the controller itself i.e. this = an instance of your controller or the generated one. When you try to access nickname in that context, Ember will first try to resolve it against the controller itself and if nothing is found, it resolves it against its content, i.e the object if you already manually set it to the controller's content.
Finally, there is no default implementation of the model() function in the Route except when you're using dynamic urls, say /foo/id that resolves against /foo/:id, Ember uses the id provided to load a Foo object with the id provided, thus providing a default implementation to the model() function. At the end it boils down to the same mechanism, only automated for your convenience.
I suggest you listen to this for more insights on how things are automated for you by Ember. But when it comes to the content being displayed, there is no magic you have to manually wire the content of the controller.

Related

Ember - Extending {{component}} helper

What I want to do is to pass a string to {{component}} helper, but I want more control over what string it gets passed in at runtime.
e.g.
// getComponentToRender will get the current UI theme in the app,
// determine whether it's running on the mobile mode, then fetches the
// appropriate name of a component to render based on the type of the
// component. Just an example.
{{component getComponentToRender(ComponentType.Button)}}
I investigated this, it turns out this isn't possible (Otherwise, please correct me). Of course, there's computed property, but it doesn't take an argument.
Next, I looked into extending the helper. This stackoverflow question shows you can extend one, but I couldn't locate where the component helper is located in Ember. There doesn't seem to be any questions/documentation regarding extending existing helpers, either. This is what I would do with this approach.
// I can't find where the {{component}} helper is located.
import Component from './???'
export default Component.extend({
compute(componentType, hash) {
let componentName = getComponentToRender(componentType);
this._super(componentName, hash)
}
})
What did I miss? It'd be great if someone can put me into the right direction.
The only way of calling a function from a template is writing an helper. Actually your reference StackOverflow question is also mentioning about helpers. Writing an helper will solve your problem.
You don't need to extend the component helper. You need to compose it. As following:
{{component (getComponentToRender ComponentType.Button)}}
Here is the working twiddle.

how to pass the Ember dynamic property from beforeModel of route to template handlebar

I want to pass a dynamic variable which is achieved from beforeModel of a route js to corresponding template's handlebar. I know that model data can be passed through model hook. However this is not model data. Also, there is no controller involved since i don't think pass variable from route to controller then template shouldn't be the easiest way.
thanks.
From ember guides beforeModel hook is appropriate for cases,
1) A decision can be made to redirect elsewhere without needing to resolve the model first.
2) Any async operations need to occur first before the model is attempted to be resolved.
so in your case, I will encourage you to do that stuff in setupController hook. if you still want to pass data from beforeModel to template then suggested solution would be, set result in route and get those values and set it in controller through setupController hook. Note: You will get default controller for every route.
This is some route data that you need in your template, that sounds like model data to me!
Also, there is always a controller involved, as one is created by the framework to be the context of the template. What this means is that the model() hook actually sets the model property of the controller, and any {{property}} you look up in your template is looked up in the related controller.
Pass the data along with the rest of the data in the model() hook.

Ember 2.0 Controllers or Just Components...?

I have read that Ember2 is attempting to remove controllers. I was even linked to this RFC https://github.com/ef4/rfcs/blob/routeable-components/active/0000-routeable-components.md. However, I have been following the tutorial, and they insist on making a Controller. Do we still need to make Controllers or is this out of date?
Controllers are still needed (and thus haven't been deprecated) for two reasons: query parameters, and because components aren't routable yet. You can follow the tutorial's use of controllers without it causing you too much grief later on.
However, if you want to pull ahead of the tutorial, you can use components instead, barring the two caveats above. There is no way around using controllers for query parameters, but you can avoid the lack of routable components using this simple hack:
Let's say you're creating a Route called Dashboard. The tutorial will tell you to create corresponding Controller and Template as well. Go ahead and do that, but delete the Controller. Create a component called dashboard-main, move the logic from the Controller to the component.js file and the Template to the component's Template. Then, in the Dashboard Template, just refer to the component:
{{dashboard-main items=model foo=foo bar=bar ...}}
Depending on what you're doing in the Route, you still may need the setupController() method (that's still the only way you can move values other than the model from the route to the template so that they can be passed to the component), and of course your controller/component implementation may have other minor changes, but that's the basic gist of it.
To be most ready for when controllers are deprecated, you should avoid them by using components instead.

Component to be notified on route change in EmberJS 2

My task is to develop a menu component for an Ember 2 app. This is going to be a complex component whose visual representation changes as the user goes through routes. For instance it should disable particular menu and all its items when on "/index" route but enable it and some of its items when on "/details" and so on.
So, I've got a component that is passed a singleton model (stored in a Service currently, btw, is it a right place to store globally available singleton models in Ember?). It displays the stuff well but it does not respect the current route nor catches the route changes as user goes through the app. How can I achieve it?
Summing it up:
The component needs to get current route somehow to be able to display its initial state, for instance the user bookmarked the "/details" page and visited it.
The component has to deal with route changes somehow.
Is a Service a good place to hold a singleton model (which could potentially be fetched from server).
Can you provide your thoughts on how to tackle the three above?
SOLVED: Ok, here's how it is done, thanks to #Bek's suggestions:
import Ember from "ember";
export default Ember.Component.extend({
router: Ember.inject.service("-routing"),
didInsertElement: function() {
let r = this.get("router");
console.log("Initial route", r.get("currentRouteName"));
r.addObserver("currentRouteName", this, "currentRouteNameChanged");
},
"currentRouteNameChanged": function(router, propertyName) {
console.log(router.get("currentRouteName"));
}
});
MORE QUESTIONS :) - I had to surround the currentRouteNameChanged function name with quotes (to make it a string) otherwise it was not called. I assume I miss something very basic and obvious here?
One more issue is the funky service name -routing - #Bek, any hints on how could I figure it out myself, is there a list of injectable stuff I could look up information in? It is not yet in Ember documentation I assume but where in the source code of it to check it out? How stable -routing name in general, would it become *routing or something in final version?
Answer to 1 and 2:
In latest versions of ember 2.x (in 2.2 at least) router is available as service so you can inject it to component router: Ember.inject.service('-routing') and observe changes on currentRouteName, but it is currently private service so should be used with caution as it might change (might be renamed to routing), there is also rfc https://github.com/emberjs/rfcs/pull/38 which proposes routable components which will be part of ember in the future.
Anser to 3:
Services usually stateless, but there can be exceptions and services made to share global logic/objects so it is not a bad idea

ember - recommended way to fetch a collection of objects to use as menu options without them being considered part of the model

I have a model Foo which has a field barId which has a belongsTo relationship to a model named Bar. I want to have a menu of Bars to set the barId value in my "new" Foo template.
I've already created a select component which displays things properly.
Where is the appropriate place for me to do a JSON fetch of the Bar collection? I'm assuming it should be stored in the controller as that decorates the model and makes extra context available to the template.
Doing an RSVP.hash() in the router's model() doesn't seem appropriate to me, as it would consider the Bar collection part part of the Foo model. Should I be using the router's afterModel()/setupController(), the controller's init() or set this up somewhere else?
Sorry I don't have a jsFiddle or anything, I don't know how to set one up now that my code requires babel.
Any help would be appreciated.
it turned out to be a non-issue as using Ember.RSVP.hash() in my router's model() without using setupController() as it defaults to model.foo, model.bar, etc, which you can access in controllers/templates/etc.
What I was originally worried about was that the other collections/objects I fetched from the store would be stored along with my 'model' when I did a
model.save(), but instead I'd be using model.foo.save(),