Reputation:
I want to add a mixin to an Ember class which has already been created. (The class is defined in a library, actually Ember itself; it's LinkView
).
I see that we can do mixin.apply(obj)
, but this applies the mixin to an instance of the class. I want to add the mixin to the class, so it's automatically mixed-in to all newly created objects.
I attempted to override the init
method of the class, using reopenClass
, and do mixin.apply(this)
there, to apply the mixin to the instance, and then call the original init
method, but this does not seem to work because the mixin wiring is set up in the init
method and it's already too late by the time I can get to it.
reopenClass
does not seem to accept a mixin argument like extend
does. Its code seems to suggest that it's doing something with mixins, but whatever it is it doesn't work:
a = Ember.Object.extend().reopenClass(Ember.Mixin.create({mixval: 1});
a.create().get('mixval'); // undefined
I know that I could create my own class with MyLinkView = Ember.LinkView.extend(mixin, ...
, but unfortunately the original class name is referenced explicitly within the library, so I really would prefer to figure out how to extend that original class with my mixin.
I experimented with Ember.LinkView = Ember.LinkView.extend(mixin, ...
. This somehow seems dangerous, although it seems to work. But in this particular case it doesn't help me since the reference within the Ember code (in the definition of the {{link-to}}
helper) is to an internal version of the class, not the fully qualified Ember.LinkView
.
Any ideas?
Upvotes: 3
Views: 2304
Reputation:
The solution is simply
Klass = Parent.extend({init: {...}});
Mixin = Ember.Mixin.create({init: {...}});
Klass.reopen(mixin);
Everything works as expected, including the super chain. In other words, a call to Klass.create().init()
will call the mixin's init
, and a call to super from there will call the original Klass#init
.
In the course of researching this question, I discovered something interesting about reopen
. Even if the argument is not a mixin, it is treated as one (internally, it actually creates a temporary one). This means that if you do
Klass.reopen({
init: function() {
this._super.apply(this, arguments);
}
});
The call to super
is actually calling the original init
, not the init
in the parent class. In other words, specifying init
in a reopen
does not replace the existing init
on the class, it more or less layers on top of it. I can't see this behavior documented anywhere in the Ember docs, but it does seem useful in the right situation.
Upvotes: 5