I am beginner with Ember.js and I guess there's a really simple answer to my problem so please bear with me.
I try to establish a two-way binding between a controller property and a
"value" property in a Ember.TextField that is in the controller's correspondant template. I already succeded in giving the property in the controller a specific initial value that is reflected in the value property of the Ember.TexField, like:
js code:
App = Ember.Application.create();
App.IndexController = Ember.ObjectController.extend({
aProperty: 1 });
template:
<script type:"text/x-handlebars" data-template-name="index">
{{view Ember.TextField valueBinding="aProperty"}}
</script>
Now the text field shows the value("1") and everything is fine.
If I now try to enter a new value into the textfield, say "2", and try to check the value of "aProperty" in the browser console with App.IndexController.get("aProperty") it doesn't work and throws an error and says that "get()" is not defined.
How could I retrieve the value of "aProperty" from the IndexController and check if it has updated the value to what I entered in the Ember.TextField?
Any hints, comments and advices are greatly appreciated!
Thank you!
Edit:
Both answers below accomplished what I was trying to do. I got it working too: I created a concrete instance of the App.IndexController after having extended it, like this:
App.IndexController = Ember.ObjectController.extend({
aProperty: 1 });
App.indexController = App.IndexController.create();
and could retrieve the value of aProperty from the console with App.cueController.get('aProperty'). However, all of the examples from Ember's documentation never create concrete instances of the controllers so I'm wondering if this is necessary, much less encouraged by Ember for a real-world app. Any ideas?
You should lookup the controller like this in your debugging console:
App.__container__.lookup('controller:index').get('aProperty')
http://jsbin.com/uxojek/9/edit
Since using the __container__ is only for debugging purposes, if you want to log the property change without using the private __container__ you could set an observer on the controller property watching for changes and log the new value to the debugger console.
Example:
App = Ember.Application.create({});
App.IndexController = Ember.ObjectController.extend({
aProperty: 1,
aPropertyDidChange: function() {
console.log(this.get('aProperty'));
}.observes('aProperty')
});
Working jsbin.
Hope it helps
Related
I have a container view in application level and I wanted to add some components to this container view via some action. The way I do this is :
1) I push some objects to the application controller
2) ContainerView is within the context of application controller, so I observe the length of the array holding the objects mentioned in step 1
3) and then I create the components and append them to the container view
One weird thing I noticed was initially the observer defined in the container view was not triggering, but then I added initialize hook where I log the controller, and then the observer started working for me. This tells me maybe I am not doing something right. Here is my container view:
App.MyContainerView = Ember.ContainerView.extend({
initialize: function(){
//IF YOU WERE TO COMMENT THIS CONSOLE.LOG THEN THE OBSERVER NEVER GETS HIT WHEN YOU CLICK ON ADD BUTTON. MAYBE CONTROLLER IS NOT SET UP AND IT NEEDS SOME TIME???
console.log(this.get('controller').toString());
}.on('init'),
appendMyComponent: function(){
console.log("inside the observer");
this.get('controller.myComponents').forEach(function(comp){
console.log(this.toString());
this.pushOject(App.MyTestComponent.create({}));
}, this);
}.observes('controller.myComponents.length')
});
Do I have to use named outlet and then render the container view within the outlet? Not sure what I am doing wrong here.
Here is a jsbin: http://emberjs.jsbin.com/xamoxese/2/
Your feedback is much appreciated. Thanks good ppl of stackoverflow.
USING :
Ember : 1.5.0
Handlebars : 1.3.0
jQuery : 1.10.2
Allright some more updates, initially the solution provided by #bmeyers was not working for me although it was working in jsbin. So in my real app mu application.hbs looked like this:
{{outlet navbar}}
{{outlet}}
{{outlet modal}}
{{view App.MyContainerView}}
but then I re-ordered it as :
{{view App.MyContainerView}}
{{outlet navbar}}
{{outlet}}
{{outlet modal}}
and then the solution worked. Now only if I could get rid of that console.log thing :)
Here is updated jsbin that I have added on top of #bmeyers solution http://emberjs.jsbin.com/xamoxese/10/
I have fixed this for you and shown an example here: http://emberjs.jsbin.com/xamoxese/3/edit?html,css,js,output
I didn't fix your issue where you are adding the same components over and over again and never clearing the childView array, but maybe that was your intention.
Basically you do not push to the container view, you need to push to the childViews property.
Hope this helped!
UPDATE FIXED
http://emberjs.jsbin.com/xamoxese/6/edit
Ok so looks like you just needed a reference outside your foreach function and that for some reason allowed using it the way it is documented
UPDATE 2:: Change the containerView code to....
App.MyContainerView = Ember.ContainerView.extend({
init: function() {
this._super();
this.appendMyComponent();
},
appendMyComponent: function(){
this.get('controller.myComponents').forEach(function(comp){
this.pushObject(App.MyTestComponent.create({}));
}, this);
}.observes('controller.myComponents.length')
});
This just feels a bit cleaner than creating an empty view that you will never use. Picky I know but it was bugging me.
Finally figured out how to get rid of that console.log thing. So I actually have 2 solutions for this:
1) pass the controller to the container view
{{view 'myContainer' controller=this}}
2) set an empty view to the ContainerView
App.MyContainerView = Ember.ContainerView.extend({
childViews: [Ember.View.create()],
appendMyComponent: function(){
this.get('controller.myComponents').forEach(function(comp){
this.pushOject(App.MyTestComponent.create({}));
}, this);
}.observes('controller.myComponents.length')
});
Actually the best solution is as #bmeyers suggested, is to have init function for container view like this:
init: function() {
this._super();
/*
THIS IS THE OBSERVER METHOD THAT WE HAVE IN THE VIEW, THIS CALL DOES NOT EVEN
NEED TO ADD A CHILD VIEW, BUT STILL NEEDS TO BE CALLED - STRANGE I KNOW BUT WHY
I AM NOT SURE. ALSO FACED ISSUE DURING TEST FOR ABOVE 2 APPROACHES, THIS ONE WORKED WELL
*/
this.appendMyComponent();
}
Here is the working jsbin: http://jsbin.com/xamoxese/13/
I'm using a render helper inside a template, which renders a searchbox with a typeahead.
Essentially (code removed for brevity):
script(type='text/x-handlebars', data-template-name='index')
{{render search}}
script(type='text/x-handlebars', data-template-name='search')
{{view App.TaggableInput valueBinding="searchText"}}
Which gives me a SearchController separated from the IndexController.
Inside App.TaggableInput I'm grabbing searchController to do some checking on the keyUp event:
App.TaggableInput = Ember.TextField.extend({
keyUp: function(e){
var controller = this.get('controller');
// Do stuff with the controller
}
});
On Ember RC7, I can access the controller inside theview as you'd expect with this.get('controller').get('searchText').
However in Ember 1.0.0 this.get('controller') returns the view, and whatever I do I can't get searchController.
I can't find any related info on the ember website regarding what's changed or what I'm supposed to do... for now I'm sticking with RC7.
Any ideas? I've spent hours on it this morning and can't figure it out. Thanks.
UPDATE: Fixed!
I swapped out this.get('controller') for this.get('targetObject') and it works as before. Had a peruse through a recent commit in ember source to find it...
Thanks for your suggestions guys!
I guess that in your code
App.TaggableInput = Ember.TextField.extend({
keyUp: function(e){
var controller = this.get('controller');
// Do stuff with the controller
}
});
this line
var controller = this.get('controller');
gets the controller associated to your (subview)
Try to use this line instead to access the route's controller:
var controller = this.get('parentView.controller');
Currently, the {{render}} helper takes 2 arguments, the first is the context, the second is the model.
I recommend using this method and following the naming convention for the model's controller rather than setting the controller explicitly.
You can find the docs here:
http://emberjs.com/guides/templates/rendering-with-helpers/#toc_the-code-render-code-helper
Accessing controllers from views was also being tracked in this discussion:
https://github.com/emberjs/ember.js/issues/1712#issuecomment-31183940
I think Ember has not changed its behaviour. I created a JSBin, where i managed to get the controller successfully.
What i did was creating a simple View and show it via {{render}} helper:
View:
App.FooView = Ember.TextField.extend({
didInsertElement : function(){
console.log(this.get("controller.constructor"));
console.log(this.get("context.constructor"));
}
});
Template:
{{render foo}}
And the first log statement showed an associated controller. Can you see any conceptual difference between my code and yours?
I am experimenting around with integrating Ember.Data into my application and also wanted to find out how to propery use ArrayController. Unfortunately I didn't even get around simple databinding on the controller.
I simply can't figure out where I went wrong, so I fully expect someone to be able to point out: Hey you wrote .extend instead of .create
I am trying something rather similar to what Discouse is doing in their AdminSiteSettings:
The controller in question:
App.UsersController = Ember.ArrayController.extend({
foo: 'bar'
});
route:
App.UsersRoute = Ember.Route.extend({
model: function () {
var users = App.User.find();
return users;
},
setupController: function (controller, model) {
controller.set('model', model);
console.log(controller.get('foo')); // this works correctly => bar
}
});
Only problem: The template is being rendered, but I can't bind to the foo property:
<script type="text/x-handlebars" data-template=name="users">
We render the correct template
{{foo}}
</script>
Only problem is: The Template never renders 'bar'. It's just empty.
Now I found something similar in Discourse where they have a textbox bound to filter:
https://github.com/discourse/discourse/blob/master/app/assets/javascripts/admin/controllers/admin_site_settings_controller.js
I can't really understand why my controller property is not showing up, (and yes I am actually trying to get Ember.data to work, but since this can easily be reproduced without it I figured I'd settle for the simple foo: bar property :(
Versions in use:
Ember.js: v1.0.0-rc.4-23-gbfd3023
Handlebars: 1.0.0-rc.4
Ember.data: 13
Any pointers are welcome. Thanks!
I don't know exactly where you app fails, but I have tried to recreate your use case and in this example jsbin it's working correctly, have a look.
Hope it helps.
I have the use case where I need to add custom views to a container view using a specific template and controller. Unfortunately this only works if I don't have the "linkTo" helper in my template. As soon as I add this I can't set a custom controller anymore.
<script type="text/x-handlebars" data-template-name="page1link">
<!-- remove this line and it will work -->{{#linkTo "page1"}}Go to Page 1{{/linkTo}}
<p>Link template</p>
</script>
App.IndexView = Ember.ContainerView.extend({
didInsertElement: function(){
var LinkView = Ember.View.extend({
templateName: "page1link",
controller: Ember.Controller.create()
});
for(var i = 0; i < 4; i++){
this.pushObject(LinkView.create());
}
}
});
I always get following exception:
Uncaught TypeError: Cannot call method 'lookup' of null
at following part of the ember code: Ember.LinkView
...
router: Ember.computed(function() {
return this.get('controller').container.lookup('router:main');
Uncaught TypeError: Cannot call method 'lookup' of null
}),
...
Somehow the container is not set in this case.
I've created a fiddle showing this issue. Is there a better way to implement this with ember?
JSFiddle Example
Thanks for any hints!
Have a look at the ember guides, specifically over here. They have a controller that displays a list of items each item wrapped in its own controller. While it seems you're trying to do this page by page (Not sure why), the principle is, I think, the same.
Let me know if I've missed the mark and I'll give it some more thought.
have a look at this stack overflow post for infinte scroll in a list using ember.
I have spent about 10 days on a simple problem I could have solved in about 10 minutes with Dojo. I hope I am missing something simple - I'm a noob who would like to use Ember.
I am simply trying to populate the content of an Ember.Select with data from another Controller using EmberData. Consider the case: being able to select a FoodGroup for a Raw Ingredient.
It seemed clear to use a 'needs' dependency between RawIngredientController and FoodGroupsController, and bind on the content.
This is the closest I have gotten to success and it does not look sufficient to me - I will explain.
<script type="text/x-handlebars" data-template-name="rawIngredients">
{{#each item in controller itemController="rawIngredient" }}
{{! view Ember.Select
contentBinding="controllers.foodGroups.content"
optionValuePath="content.id"
optionLabelPath="content.name"
prompt="select Food Group" }}
{{/each}}
</script>
Cook.RawIngredientController = Ember.ObjectController.extend({
isEditing: false,
needs: ['foodGroups'],
...
});
Cook.RawIngredient = DS.Model.extend({
name: DS.attr('string'),
nameKey: DS.attr('string'),
foodGroup: DS.belongsTo('Cook.FoodGroup')
});
Cook.FoodGroupsRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('content', Cook.FoodGroup.all()); // have also tried find()
}
});
Cook.FoodGroup = DS.Model.extend({
name: DS.attr('string'),
nameKey: DS.attr('string')
});
This actually renders all RawIngredients with no errors, but the Selects are empty because the FoodGroups store is empty. I can navigate to the FoodGroups screen which loads the store, come back to RawIngredients, and the selects are populated with FoodGroups.
I've seen a number of posts on this issue but none sufficiently address the issue when EmberData is involved. There are plenty of post-loaded examples, like this clever one from pangratz http://jsfiddle.net/pangratz/hcxrJ/ - but I haven't found any using EmberData to lazy-load the content.
This post is pretty close
Ember.js: Ember.Select valueBinding and lazy loading
and contains a workaround using an observer, which I couldn't get to work. I ended up binding to a binding which never invoked the actual loading of the FoodGroup data. Here's an example, maybe not the best, of one such attempt (of probably 20+):
Cook.FoodGroupsController = Ember.ArrayController.extend({
...
// NOTE: This seems redundant
values: function() {
return this.get('content');
}.property(),
...
});
Cook.RawIngredientController = Ember.ObjectController.extend({
...
// this should be the instance; also tried the object name FoodGroupsController....also tried using needs + 'controllers.foodGroups.values' in the binding
allFoodGroups: Ember.Binding.oneWay('Cook.foodGroupsController.values'), '
/*
// ... or rather ....??
allFoodGroups: function() {
this.get('foodGroupsBinding');
},
foodGroupsBinding: "Cook.foodGroupsController.values",
*/
...
});
{{view Ember.Select
contentBinding="allFoodGroups"
optionValuePath="content.id"
optionLabelPath="content.name"
prompt="select Food Group" }}
but it errors saying 'allFoodGroups' is providing a Binding not an Ember.Array. I think I am lost in a sea of swirling naming conventions. I could show my other attempts at this, but all had errors of undefined content.
Ugh. So backing up a bit... at least using my original solution and pre-loading the FoodGroups store should provide a workaround, however I cannot seem to get EmberData to go out and load the data, programmatically. I tried an init() function in the RawIngredientController like
init: function() {
var item = this.get('controllers.foodGroups.content.firstObject');
},
but I haven't found the right combination there either. And even if I do, it seems like the wrong approach because this will set up a reference for each RawIngredient rather than use a single reference from RawIngredients (or FoodGroups?) - but that seems like a different topic.
This is my attempt at a fiddle describing the problem http://jsfiddle.net/jockor/Xphhg/13/
Has anyone figured out an efficient, effective way to load and use stores defined in other controllers, using EmberData to lazy-load the associated content?
Your basic problem seems to be that you're not accessing the foodGroups route, so its setupController() never gets executed, thus the content of the select controller never gets set.
In your example, when adding a link to the route in question and clicking it, the route gets initialized and the bindings work.
I tried to update your JSFiddle, but it is linking to the "latest" version of ember-data from Github which has not been updated in a while (you're supposed to build it yourself until they make an official release), and it's also using an old version of Ember, so I was getting some weird errors.
So here is a version with the latest Ember and Ember-data: http://jsfiddle.net/tPsp5/
Notice what happens when you click the Countries link. I left behind some debugger; statements that may help you understand what gets invoked when.
As a design note, your "parent" controller should probably not depend on "child" controllers.