I have an ember object and i'd like to know if it is in a dirty state.
var App.Post = Ember.Object.create({
title: "Test",
isDirty: false
});
App.Post.set("title", "Test2");
App.Post.get("isDirty") // Should === true
For the moment, I have tried overloading the set for the object
App.Post = Ember.Object.create({
set: function(path, value) {
this._super(path, value);
this._super("isDirty", true);
}
})
It works when I am calling directly myObject.set but it doesn't seem to use that set function when using embers binding. I added logs and this method isn't called by the regular emberjs bindings workflow.
Another thing I've tried is to add an observers to toggle the dirty flag.
App.Post = Ember.Object.create({
hasBeenModified: function() {
this.set("isDirty", true);
}.observes("title")
})
For a reason still unknown, when I use observes at the model level my bindings do not work anymore in the UI.
I believe you may also need to override setUnknownProperty. The UI is using Ember.set(object, key, value). If you look at the implementation
https://github.com/emberjs/ember.js/blob/master/packages/ember-metal/lib/property_set.js#L60
It doesn't call your setter, but will call setUnknownProperty if it exists.
Actually, at
https://github.com/emberjs/ember.js/blob/master/packages/ember-metal/lib/property_set.js#L52
It looks like they will call your setter if you have predefined the field in your App.Post class.
Related
I have a computed property, which fetches an associated record and tries to print it. The first time I fetch the record, it's null. All subsequent accesses work correctly. It is set as 'async: true', but setting it as false doesn't change this behavior.
MyApp.ThingsController = Ember.ArrayController.extend({
myProperty: function() {
var content = this.get('content');
return content.filter(function(thing) {
console.log(thing.get('title')); // Since this is a direct attribute on the model, it prints fine.
var associatedThing = thing.get('associatedThing'), otherThings = [];
console.log(associatedThing.get('content')); // This is a hasMany attribute on the model, and is null the *first* time, but fine on subsequent accesses.
otherThings = associatedThing.get('content'); // Obviously doesn't work the first time either.
return thing.get('title') + otherThings[0].get('name'); // Or similar.
});
}.property('content.#each') // adding observers for content.associatedThing.#each does not seem to make any difference.
});
Models are like:
MyApp.Thing = DS.Model.extend({
title: DS.attr('string'),
associatedThings: DS.hasMany('associatedThing', { async: true })
});
MyApp.AssociatedThing = DS.Model.extend({
name: DS.attr('string')
});
Obviously, I cannot use promises here since I need to return a value from the function, so I cannot use a callback (since we're in a computed property.) How can I make this work the first time this associated record is accessed?
Edit: myProperty is a computed property on an ArrayController, and is used for showing or hiding Things
Actually, you can use a promise, just not in the way you're thinking. For hasMany relationships, Ember-Data returns a PromiseArray. That means that it returns a promise that will resolve to an array. But in the meantime, the proxy will actually respond to get requests that you make with undefined. Then, when the promise resolves, any observers are fired. So, if you have your property depend on the associatedThings property, it will update when the promise resolves. In other words, this will work as expected:
MyApp.Thing = DS.Model.extend({
title: DS.attr('string'),
associatedThings: DS.hasMany('associatedThing', { async: true }),
sum: function() {
var things = this.get('associatedThings');
return things.filter(function(thing) {
return shouldFilterThing(thing);
});
}.property('associatedThings.#each.size')
});
Also, please don't be bugged by the fact that this doesn't happen synchronously. Trying to change it from asynchronous to synchronous will just make your code that much more fragile. Let Ember do its job and handle all of the properties and bindings for you.
My solution to this was simply to access the associated data in the ArrayController's init method:
init: function() {
var content = this.get('content');
content.forEach(thing) {
// Prime the data.
var associatedThings = get('associatedThings');
});
}
This makes everything work as expected.
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 have model:
App.Item = DS.Model.extend({
itemId: DS.attr('string'),
itemName: DS.attr('string'),
itemType: DS.attr('string'),
});
I successfully create some items from JSON. I can put them to page by {{#each items}}{{ itemName}}{{/each}}. But I don't know, how to get itemName in javascript.
I tried this:
var item = App.Item.find(1);
console.log(item.itemName);
--> undefined
I can't find anything useful from emberjs and ember-data docs. Can anyone help me?
Thanks
I tried this:
var item = App.Item.find(1);
console.log(item.itemName);
--> undefined
This is normal because the call to .find(1); is asyncronous and returns a promise and not the item you are expecting.
Therefore you should try:
App.Item.find(1).then(function(result) {
console.log(record.get('itemName'));
});
It also depends from where you are doing App.Item.find() if it's from inside a route you should wait until the afterModel hook is called to access your items:
App.FooRoute = Ember.Route.extend({
model: function() {
return App.Item.find(1);
},
afterModel: function(record) {
console.log(record.get('itemName'));
}
});
Also be aware that if you where calling find() without parameter then you will receive a RecordArray which you need to loop over to get access to your items. Also worth mentioning is that in ember you should always use .get() and .set() instead of the vanilla dot-notation otherwise you hijack the binding mecanism resulting in no updates in your view etc.
Note, if you are using the latest ember.js release (1.0.0) then the call to .find() should be made somewhat different. But that's not clear from your question.
Hope it helps.
So, I am trying to get a simple propertyBinding to work with emberjs. Specifically, I have a controller with a content property, that gets updated under certain circumstances and a view, which needs that content array to draw some chart.
I have made the most basic example and it doesn't seem to work. My simple example is the following:
Appname.IndexController = Ember.Controller.extend({
value: 'bla'
});
Appname.IndexView = Ember.View.extend({
templateName: 'Index',
propertyBinding: 'Appname.IndexController.value',
didInsertElement: function() {
console.log('Indexview');
console.log(this.get('property'));
}
});
It is as simple as that, and it just does not work. What is really odd though, if I create another testcontroller (rather then extending it) e.g.
Appname.TestController = Ember.Controller.create({
value: 'jpopo'
});
the property binding works all of the sudden. But I just can not get it to work with the IndexController
(And in case the information is necessary, in the Applicaton.hbs I have an outlet)
Thanks for any help
Bindings work for instantiated objects, not for object definitions.
Appname.IndexController is the controller definition, not an instance. It is not what you want to bind to. The Ember.js app will create an instance of IndexController, and it's that created instance that you want to bind to:
To access the actual controller instance from its view, use controller.
Appname.IndexView = Ember.View.extend({
templateName: 'index',
propertyBinding: 'controller.value',
didInsertElement: function() {
console.log(this.get('property'));
}
});
Of course, that is if you follow Ember.js conventions.
I have a Ember.Mixin which observes one of its properties (here bar.baz).
I've extended this Mixin and set bar.baz in the .create() parameter, but my observer is not called.
Here is my code :
App.FooMixin = Ember.Mixin.create({
barBazDidChange: function() {
console.log('barBazDidChange'); // never called
}.observes("bar.baz")
});
App.Foo = Ember.Object.extend(App.FooMixin);
App.fooObject = App.Foo.create({
bar: Ember.Object.create({
baz: "ember"
})
});
And the associated jsfiddle : http://jsfiddle.net/aMQmn/
I could of course call the observer in the init() method like below, but I wonder if there is a better solution (or if this solution is the proper way to do that) :
App.FooMixin = Ember.Mixin.create({
init: function() {
this._super();
if (this.getPath('bar.baz')) {
this.notifyPropertyChange('bar.baz');
}
}
});
So no answer during 7days... what I would do instead of passing the property at creation time is chaining the creation and setting the property, like this:
App.fooObject = App.Foo.create().set('bar', Ember.Object.create({baz: "ember"}));
If it's not satisfying enough, perhaps you could post an issue in the ember project on github:
https://github.com/emberjs/ember.js/issues
The correct solution is to override the init method (make sure to call this._super()) and call the function there. As others have noted, the observer is not firing because the value is not actually changing. There has been some discussion around making create behave more like setProperties which would make this a non-issue.