As you'll be able to tell from my question, I'm slowly learning EmberJS. I've read the great guide on routes and I felt ready to take on the world but then...
In my example, I thought the {{somethingView}} would be rendered and not the controller property {{somethingCtrl}}. Is this the correct behaviour? If so how would you render a property from the Ember.View?
The JS
window.App = Ember.Application.create({
ready: function() {
this.initialize();
}
});
window.App.Router = Ember.Router.extend({
root: Ember.Route.extend({
index: Ember.Route.extend({
route: '/',
connectOutlets: function(router) {
var controller = router.get('applicationController');
controller.connectOutlet('garments');
}
})
})
})
window.App.ApplicationView = Ember.View.extend({
templateName: 'application',
});
window.App.ApplicationController = Ember.Controller.extend();
window.App.GarmentsController = Ember.Controller.extend({
somethingCtrl: "Something in the controller"
});
window.App.GarmentsView = Ember.View.extend({
templateName: 'garments',
somethingView: "Something in the view"
});
The DOM stuff
<script type="text/x-handlebars" data-template-name="application">
<h1>Hi Ember</h1>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="garments">
<h1>Garments</h1>
{{somethingView}}<br>
{{somethingCtrl}}
</script>
The Fiddle
This behaviour is correct. These are my understandings of these ember core concepts:
Model: These objects represent the date that is handled by your application. These are the business objects that form the domain model of your Application.
Controller: A Controller is responsible for providing access to your models. Controllers have the property content, where models should be injected (a single Object for Ember.Controller and an array of objects for Ember.ArrayController). The Controller passes this content to your View. The Controller is the default context for your view. Therefore the behaviour you describe is expected.
View: The View is just intended for displaying issues. I personally use it mainly to do jQuery animations.
But nonetheless it is possible to access the view instance in the template. You just have to use the variable with the name 'view' in your template. I updated your fiddle with a working example: http://jsfiddle.net/jPK8A/5/
<script type="text/x-handlebars" data-template-name="garments">
<h1>Garments</h1>
{{view.somethingView}}<br>
{{somethingCtrl}}
</script>
But to be clear: The most common case should be to access contents from your controller. It should be not often that you access variables of your view. You want to display date in your App and this date resides in models and should therefore be accessed through controllers. The most likely case might be, that you want to store labels in your view or something like that (labels that have to be computed).
Related
I'd like to know if there is a way to check if a controller exists using Ember, then associate it to a view?
I'm going through a list of element coming from an array, and depending of those element, I generate a view, and sometimes I need to associate a controller to this view. I'm using Ember AppKit.
export default Ember.CollectionView.extend({
init: function () {
this._super();
var self = this;
myList = [{name: 'element-1'}, {name: 'element-2'}];
myList.forEach(function (element) {
self.push(Ember.View.create({
templateName: 'path/to/template/'+element.name,
controller: 'path/to/controller/'+element.name //Associate the controller only if it exists, I don't know how to do that.
}));
});
}
});
Thanks
As mentioned in the comment of your question, the View knows its Controller so I'd go the other way and tell the Controller beforehand which other controllers are needed through its needs property which could then be aliased and observed by the view.
I was fiddling around with Ember and I came across something that confused me.
I have an ArrayController (Index) with an item controller (Post). For the init hook on the Post item controller, I have it send a debug line out to the console. This debug statement is being sent twice for each post, and I can't figure out why.
Code on JSBin:
http://emberjs.jsbin.com/momikuto/14/edit
HTML:
<script type="text/x-handlebars">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="index">
{{each controller itemViewClass="App.PostView"}}
</script>
<script type="text/x-handlebars" data-template-name="post">
POST {{id}}<br />
</script>
JavaScript:
App = Ember.Application.create();
App.IndexController = Ember.ArrayController.extend({
itemController: 'post',
templateName: 'index',
addPost: function (id) {
this.pushObject(App.PostController.create({ id: id }));
}
});
App.PostController = Ember.ObjectController.extend({
debug: function () {
console.log('init for post ' + this.get('id'));
}.on('init')
});
App.IndexView = Ember.View.extend({
didInsertElement: function () {
this.get('controller').addPost(1);
this.get('controller').addPost(2);
}
});
App.PostView = Ember.View.extend({
tagName: 'span',
templateName: 'post'
});
Output:
init for post 1
init for post 1
init for post 2
init for post 2
If I remove post 2 and only use post 1, I just get init for post 1 twice. If I add a post, I get all three of them twice. The number of Post objects in the ArrayControl does not seem to have anything to do with the problem.
I saw one post on the Ember Github, but it was closed after it was assumed to not be an Ember issue. Any ideas?
You're creating it twice. You have itemController set on your IndexController and you're actually creating the controller in the addPost function.
You shouldn't ever need to create a controller instance yourself, except for some strange edge cases. Ember will create the itemControler instance for you when you pushObject so you don't need to manually create.
I am building an emberjs application I want to have two different layout for example application template and another template ,I don't want all my views to be rendered in the application template ,like in rails when you can specify to have multiple layouts for different controllers
Its actually pretty easy, just specify which template to render:
App.ApplicationRoute = Ember.Route.extend({
renderTemplate: function() {
if (expression) {
this.render('application');
} else {
this.render('site');
}
}
});
Via http://emberjs.com/guides/routing/rendering-a-template/
these is the answer I got from emberjs discussion forum
Create two views for the two layouts with the "layoutName" property:
App.MainLayoutView = Ember.View.extend({ layoutName: 'layout/main', });
and:
App.SecondaryLayoutView = Ember.View.extend({
layoutName: 'layout/secondary',
});
Create two templates for the layout, called "layout/main" and "layout/secondary".
Make sure that your views extends those layout views. For example, imaging the following route config:
App.Router.map(function() {
this.resource('users', function() { this.route('new');
this.route('login'); });
});
If you want all the users route to be using MainLayout and login to be using the Secondary layout, create two views:
App.ProjectsView = App.MainLayoutView.extend();
and
App.LoginView = App.SecondaryLayoutView.extend();
There is no need to create a view for "projects/new" because it is a nested route of projects, hence inheriting the layout of Projects.
Hope it helps!
ContainerView.pushObject() does not automatically wire-up dynamically added views with a Container object.
The lack of an auto-wired container causes a render failure when a view renders a template that contains a handlebars render helper.
SIMPLE CASE THAT WORKS (KIND OF)
View:
App.DynamicView = Em.View.extend({
templateName: 'dynamic',
didInsertElement: function() {
var control = this.get('controller');
control.send( 'view_inserted', this.templateName, control._debugContainerKey);
control.send('callDynamicController');
}
});
Template:
<script type="text/x-handlebars" data-template-name="dynamic">
dynamic
</script>
Controller (only used when manually assigned):
App.DynamicController = Em.ObjectController.extend({
className: 'App.DynamicWithRenderController',
callDynamicController: function() {
console.log('DynamicController.callDynamicController()');
}
});
Index Controller:
App.IndexController = Em.ObjectController.extend({
view_inserted: function(aview, acontroller) {
console.log('view inserted!', aview, acontroller);
}
})
Instantiation code:
var acontainer = App.DynamicController.create({});
var aview = App.DynamicView.create({ controller: acontroller })
acontainerView.pushObject(aview);
These classes render & behave as expected, but if you interogate them, lack some of the Ember-wiring (e.g. no _debugContainerKey & container properties IIRC):
MORE ADVANCED CASE THAT BREAKS
If we introduce a handlebars template that uses rendering helpers, it breaks rendering. The dynamically added view currently lacks some properties the rendering helper assumes
<script type="text/x-handlebars" data-template-name="dynamic-with-render">
dynamic w/render:
{{render knob}}
</script>
and make knob look like this:
<script type="text/x-handlebars" data-template-name="knob">
{{render knob}}
</script>
The (failing) dynamic view instantiation code:
var acontainer = App.DynamicController.create({});
var aview = App.DynamicView.create({
controller: acontroller,
template:'dynamic-with-render' })
acontainerView.pushObject(aview);
CODE EXAMPLE
A fuller example with some notes can be seen here:
http://jsfiddle.net/AshCoolman/KyJ2U/6/embedded/result/
NOTE: My tests include a custom handlebars helper based of the control helper called controlWithVars
THE PROBLEM
It looks like I need to write something that does the Ember-wiring, in either:
the more native ContainerView (getting into the Ember guts), OR
a more de-coupled new render helper possibly inelegant)
I'm not sure how to proceed. It would be great if someone has already come up with an elegant solution, or at least could give me some helpful tips.
EDIT: So it looks like creating and assigning a container, which includes the views dependencies might be a solution. Thoughts anyone?
HELPFUL READING
https://github.com/emberjs/ember.js/issues/2108
What is the purpose of the Ember.Container
http://mcdowall.info/posts/ember-application-initializers/
The raison d'être of Ember.ContainerView is for dynamically adding and removing views, so I'm pretty confident you can do all the things you want to with it.
One thing I noticed in your examples is that you are creating your child views with View.create(attrs). It is important to use containerView.createChildView(viewClassName, attrs) to create views that get the container, parent view hierarchy, and more. See the implementation more details:
https://github.com/emberjs/ember.js/blob/master/packages/ember-views/lib/views/view.js#L2072
this is my first post in stackoverflow.
I'm new to ember.js and i've been studying ember.js.
I'm building a small app to learn ember.js now,but binding doesn't work somehow.
Please give me a help!!
#------------------------Controller------------------------
App.ApplicationController = Ember.Controller.extend();
App.monstersController = Ember.ArrayProxy.create({
content:[],
//some code to add model instances to content...
counter: function(){
var content = this.get('content');
return content.get('length');
}.property('length')
});
#------------------------View------------------------
App.StatsView = Ember.View.extend({
counterBinding : 'App.monstersController.counter',
#------------------------HTML------------------------
<script type="text/x-handlebars" data-template-name="application">
//some code here
{{#view App.StatsView}}Counter: {{counter}}{{/view}}
//I'm expecting the length of content array in App.monstersController above.
//some code here
</script>
If you want to access a value off of a view you need to access it via {{view.counter}}.
However, in your case you should probably be setting the controller on the view like:
App.StatsView = Ember.View.extend({
controllerBinding : 'App.monstersController'
})
Once you get the hang of that, I'd look into setting up a router to manage binding controllers and views together.