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.
Related
I am wondering the appropriate way to access a route model from a different non nested route controller.
If I have my routes set up like this: (this works however, not sure if its proper)
App.Router.map(function() {
this.route('admin');
this.route('page1');
}
And the Page 1 route has a model like this:
App.page1Model = {content:'Content of simple model'};
App.Page1Route = Ember.Route.extend({
model(){
return App.page1Model;
});
Then the admin controller wants to access the page1 route, I can only do it like this:
App.AdminController = Ember.Controller.extend({
page1Model:App.page1Model,
Now do stuff with page1Model.....
});
Ive tried to use Ember.inject.controller() however that only works for me when my routes are nested and I want to access Parent controller from child. Is there a way to use that syntax to get what I want, or is there a better way than what im doing?
Thanks
There's an inherent problem with what you're asking for: when the user is on the admin page, they're not on the page1 page, so there's no page1 context. Some questions you might want to ask:
what happens if the user goes to /admin having never gone to /page1?
what happens if the user goes to /page1 then /page2 then /admin?
I can think of two Ember-esque ways of doing what you want:
A Page1ModelService. Here, you create an Ember.Service that holds an instance of Page1Model. You inject the service into route:page1 and route:admin and let them each pull off the instance. Whether they can change which instance of the model is showing is up to you.
Return a Page1Model instance in the model hook for route:application. This route sits above both route:page1 and route:admin, so they can both look up the model as follows:
// route:application
model() {
return App.Page1Model.create();
}
// route:page1
model() {
return this.modelFor('application');
}
I was able to achieve my goal through using registers and injection. Can someone please take a look and let me know if this is 'proper' through Ember standards or if there is a better way ( #James A. Rosen :) )?
OH! If there is a better way to attach the model to the page1 route, please let me know. This worked though I am not sure if i like the .model after create().
JSBIN: http://jsbin.com/tikezoyube/1/edit?html,js,output
JS of that:
var App = Ember.Application.create();
var page1Model = {title:'Old Title'};
var page1ModelFactory = Ember.Object.extend({
model : page1Model
});
App.Router.map(function(){
this.route('page1');
this.route('admin');
});
App.register('model:page1', page1ModelFactory);
App.inject('controller:admin','page1Model','model:page1');
App.Page1Route = Ember.Route.extend({
model(){ return page1ModelFactory.create().model; }
});
App.AdminController = Ember.Controller.extend({
actions:{
updateTitle:function(){
console.log(this.get('page1Model').model.title);
this.get('page1Model').set('model.title','THE NEW TITLE!');
console.log(this.get('page1Model').model.title);
this.transitionToRoute('page1');
}
}
});
Thanks!
I'm trying to display a string, pulled from my model, that contains ember custom components. They don't seem to get compiled though -- see (1) and (2) in the output. If I replace the custom components with standard html elements and use the {{{-}}} syntax for binding, things look right (see (3) and (4) in the output), but this is not sufficient for the application I have in mind, though. How can I get ember to compile the custom components before displaying them?
app.js:
App = Ember.Application.create();
var g1 = "{{#my-bold}}Yo{{/my-bold}}, {{#my-italic}}dude{{/my-italic}}!";
var g2 = "<b>Yo</b>, <i>dude</i>!";
App.IndexRoute = Ember.Route.extend({
model: function() {
return {greeting1: g1, greeting2: g2}
}
});
App.MyBoldComponent = Ember.Component.extend({tagName: "span"});
App.MyItalicComponent = Ember.Component.extend({tagName: "span"});
index.html
<script type="text/x-handlebars">
{{outlet}}
</script>
<script type="text/x-handlebars" id="components/my-bold"><b>{{yield}}</b></script>
<script type="text/x-handlebars" id="components/my-italic"><i>{{yield}}</i></script>
<script type="text/x-handlebars" id="index">
<ol>
<li>{{model.greeting1}}</li>
<li>{{{model.greeting1}}}</li>
<li>{{{model.greeting2}}}</li>
<li>{{#my-bold}}Yo,{{/my-bold}} {{#my-italic}}dude!{{/my-italic}}</li>
</ol>
</script>
output:
{{#my-bold}}Yo{{/my-bold}}, {{#my-italic}}dude{{/my-italic}}!
{{#my-bold}}Yo{{/my-bold}}, {{#my-italic}}dude{{/my-italic}}!
Yo, dude!
Yo, dude!
From the Ember.js issue tracker https://github.com/emberjs/ember.js/issues/11649 on dynamically inserting components:
This isn't something we support, I also suspect we won't as it would require all of the htmlbars compiler client side and would likely be pretty slow. If you wish to add components dynamically. The component helper may be your best bet.
On that issue they are proposing to use the {{component}} helper, but that doesn't work with your code since you actually want to produce two component.
They are also talking about the RFC for contextual components: https://github.com/emberjs/rfcs/pull/64#issuecomment-111761176 which depending on their implementation would work similar to what you're doing. So what can you do?
As far as I can tell it's simply not possible to do with 1.13 (I tried quite a number of things) because rerender doesn't work, but they are going to fix it. If you downgrade to 1.12 you can do:
App.RenderTemplateComponent = Ember.Component.extend({
layout: function(){
return Ember.Handlebars.compile(this.get('templateString'));
}.property('templateString')
});
Then in your template something like:
{{render-template templateString="test {{x-foo}} {{x-foo}}"}}
Take a look at this JSFiddle:
http://emberjs.jsbin.com/jazayiyufi/1/edit?html,css,js,output
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
Cheers! I have two controllers and I want to have access to takenSeatsNumbers from TravelClient.TourController.
TravelClient.TourController = Ember.ObjectController.extend({
needs: ['tour.seats']
});
TravelClient.TourSeatsController = Ember.ObjectController.extend({
takenSeatsNumbers: []
});
Do I use needs in the right way? And how to get takenSeatsNumbers from tour template?
As mentioned above, this is a current bug and so the above will seemingly work, but won't work in reality, as the jsFiddle demonstrates. The solution for the moment is to pass in the instance of the controller via this.controllerFor in the route. It's far from ideal, but it will suffice for now.
It occurs because Ember refers to controllers using the dot.notation, whereas if you use camelCase or *under_scores* then it will create you different instances.
The solution is to inject the controller from your route, like so:
TravelClient.IndexRoute = Ember.Route.extend({
setupController: function(controller) {
var tourSeatsController = this.controllerFor('tour.seats');
tourSeatsController.set('takenSeatsNumbers', [1,2,3]);
controller.set('tourSeatsController', tourSeatsController);
}
});
And then you can use it in your view as: {{tourSeatsController.takenSeatsNumbers.length}}.
This code is based on ember-pre4. The proxying of properties from another controller seems to be a pattern this way and could likely be generalized with the help of a mixin:
TravelClient.TourController = Ember.ObjectController.extend({
needs: ['tourSeats'],
someMethod : function(){
var tourSeatsController = this.get("controllers.tourSeats");
// do something with it
},
takenSeatsNumbers : function(){ //proxy the property
return this.get("controllers.tourSeats.takenSeatsNumbers")
}.property("controllers.tourSeats.takenSeatsNumbers")
});
TravelClient.TourSeatsController = Ember.ObjectController.extend({
takenSeatsNumbers: []
});
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).