]>
git.proxmox.com Git - extjs.git/blob - extjs/packages/core/src/class/Mixin.js
2 * This class is a base class for mixins. These are classes that extend this class and are
3 * designed to be used as a `mixin` by user code.
5 * It provides mixins with the ability to "hook" class methods of the classes in to which
6 * they are mixed. For example, consider the `destroy` method pattern. If a mixin class
7 * had cleanup requirements, it would need to be called as part of `destroy`.
9 * Starting with a basic class we might have:
11 * Ext.define('Foo.bar.Base', {
12 * destroy: function () {
18 * A derived class would look like this:
20 * Ext.define('Foo.bar.Derived', {
21 * extend: 'Foo.bar.Base',
23 * destroy: function () {
27 * this.callParent(); // let Foo.bar.Base cleanup as well
31 * To see how using this class help, start with a "normal" mixin class that also needs to
32 * cleanup its resources. These mixins must be called explicitly by the classes that use
35 * Ext.define('Foo.bar.Util', {
36 * destroy: function () {
41 * Ext.define('Foo.bar.Derived', {
42 * extend: 'Foo.bar.Base',
45 * util: 'Foo.bar.Util'
48 * destroy: function () {
52 * this.mixins.util.destroy.call(this);
54 * this.callParent(); // let Foo.bar.Base cleanup as well
58 * var obj = new Foo.bar.Derived();
61 * // logs D then U then B
63 * This class is designed to solve the above in simpler and more reliable way.
67 * Using `mixinConfig` the mixin class can provide "before" or "after" hooks that do not
68 * involve the derived class implementation. This also means the derived class cannot
69 * adjust parameters to the hook methods.
71 * Ext.define('Foo.bar.Util', {
72 * extend: 'Ext.Mixin',
76 * destroy: 'destroyUtil'
80 * destroyUtil: function () {
85 * Ext.define('Foo.bar.Class', {
87 * util: 'Foo.bar.Util'
90 * destroy: function () {
95 * var obj = new Foo.bar.Derived();
100 * If the destruction should occur in the other order, you can use `before`:
102 * Ext.define('Foo.bar.Util', {
103 * extend: 'Ext.Mixin',
107 * destroy: 'destroyUtil'
111 * destroyUtil: function () {
116 * Ext.define('Foo.bar.Class', {
118 * util: 'Foo.bar.Util'
121 * destroy: function () {
126 * var obj = new Foo.bar.Derived();
133 * One way for a mixin to provide methods that act more like normal inherited methods is
134 * to use an `on` declaration. These methods will be injected into the `callParent` chain
135 * between the derived and superclass. For example:
137 * Ext.define('Foo.bar.Util', {
138 * extend: 'Ext.Mixin',
142 * destroy: function () {
149 * Ext.define('Foo.bar.Base', {
150 * destroy: function () {
155 * Ext.define('Foo.bar.Derived', {
156 * extend: 'Foo.bar.Base',
159 * util: 'Foo.bar.Util'
162 * destroy: function () {
168 * var obj = new Foo.bar.Derived();
171 * // logs M then B then D
173 * As with `before` and `after`, the value of `on` can be a method name.
175 * Ext.define('Foo.bar.Util', {
176 * extend: 'Ext.Mixin',
180 * destroy: 'onDestroy'
184 * onDestroy: function () {
189 * Because this technique leverages `callParent`, the derived class controls the time and
190 * parameters for the call to all of its bases (be they `extend` or `mixin` flavor).
194 * Some mixins need to process class extensions of their target class. To do this you can
195 * define an `extended` method like so:
197 * Ext.define('Foo.bar.Util', {
198 * extend: 'Ext.Mixin',
201 * extended: function (baseClass, derivedClass, classBody) {
202 * // This function is called whenever a new "derivedClass" is created
203 * // that extends a "baseClass" in to which this mixin was mixed.
210 Ext
.define('Ext.Mixin', function (Mixin
) { return {
213 addHook: function (hookFn
, targetClass
, methodName
, mixinClassPrototype
) {
214 var isFunc
= Ext
.isFunction(hookFn
),
217 fn
= isFunc
? hookFn
: mixinClassPrototype
[hookFn
],
218 result
= this.callParent(a
);
222 existingFn
= targetClass
.hasOwnProperty(methodName
) &&
223 targetClass
[methodName
];
226 hookFn
.$previous
= Ext
.emptyFn
; // no callParent for these guys
229 hook
.$name
= methodName
;
230 hook
.$owner
= targetClass
.self
;
233 hook
.$previous
= existingFn
.$previous
;
234 existingFn
.$previous
= hook
;
236 targetClass
[methodName
] = hook
;
241 onClassExtended: function(cls
, data
) {
242 var mixinConfig
= data
.mixinConfig
,
244 superclass
= cls
.superclass
,
245 onClassMixedIn
= data
.onClassMixedIn
,
247 befores
, afters
, extended
;
252 (mixinConfig
|| (data
.mixinConfig
= mixinConfig
= {})).on
= hooks
;
256 parentMixinConfig
= superclass
.mixinConfig
;
258 if (parentMixinConfig
) {
259 data
.mixinConfig
= mixinConfig
= Ext
.merge({}, parentMixinConfig
, mixinConfig
);
262 data
.mixinId
= mixinConfig
.id
;
265 if (mixinConfig
.beforeHooks
) {
266 Ext
.raise('Use of "beforeHooks" is deprecated - use "before" instead');
268 if (mixinConfig
.hooks
) {
269 Ext
.raise('Use of "hooks" is deprecated - use "after" instead');
271 if (mixinConfig
.afterHooks
) {
272 Ext
.raise('Use of "afterHooks" is deprecated - use "after" instead');
276 befores
= mixinConfig
.before
;
277 afters
= mixinConfig
.after
;
278 hooks
= mixinConfig
.on
;
279 extended
= mixinConfig
.extended
;
282 if (befores
|| afters
|| hooks
|| extended
) {
283 // Note: tests are with Ext.Class
284 data
.onClassMixedIn = function (targetClass
) {
285 var mixin
= this.prototype,
286 targetProto
= targetClass
.prototype,
290 Ext
.Object
.each(befores
, function (key
, value
) {
291 targetClass
.addMember(key
, function () {
292 if (mixin
[value
].apply(this, arguments
) !== false) {
293 return this.callParent(arguments
);
300 Ext
.Object
.each(afters
, function (key
, value
) {
301 targetClass
.addMember(key
, function () {
302 var ret
= this.callParent(arguments
);
303 mixin
[value
].apply(this, arguments
);
311 Mixin
.addHook(hooks
[key
], targetProto
, key
, mixin
);
316 targetClass
.onExtended(function () {
317 var args
= Ext
.Array
.slice(arguments
, 0);
318 args
.unshift(targetClass
);
319 return extended
.apply(this, args
);
323 if (onClassMixedIn
) {
324 onClassMixedIn
.apply(this, arguments
);