Observer are not called on create() - ember.js

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.

Related

Can't set controller's content when async action in EmberJS

I'm getting stuck when I want to update my controller's content after a specific action :
this.set('content', CL.myModel.findALL());
I'm not using ember-data or any of them.
CL.myModel.findALL returns an Ember promise that returns itself the result in it's callback.
Actually, I've done the same thing in my setupController in the router and It works well.
Why does it not work in the controller itself ?
Thank you for helping.
You should do:
controller = this;
CL.myModle.findALL().then(function(models) {
controller.set('content', models);
})
I believe the reason that it works without this in Route#setupController is because, behind the scenes, Ember does something similar to the above code with the result of your model hook (which is typically a promise).
Another option you could look at is DS.PromiseObject (it's a nice abstraction for promises that wrap a promise and expose it's fulfillment value as content. You'd have to extract it from an ember-data build though.
Here is an example of my model :
CL.myModle.reopenClass({
findAll : function(){
return CL.Utils.ajaxPromise(r)
.then(this.findByIdSuccess.bind(this), this.findByIdFailed.bind(this));
},
findByIdSuccess: function (data) {
var negotiation = CL.NegotiationModel.create(data);
this.setMembers(negotiation);
this.setParties(negotiation);
this.setVersions(negotiation);
return negotiation;
},
})
Here is my controller :
onRefresh : function(){
this.set('content',CL.myModle.findAll())
}
As you can see in the model part I call some functions that do some model modification and I get it back findByIdSuccess callback.
I can't just return the datas I retrieve in the "then" of the promise.
My question again if it's still not clear : Is there any way to get the promise's data without doing :
CL.myModle.findALL().then(function(models) {
controller.set('content', models);
})

The Ember Way for Setting a Controller Property Based on this.store.find()

I am setting up a page where my user can add an orgLanguage, and I'd like to show a special message if this is the first orgLanguage being added. I'm able to get my code working, but it sure looks ugly, and I'm wondering if there's a better way to handle this?
First, here's my Handelbars template:
Handlebars Template (Simplified):
{{#if isFirstOrgLanguage}}
...display some content
{{/if}}
That variable is defined on my controller as follows.
Controller (Simplified):
export default Ember.ObjectController.extend({
isFirstOrgLanguage: function() {
// the 'orgLanguages' controller property is set in the route
var orgLanguagesPromiseArray = this.get('orgLanguages');
return orgLanguagesPromiseArray.then( function() {
var orgLanguagesRecordArray = orgLanguagesPromiseArray.get('content');
var orgLanguagesArray = orgLanguagesRecordArray.get('content');
return orgLanguagesArray ? orgLanguagesArray.length === 1 : true;
});
}.property('orgLanguages')
}
I've named my variables the data type that I receive. You'll note that this is a computed property that depends on a controller property set on my route, shown below.
Route (Simplified):
setupController: function (controller, model) {
this._super(controller, model);
controller.set('orgLanguages', this.store.find('org-language') );
},
Finally, I'd like to call some basic jQuery on this Handlebars template if isFirstOrgLanguage is true, so I set up my view as follows.
View:
export default Ember.View.extend({
didInsertElement: function() {
this.get('controller').get('isFirstOrgLanguage').then( function( isFirstOrgLanguage ) {
console.log('isFirstOrgLanguage', isFirstOrgLanguage);
});
}
});
This seems like a crazy amount of promises and async management just to answer the question "is there exactly 1 orgLanguage defined"? Although the above works, is there a simpler way, or perhaps "The Ember Way" to do this?
Update:
In doing some additional research, it seems this has been a topic for some debate. Here are relevant discussions I've seen on this. If I settle on a pattern I like, I'll post it as as an answer, but would welcome other suggestions.
http://discuss.emberjs.com/t/dashboard-type-views/5187/24
http://discuss.emberjs.com/t/the-right-way-to-load-additional-models-to-build-filtering-checkboxes/4966/4
I wanted to post how I eventually solved this.
First, it became clear that there are recommended solutions to this pattern, but no "one true way". See http://discuss.emberjs.com/t/the-right-way-to-load-additional-models-to-build-filtering-checkboxes/4966/4.
What I wound up using was this:
Route:
...
afterModel: function() {
var _this = this;
Ember.RSVP.hash({
languages: this.store.find('language'),
orgLanguages: this.store.find('org-language')
}).then( function( hash ) {
_this.set('controller.languages', hash.languages );
_this.set('controller.orgLanguages', hash.orgLanguages );
});
},
...
The key insights here are:
This is done after the page's model loads. This may or may not make sense depending on your context.
Some people like to wrap each model in its own controller, but I didn't have clean mappings to controllers like that, so I directly set these property values.
It's generally bad practice to set computed properties that are promises, so if you have to deal with promises (which with any use of this.store.find() you do, then it's best to resolve the promise in the route and then pass the "concrete" property to your controller. But keep in mind that your template will be rendering these values when they eventually resolve! So, again there is some room for debate.
I think the general takeaway is that Ember is giving you lots of options to get this done, with plenty of possibilities to use depending on your needs.

Call custom function when view is rendered, from one place and not repeating code for every view

in ember application i want to call my custom function which does some modifications of dom elements, the only solution i found is to repeat below code as many times as many views/routes i have
for example
rendering indexView
indexView = Ember.View.Extend({
didInsertElement:function(){
//my custom function call goes here.. myFunction();
}
});
rendering OtherView
OtherView = Ember.View.Extend({
didInsertElement:function(){
//my custom function call goes here.. myFunction();
}
});
rendering MoreView
MoreView = Ember.View.Extend({
didInsertElement:function(){
//my custom function call goes here.. myFunction();
}
});
Is there a way of calling myfunction globaly whenever any view is rendered? I really do not want to repeat code for every single view i render.
thanks!
You can create a Mixin:
App.SomeMixin = Ember.Mixin.create({
didInsertElement: function() {
this._super();
//do your common stuff here
}
});
And use it in your views:
App.SomeView = Ember.View.Extend(App.SomeMixin, {
didInsertElement: function() {
this._super();
//do your custom stuff here
}
});
I would use a mixin to do this. If, however, you find that you are using this mixin into every single view that you create, it might be better to reopen the Ember.View class and add this functionality there. Also,if you reopened the class, what you could do is, depending upon the use case, create a static function inside Ember.View.reopenClass which you would pass to a
Ember.run.scheduleOnce()
utility that ember provides so that, if the function that you need doesn't need any state (for example, just does something to the page after the content has loaded or something.), it will just run the function once after all the views are rendered in the page.
There are two ways to handle this. Create a base class, or reopen the global View class and insert your code. I prefer the first, since it's easier to maintain and track down issues.
Reopen the View class and tack on an additional method that triggers on didInsertElement
Ember.View.reopen({
doStuff:function(){
//myFunction();
}.on('didInsertElement')
});
In the case of debouncing
Ember.View.reopen({
doStuff:function(){
Ember.run.debounce(this.doRealStuff, 200);
}.on('didInsertElement'),
doRealStuff: function(){
console.log('foo');
}
});
http://emberjs.jsbin.com/gecoziwe/1/edit
Create a base class and extend from it (My preferred method, since it's easier to track down issues, can't say this enough)
App.BaseView = Ember.View.extend({
doStuff:function(){
//myFunction();
}.on('didInsertElement')
});
App.FooView = App.BaseView.extend({
});
App.BarView = App.BaseView.extend({
});

this._super() issue when an object is created

In the new EmberRC1 version, it seems that we cannot call super class method from a created object. I have some standard mixins and views which i use to either create or extend to some other objects. I have some methods overriden, but still i needed to execute super methods which we can acheive in previous versions using this._super(). But in the newer version, when i create an object, this is not happening.
window.App = Ember.Application.create();
App.Router.map(function(){
this.resource("users");
});
App.UsersView = Ember.ContainerView.extend({
init : function(){
this._super();
this.pushObject(App.TestView.create({
didInsertElement : function() {
this.$().append(' <br><h4>Appended at Object</h4> ');
}
}))
}
});
App.TestView=Ember.View.extend({
templateName:'test',
didInsertElement : function() {
this.$().append(' <br><h4>Appended at Class</h4> ');
}
});
So is this part completely removed or we can acheive this super calling some other way?
To understand my problem here is the jsfiddle.
PS: I know that i can have the code that needs to be executed with some other methodname and call the same method. But it would be nice if i have solution for this.
You can still call this._super() from an init method and it will call the parent. What has been removed is calling create on an object and passing in an init method. There is a createWithMixin method that still allows that functionality.
There is a pretty detailed post about this functionality here:
Difference between .create() and .createWithMixins() in ember

Best way to detect if an Ember.Object is dirty

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.