why view. is needed in handlebars - ember.js

I can't understand something in Ember.js, my contains a property, but in order to display it i have to do {{view.property}}, why can't I simple use {{property}} ?
in the following example only {{view.test}} is displayed.
shouldn't the view be the default scope ?
index.html:
<script type="text/x-handlebars" data-template-name="places">
{{test}} {{view.test}}
</script>
app.js:
App.PlacesController = Ember.ArrayController.extend({
});
App.PlacesView = Ember.View.extend({
templateName: 'places',
test: 'test'
});

As of Ember 1.0pre, the context for handlebars helpers in your template has been changed to be the controller.
Before that, it was the view. The view keyword is available to access properties from the view.
Try adding a test property to your controller and see what happens.

Related

Custom view helper in Ember.js, "You can't use appendChild outside of the rendering process"

I want to bind my custom view's class to a controller property.
[javascript]
App.IndexController = Ember.Controller.extend({
headerClass: "a"
});
App.TestHeaderView = Ember.View.extend({
classNames: ["test-header"],
classNameBindings: ["headerClass"],
headerClass: null,
templateName: "views/test-header"
});
[templates]
<script type="text/x-handlebars" data-template-name="index">
{{view App.TestHeaderView text="view helper" headerClass=controller.headerClass }}
<hr />
{{input value=headerClass}}
</script>
<script type="text/x-handlebars" data-template-name="views/test-header">
<small>{{view.text}}</small>
</script>
The result is predictable: everything works. I can enter the class name in the text box and see it reflected in the view.
So now I want to extend this and add my own helper that wraps the {{view}} call.
[javascript]
Ember.Handlebars.helper("test-header", function (options) {
return Ember.Handlebars.helpers.view.call(this, App.TestHeaderView, options);
});
[templates]
<script type="text/x-handlebars" data-template-name="index">
{{test-header text="custom helper" headerClass=controller.headerClass}}
</script>
Nothing special right? Except, I keep getting this:
Uncaught Error: You can't use appendChild outside of the rendering process
For full working jsbin, click here.
It seems this should work. I'm just wrapping the ember's view helper pretty much exactly. What am I missing?
I figured it out.
The trick is in the contexts array in the options hash.
When you call {{view App.MyView}} from handlebars, Ember's view helper gets in its options.contexts array the "context" in which it should search for "App.MyView" property - usually the current controller. In this case, "App.MyView" will be resolved regardless of the context, but I guess Ember keeps the context around and uses it to resolve bound properties.
When I called:
{{test-header text="custom helper" headerClass=controller.headerClass}}
there was no first argument from which to draw the context. Therefore, when I passed the call along to the view helper:
return Ember.Handlebars.helpers.view.call(this, App.TestHeaderView, options);
... there was no context passed along in the options.contexts array.
The way I fixed this is:
Ember.Handlebars.helper("test-header", function (options) {
options.contexts = [this].concat(options.contexts);
return Ember.Handlebars.helpers.view.call(this, App.TestHeaderView, options);
});
IMO Ember should do a better job here. They should either figure out a context from reference, or throw an error (a preferred option).

templateName as computed property

According to the documentation it is possible to specify a template for a view with templateName:
App.ShowEntryView = Ember.View.extend({
templateName: 'my-template',
});
And we can use it like:
<script type="text/x-handlebars">
<div>
{{view App.ShowEntryView}}
</div>
</script>
Could we bind the templateName to another property? Something like:
{{view App.ShowEntryView templateNameBinding="myComputedTemplateName"}}
So that in the controller we have:
myComputedTemplateName: function() {
return "this-is-my-template-name";
}.property()
The reason why I want to do this is that I have several models which I am displaying as an heterogeneous table. I want that, whenever the user selects one of the entries in the table, a detailed view is shown, using the right template according to the underlying model.
I guess you could do this:
{{view App.ShowEntryView templateName=myComputedTemplateName}}
JS Bin example

Evaluating controller property everytime the template is rendered in Ember

I have a template called sample and I am changing the property show in the controller based on a button press. Now I want the controller to reset the property every time the template is rendered. Currently even if I go to 'next' template and come back, the property show remains true if the button on sample was pressed. I want to change this behaviour and the property show should be false by default. I know I can do this by defining a view and using the didInsertElement hook but is that the only way to do this?? Ember.js website says that Views in Ember.js are typically only created for the following reasons:
When you need sophisticated handling of user events
When you want to create a re-usable component
and I am doing none of the above. Here is some sample code:
<script type="text/x-handlebars" data-template-name="sample">
{{#if show}}
Showing stuff
{{/if}}
<button {{action changeShow}}>Change</button>
{{#link-to 'next'}} Next {{/link-to}}
</script>
<script type="text/x-handlebars" data-template-name="next">
Hello
{{#link-to 'sample'}}Back{{/link-to}}
</script>
App.SampleController=Ember.Controllers.Extend{(
show:false,
actions:{
changeShow:function(){
this.controllerFor('sample').set('show',true);
}
}
)};
you can use didTransition action which will trigger automatically once the transition happened. didTransition action
App.SampleController=Ember.Controllers.Extend{(
show:false,
actions:{
didTransition:function(){
this.controllerFor('sample').set('show',false);
},
changeShow:function(){
this.controllerFor('sample').set('show',true);
}
}
)};
You can use the renderTemplate hook for the route you're doing this in, and change the controller variable in there.
http://emberjs.com/api/classes/Ember.Route.html#method_renderTemplate
I'd do something like this:
App.PostsRoute = Ember.Route.extend({
renderTemplate: function(controller, model) {
var favController = this.controllerFor('favoritePost');
favController.set("toggle", false)
this._super()
}
});

Dynamic value in application template

I tried to implement user name displaying after log in. It displays in top menu. But top menu is getting displayed before log in, so it user name is getting cached.
I tried many approaches, and using volatile() is seems the best option, but it doesn't work. In this simple example currentTime calculates only once:
<script type="text/x-handlebars" data-template-name="application">
{{currentTime}}
</script>
App.ApplicationController = Ember.Controller.extend({
currentTime: function() {
console.log('computing value');
var time = new Date();
return time;
}.property().volatile()
});
Ember version 1.3
P.S. I prepared the gist to illustrate this issue: http://jsbin.com/OPUSoTaF/1
Actually, I can't find ANY way do display dynamic value in Ember's application template. Tried to display value from another controller using {{render}} helper, value still gets cached.
It seems that I just need to update value on ApplicationController from some other controller, and to do it in a proper way. Like this:
App.LoginController = Ember.Controller.extend({
needs: 'application',
setTime: function() {
this.get('controllers.application').set('currentTime', new Date());
}
});
The application to illustrate: http://jsbin.com/OPUSoTaF/4/edit
You can change ember properties and thus views using Handlebars {{action 'actionName'}} helper. You can add action helper to almost any UI element in your handlebars template an it is usually triggered on click. When triggered it calls actionName method on the controller.
Example:
Handlebars template:
<script type="text/x-handlebars" data-template-name="application">
<button {{action 'login'}}>Login</button>
{{loginTime}}
</script>
Controller:
App.ApplicationController = Ember.Controller.extend({
loginTime: 'User not logged in yet',
actions: {
login: function() {
// ... Do some login stuff ...
this.set('loginTime', new Date());
}
}
});
Working jsbin example is here: http://jsbin.com/udUyOXaL/1/edit

Accessing Ember controller attributes from a controller

I have the following controller:
App.ShowController = Ember.Controller.expend({
buttonTitle: 'Create'
});
And the following template show.handlebars
<a href='#'>{{buttonTitle}}</a>
but the text is not rendering. Is there a special call to access the attribute?
Normally, when a view is displayed (via the Router), the context of the view is automatically set to the controller, so there should be nothing to do special.
Here is an example, where the MyApp.IndexController is automatically set as the context of the IndexView (and its template is the index template):
MyApp = Ember.Application.create({});
MyApp.Router = Ember.Router.extend();
MyApp.Router.map(function(match) {
match('/').to('index');
});
MyApp.IndexController = Ember.Controller.extend({
buttonTitle: "create"
});
The template:
<script type="text/x-handlebars" data-template-name="index">
{{buttonTitle}}
</script>
And you could try it on this JSFiddle.
N.B.: I'm using Ember v1.0.0-pre.2-239 here. There are some changes to do for upgrading this example to master