I have a component that is provided each record ('data') of a model, along with 'meta' information that defines the attribute of the record to use, and renders it to a table. Within the component I'm trying to bind the underlying record attribute to each UI element {{tdVal}}:
tdVal : function(){
return Ember.computed.alias('data.' + this.get('meta').get('field'));
}.property()
Unfortunately this just renders [object object] in the UI. For comparison the following renders all of the items correctly, but obviously does not bind:
tdVal : function(){
return this.get('data').get(this.get('details').get('field'));
}.property()
Am I going about this in completely the wrong way? Any help would be very much appreciated.
UPDATE
To add clarity, if I bind to a literal key instead of an attribute key derived from the meta information I still have exactly the same problem, so I don't think it's an issue with using a derived key:
tdVal : function(){
return Ember.computed.alias('data.partner_id');
}.property()
UPDATE
If I set the binding against the component as an attribute rather than a function assigned to the attribute, then it works. Problem is I can't do this as the key for the alias needs to be derived and not a literal:
export default Ember.Component.extend({
tdVal : Ember.computed.alias('data.partner_id')
})
I found the solution to this. I think the computed alias was failing when returned from a function due to timing issues. Instead I added it to init()
export default Ember.Component.extend({
tagName : '',
init: function(){
this._super();
this.set('tdVal', Ember.computed.alias('data.' + this.get('details').get('field')));
}
});
This has done the trick, everything renders as it should and updates to the UI are reflected in the model and vice versa.
The second way looks right. It doesn't bind because you didn't provide the properties to bind to:
tdVal : function(key, value){
var path = 'data.' + this.get('details.field');
if (value)
this.set(path, value);
return this.get(path);
}.property('data', 'details.field')
Related
I have a component in which I am observing a property from model and model is fed to controller in controller setup as controller property 'model'. Model has properties age, salary, rank. The property in component is to be entered by user.
The component will be called as:
{{ui-slider prop="age"}}
OR
{{ui-slider prop="salary"}}
This is not the complete component but it explains my problem.. The Component is :
App.UiSliderComponent = Ember.Component.extend({
prop:function(){
return this.get('prop');
}.property('prop'),
modelPropertyObserver:function(){
console.log("property is "+this.get('targetObject.model').get(this.get('prop'));
}.observes('targetObject.model.'+this.get('prop')),
didInsertElement:function(){
console.log("Element inserted");
}
})
This is not working. When I observe property like .observes('targetObject.model.age') then it works fine. But now it is showing cori_component.js:29 Uncaught TypeError: this.get is not a function
I also tried .observes('targetObject.model.'+this.prop) Though it doesn't show any error but the observer doesn't work.
How to concatenate the 'prop' property to the observer?
Or is there any way that I can concatenate the string inside component and then substitute it into observer.
Innstead you can try,
{{ui-slider prop=model.age}}
and
modelPropertyObserver: Ember.observer('prop', function() {
console.log("modelPropertyObserver");
})
I don't think the below one is acceptable.
.observes('targetObject.model.'+this.get('prop'))
You can try the following by overriding init and setting the property there (I've done this to have a dynamic computed property). I'm using Babel to have ES2015 features (like ...arguments and string interpolation
)
init() {
this._super(...arguments);
this.set('value', Ember.computed(`model.${this.get('attribute')}`, function() {
return this.get(`model.${this.get('attribute')}`);
}));
}
I assume by the documentation that you could replace Ember.computed with Ember.observer (and replace the corresponding method signature - snippet bellow copied from the documentation)
Ember.observer('value', function(sender, key, value, rev) {
// Executes whenever the "value" property changes
// See the addObserver method for more information about the callback arguments
}
I have a model in ember-data defined as:
App.Note = DS.Model.extend({
content: attribute('string'),
createdDate: attribute('string', {
defaultValue: function() {return new Date()}
}),
title: function() {
// do stuff
}.property('content', 'createdDate')
});
I notice that when I create a new object with:
this.store.createRecord('note');
The title property is not computed. I assumed that the default value would trigger the property to update, but it's not. How can I get a default value to also trigger a computed property to fire?
I believe the problem is that you are using 'content' as a property name. I would avoid using that word, as Ember tends to use it a lot itself and it can mess things up. Here is a jsbin of your code woriking: http://emberjs.jsbin.com/jebugofo/6/edit?html,css,js,output . Simply needed to get rid of that name for the property.
I'm in the process of optimizing my Ember application by following some of the tips given in this presentation. I'm wondering how I can give unbound properties as arguments to components and views. For instance in
{{my-component arg=unboundProperty}}
I want unboundProperty to be unbound, i.e. it takes as value its first non-null value (set after the models in the route has been resolved) but does not propagate to the component when its value changes. How can I achieve that?
If you really need to do it you can use a computed property without defining dependencies. The computed property will be calculated the first time it's requested, and then it will never think it needs to update, so it will never update.
App.FooController = Ember.ObjectController.extend({
realProperty: 'fooBar',
unboundProperty: function(){
return this.get('realProperty');
}.property()
});
{{my-component arg=unboundProperty}}
You could do the same thing in your component
App.MyComponentComponent = Ember.Component.extend({
readOnceArg: function(){
return this.get('arg');
}.property()
})
I'm trying to use the 'needs' feature to allow one controller to obtain a value from another. Here's a JSFiddle that shows a stripped-down version of my app before binding a value: http://jsfiddle.net/kevinwh/WRxnE/4/
App.ApplicationController = Ember.ObjectController.extend({
init: function() {
this._super();
},
dishClicked: function() {
console.log('clicked');
this.incrementProperty('clickCount');
}
});
App.DishController = Ember.ObjectController.extend({
needs: ['application'],
init: function() {
this._super();
},
//clickCountBinding: 'controllers.application.clickCount'
});
Basically, my ApplicationController has a clickCount property that is updated (by an action) whenever one of the Dish links is clicked. Clicking on a link also activates the DishRoute via linkTo.
Now I'd like the contained DishController to also have access to ApplicationController's clickCount. So I add the 'needs' property and a clickCountBinding property (which will have to be uncommented in the JSFiddle). Then, when I click on a link I get a complaint:
assertion failed: Cannot delegate set('clickCount', 0) to the 'content' property of object proxy : its 'content' is undefined.
Apparently the binding is being activated before the model content is set on the controller. Since the controller is being set up by the linkTo, my DishRoute.model() and DishRoute.setupController() methods are not invoked. Also, the DishController.init() method isn't even called before the binding error happens.
I considered the possibility that I should just stick a content member object into the class (commented out in the JSFiddle), but doing that gives a bizarre result: the click count is incremented separately for the different links. Interesting, but not what I'm after.
So - how do I share the clickCount value across these controllers? Is there some other way to set up the content in the DishController so that the binding will work?
You've just slightly misunderstood the error message.
The issue is that you've subclassed the ApplicationController from ObjectController even though it doesn't have an underlying content object to proxy to, you should just user Ember.Controller in this case.
That being said, if you have a counter you should probably default it to zero anyway.
App.ApplicationController = Ember.Controller.extend({
clickCount: 0,
dishClicked: function() {
console.log('clicked');
this.incrementProperty('clickCount');
}
});
App.DishController = Ember.ObjectController.extend({
needs: ['application'],
clickCountBinding: 'controllers.application.clickCount'
});
Here is the linkTo helper in my handlebars template
{{#linkTo 'person.page' nextPage target="controller"}}Next{{/linkTo}}
Here is my controller
PersonApp.PersonController = Ember.ArrayController.extend(Ember.PaginationMixin, {
itemsPerPage: 2
});
Here is the computed property in the mixin
nextPage: function() {
var nextPage = this.get('currentPage') + 1;
var availablePages = this.get('availablePages');
if (nextPage <= availablePages) {
return Ember.Object.create({id: nextPage});
}else{
return Ember.Object.create({id: this.get('currentPage')});
}
}.property('currentPage', 'availablePages'),
when I console log just before each return statement I can see the page id is correct ... yet my html isn't updated. Anything simple that I'm doing wrong in the above?
Also I do see a print each time I change the page (so the computed properties I depend on are being fired)
Here is a full blown jsfiddle showing that after you click next the first time ... it still points at /#/page/2 instead of /#/page/3
http://jsfiddle.net/RhTyx/2/
Thank you in advance
First off: It would be nice, if you would not link a fiddle where the most important code is not part of the fiddle (the FilterSortSliceMixin). Therefore one cannot test anything, despite the fact the fiddle was really huge and contained lots of unnecessary code.
Regarding your problem:
I think this cannot work because the dependent properties you specified do not do anything. Your {{#linkTo}} helper sends the user into the the PersonPageRoute. The code of this route is:
PersonApp.PersonPageRoute = Ember.Route.extend({
model: function(params) {
return PersonApp.Person.find(params.page_id);
},
setupController: function(controller, model) {
this.controllerFor('person').set('selectedPage', model.get('id'));
}
});
So you are getting the personcontroller and set the property selectedPage. But this property is not specified in your dependent keys. So therefore i would suggest this:
//maybe even remove currentPage??
}.property('currentPage', 'availablePages' , 'selectedPage'),
So i guess you got confused with your naming. I guess, you should either have the property 'selectedPage' or 'currentPage', right?
Update: This is definitely a bug. Heres an excerpt from the LinkView class, which is used with the linkTo helper:
var LinkView = Ember.View.extend({
attributeBindings: ['href', 'title'],
href: Ember.computed(function() {
var router = this.get('router');
return router.generate.apply(router, args(this, router));
})
});
As you see it does not specify any dependent keys. Maybe you can reopen/patch this class and add the dependent key. Don't know which one this would have to be, but maybe context?