linuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacos
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
620 lines
22 KiB
620 lines
22 KiB
/** |
|
* @protected |
|
* @class Ext.app.BaseController |
|
* Base class for Controllers. |
|
* |
|
*/ |
|
Ext.define('Ext.app.BaseController', { |
|
requires: [ |
|
'Ext.app.EventBus', |
|
'Ext.app.domain.Global' |
|
], |
|
|
|
uses: [ |
|
'Ext.app.domain.Controller' |
|
], |
|
|
|
mixins: ['Ext.mixin.Observable'], |
|
|
|
isController: true, |
|
|
|
config : { |
|
/** |
|
* @cfg {String} id The id of this controller. You can use this id when dispatching. |
|
* |
|
* For an example of dispatching, see the examples under the |
|
* {@link Ext.app.Controller#cfg-listen listen} config. |
|
* |
|
* If an id is not explicitly set, it will default to the controller's full classname. |
|
* |
|
* @accessor |
|
*/ |
|
id: null, |
|
|
|
/** |
|
* @cfg {Object} control |
|
* @accessor |
|
* |
|
* Adds listeners to components selected via {@link Ext.ComponentQuery}. Accepts an |
|
* object containing component paths mapped to a hash of listener functions. |
|
* The function value may also be a string matching the name of a method on the |
|
* controller. |
|
* |
|
* In the following example the `updateUser` function is mapped to to the `click` |
|
* event on a button component, which is a child of the `useredit` component. |
|
* |
|
* Ext.define('MyApp.controller.Users', { |
|
* extend: 'Ext.app.Controller', |
|
* |
|
* control: { |
|
* 'useredit button[action=save]': { |
|
* click: 'updateUser' |
|
* } |
|
* }, |
|
* |
|
* updateUser: function(button) { |
|
* console.log('clicked the Save button'); |
|
* } |
|
* }); |
|
* |
|
* The method you pass to the listener will automatically be resolved on the controller. |
|
* In this case, the `updateUser` method that will get executed on the `button` `click` |
|
* event will resolve to the `updateUser` method on the controller, |
|
* |
|
* See {@link Ext.ComponentQuery} for more information on component selectors. |
|
*/ |
|
|
|
control: null, |
|
|
|
/** |
|
* @cfg {Object} listen |
|
* @accessor |
|
* |
|
* Adds listeners to different event sources (also called "event domains"). The |
|
* primary event domain is that of components, but there are also other event domains: |
|
* {@link Ext.app.domain.Global Global} domain that intercepts events fired from |
|
* {@link Ext.GlobalEvents} Observable instance, |
|
* {@link Ext.app.domain.Controller Controller} domain can be used to listen to events |
|
* fired by other Controllers, {@link Ext.app.domain.Store Store} domain gives access to |
|
* Store events, and {@link Ext.app.domain.Direct Direct} domain can be used with |
|
* Ext.Direct Providers to listen to their events. |
|
* |
|
* To listen to "bar" events fired by a controller with id="foo": |
|
* |
|
* Ext.define('AM.controller.Users', { |
|
* extend: 'Ext.app.Controller', |
|
* |
|
* listen: { |
|
* controller: { |
|
* '#foo': { |
|
* bar: 'onFooBar' |
|
* } |
|
* } |
|
* } |
|
* }); |
|
* |
|
* To listen to "bar" events fired by any controller, and "baz" events |
|
* fired by Store with storeId="baz": |
|
* |
|
* Ext.define('AM.controller.Users', { |
|
* extend: 'Ext.app.Controller', |
|
* |
|
* listen: { |
|
* controller: { |
|
* '*': { |
|
* bar: 'onAnyControllerBar' |
|
* } |
|
* }, |
|
* store: { |
|
* '#baz': { |
|
* baz: 'onStoreBaz' |
|
* } |
|
* } |
|
* } |
|
* }); |
|
* |
|
* To listen to "idle" events fired by {@link Ext.GlobalEvents} when other event |
|
* processing is complete and Ext JS is about to return control to the browser: |
|
* |
|
* Ext.define('AM.controller.Users', { |
|
* extend: 'Ext.app.Controller', |
|
* |
|
* listen: { |
|
* global: { // Global events are always fired |
|
* idle: 'onIdle' // from the same object, so there |
|
* } // are no selectors |
|
* } |
|
* }); |
|
* |
|
* As this relates to components, the following example: |
|
* |
|
* Ext.define('AM.controller.Users', { |
|
* extend: 'Ext.app.Controller', |
|
* |
|
* listen: { |
|
* component: { |
|
* 'useredit button[action=save]': { |
|
* click: 'updateUser' |
|
* } |
|
* } |
|
* } |
|
* }); |
|
* |
|
* Is equivalent to: |
|
* |
|
* Ext.define('AM.controller.Users', { |
|
* extend: 'Ext.app.Controller', |
|
* |
|
* control: { |
|
* 'useredit button[action=save]': { |
|
* click: 'updateUser' |
|
* } |
|
* } |
|
* }); |
|
* |
|
* Of course, these can all be combined in a single call and used instead of |
|
* `control`, like so: |
|
* |
|
* Ext.define('AM.controller.Users', { |
|
* extend: 'Ext.app.Controller', |
|
* |
|
* listen: { |
|
* global: { |
|
* idle: 'onIdle' |
|
* }, |
|
* controller: { |
|
* '*': { |
|
* foobar: 'onAnyFooBar' |
|
* }, |
|
* '#foo': { |
|
* bar: 'onFooBar' |
|
* } |
|
* }, |
|
* component: { |
|
* 'useredit button[action=save]': { |
|
* click: 'updateUser' |
|
* } |
|
* }, |
|
* store: { |
|
* '#qux': { |
|
* load: 'onQuxLoad' |
|
* } |
|
* } |
|
* } |
|
* }); |
|
*/ |
|
listen: null, |
|
|
|
/** |
|
* @cfg {Object} routes |
|
* @accessor |
|
* |
|
* An object of routes to handle hash changes. A route can be defined in a simple way: |
|
* |
|
* routes : { |
|
* 'foo/bar' : 'handleFoo', |
|
* 'user/:id' : 'showUser' |
|
* } |
|
* |
|
* Where the property is the hash (which can accept a parameter defined by a colon) and the value |
|
* is the method on the controller to execute. The parameters will get sent in the action method. |
|
* |
|
* At the application level, you can define a event that will be executed when no matching |
|
* routes are found. |
|
* |
|
* Ext.application({ |
|
* name: 'MyApp', |
|
* listen: { |
|
* controller: { |
|
* '#': { |
|
* unmatchedroute: 'onUnmatchedRoute' |
|
* } |
|
* } |
|
* }, |
|
* |
|
* onUnmatchedRoute: function(hash) { |
|
* console.log('Unmatched', hash); |
|
* // Do something... |
|
* } |
|
* }); |
|
* |
|
* There is also a complex means of defining a route where you can use a before action and even |
|
* specify your own RegEx for the parameter: |
|
* |
|
* routes : { |
|
* 'foo/bar' : { |
|
* action : 'handleFoo', |
|
* before : 'beforeHandleFoo' |
|
* }, |
|
* 'user/:id' : { |
|
* action : 'showUser', |
|
* before : 'beforeShowUser', |
|
* conditions : { |
|
* ':id' : '([0-9]+)' |
|
* } |
|
* } |
|
* } |
|
* |
|
* This will only match if the `id` parameter is a number. |
|
* |
|
* The before action allows you to cancel an action. Every before action will get passed an `action` argument with |
|
* a `resume` and `stop` methods as the last argument of the method and you *MUST* execute either method: |
|
* |
|
* beforeHandleFoo : function(action) { |
|
* //some logic here |
|
* |
|
* //this will allow the handleFoo action to be executed |
|
* action.resume(); |
|
* }, |
|
* handleFoo : function() { |
|
* //will get executed due to true being passed in callback in beforeHandleFoo |
|
* }, |
|
* beforeShowUser : function(id, action) { |
|
* //allows for async process like an Ajax |
|
* Ext.Ajax.request({ |
|
* url : 'foo.php', |
|
* success : function() { |
|
* //will not allow the showUser method to be executed but will continue other queued actions. |
|
* action.stop(); |
|
* }, |
|
* failure : function() { |
|
* //will not allow the showUser method to be executed and will not allow other queued actions to be executed. |
|
* action.stop(true); |
|
* } |
|
* }); |
|
* }, |
|
* showUser : function(id) { |
|
* //will not get executed due to false being passed in callback in beforeShowUser |
|
* } |
|
* |
|
* You *MUST* execute the `resume` or `stop` method on the `action` argument. Executing `action.resume();` will continue |
|
* the action, `action.stop();` will not allow the action to resume but will allow other queued actions to resume, |
|
* `action.stop(true);` will not allow the action and any other queued actions to resume. |
|
* |
|
* The default RegEx that will be used is `([%a-zA-Z0-9\\-\\_\\s,]+)` but you can specify any |
|
* that may suit what you need to accomplish. An example of an advanced condition may be to make |
|
* a parameter optional and case-insensitive: |
|
* |
|
* routes : { |
|
* 'user:id' : { |
|
* action : 'showUser', |
|
* before : 'beforeShowUser', |
|
* conditions : { |
|
* ':id' : '(?:(?:\/){1}([%a-z0-9_,\s\-]+))?' |
|
* } |
|
* } |
|
* } |
|
*/ |
|
routes : null, |
|
before : null |
|
}, |
|
|
|
/** |
|
* Creates new Controller. |
|
* |
|
* @param {Object} [config] Configuration object. |
|
*/ |
|
constructor: function(config) { |
|
var me = this; |
|
|
|
// In versions prior to 5.1, this constructor used to call the Ext.util.Observable |
|
// constructor (which applied the config properties directly to the instance) |
|
// AND it used to call initConfig as well. Since the constructor of |
|
// Ext.mixin.Observable calls initConfig, but does not apply the properties to |
|
// the instance, we do that here for backward compatibility. |
|
Ext.apply(me, config); |
|
// The control and listen properties are also methods so we need to delete them |
|
// from the instance after applying the config object. |
|
delete me.control; |
|
delete me.listen; |
|
|
|
me.eventbus = Ext.app.EventBus; |
|
|
|
//need to have eventbus property set before we initialize the config |
|
me.mixins.observable.constructor.call(me, config); |
|
// Assuming we haven't set this in updateControl or updateListen, force it here |
|
me.ensureId(); |
|
}, |
|
|
|
applyListen: function(listen) { |
|
if (Ext.isObject(listen)) { |
|
listen = Ext.clone(listen); |
|
} |
|
|
|
return listen; |
|
}, |
|
|
|
applyControl: function(control) { |
|
if (Ext.isObject(control)) { |
|
control = Ext.clone(control); |
|
} |
|
|
|
return control; |
|
}, |
|
|
|
/** |
|
* @param {Object} control The object to pass to the {@link #method-control} method |
|
* @private |
|
*/ |
|
updateControl: function(control) { |
|
this.ensureId(); |
|
if (control) { |
|
this.control(control); |
|
} |
|
}, |
|
|
|
/** |
|
* @param {Object} listen The object to pass to the {@link #method-listen} method |
|
* @private |
|
*/ |
|
updateListen: function(listen) { |
|
this.ensureId(); |
|
if (listen) { |
|
this.listen(listen); |
|
} |
|
}, |
|
|
|
/** |
|
* @param {Object} routes The routes to connect to the {@link Ext.app.route.Router} |
|
* @private |
|
*/ |
|
updateRoutes : function(routes) { |
|
if (routes) { |
|
var me = this, |
|
befores = me.getBefore() || {}, |
|
Router = Ext.app.route.Router, |
|
url, config, method; |
|
|
|
for (url in routes) { |
|
config = routes[url]; |
|
|
|
if (Ext.isString(config)) { |
|
config = { |
|
action : config |
|
}; |
|
} |
|
|
|
method = config.action; |
|
|
|
if (!config.before) { |
|
config.before = befores[method]; |
|
} |
|
//<debug> |
|
else if (befores[method]) { |
|
Ext.log.warn('You have a before method configured on a route ("' + url + '") and in the before object property also in the "' + |
|
me.self.getName() + '" controller. Will use the before method in the route and disregard the one in the before property.'); |
|
} |
|
//</debug> |
|
|
|
//connect the route config to the Router |
|
Router.connect(url, config, me); |
|
} |
|
} |
|
}, |
|
|
|
isActive: function() { |
|
return true; |
|
}, |
|
|
|
/** |
|
* Adds listeners to components selected via {@link Ext.ComponentQuery}. Accepts an |
|
* object containing component paths mapped to a hash of listener functions. |
|
* |
|
* In the following example the `updateUser` function is mapped to to the `click` |
|
* event on a button component, which is a child of the `useredit` component. |
|
* |
|
* Ext.define('AM.controller.Users', { |
|
* init: function() { |
|
* this.control({ |
|
* 'useredit button[action=save]': { |
|
* click: this.updateUser |
|
* } |
|
* }); |
|
* }, |
|
* |
|
* updateUser: function(button) { |
|
* console.log('clicked the Save button'); |
|
* } |
|
* }); |
|
* |
|
* Or alternatively one call `control` with two arguments: |
|
* |
|
* this.control('useredit button[action=save]', { |
|
* click: this.updateUser |
|
* }); |
|
* |
|
* See {@link Ext.ComponentQuery} for more information on component selectors. |
|
* |
|
* @param {String/Object} selectors If a String, the second argument is used as the |
|
* listeners, otherwise an object of selectors -> listeners is assumed |
|
* @param {Object} [listeners] Config for listeners. |
|
*/ |
|
control: function(selectors, listeners, controller) { |
|
var me = this, |
|
ctrl = controller, |
|
obj; |
|
|
|
if (Ext.isString(selectors)) { |
|
obj = {}; |
|
obj[selectors] = listeners; |
|
} |
|
else { |
|
obj = selectors; |
|
ctrl = listeners; |
|
} |
|
|
|
me.eventbus.control(obj, ctrl || me); |
|
}, |
|
|
|
/** |
|
* Adds listeners to different event sources (also called "event domains"). The |
|
* primary event domain is that of components, but there are also other event domains: |
|
* {@link Ext.app.domain.Global Global} domain that intercepts events fired from |
|
* {@link Ext.GlobalEvents} Observable instance, {@link Ext.app.domain.Controller Controller} |
|
* domain can be used to listen to events fired by other Controllers, |
|
* {@link Ext.app.domain.Store Store} domain gives access to Store events, and |
|
* {@link Ext.app.domain.Direct Direct} domain can be used with Ext.Direct Providers |
|
* to listen to their events. |
|
* |
|
* To listen to "bar" events fired by a controller with id="foo": |
|
* |
|
* Ext.define('AM.controller.Users', { |
|
* init: function() { |
|
* this.listen({ |
|
* controller: { |
|
* '#foo': { |
|
* bar: this.onFooBar |
|
* } |
|
* } |
|
* }); |
|
* }, |
|
* ... |
|
* }); |
|
* |
|
* To listen to "bar" events fired by any controller, and "baz" events |
|
* fired by Store with storeId="baz": |
|
* |
|
* Ext.define('AM.controller.Users', { |
|
* init: function() { |
|
* this.listen({ |
|
* controller: { |
|
* '*': { |
|
* bar: this.onAnyControllerBar |
|
* } |
|
* }, |
|
* store: { |
|
* '#baz': { |
|
* baz: this.onStoreBaz |
|
* } |
|
* } |
|
* }); |
|
* }, |
|
* ... |
|
* }); |
|
* |
|
* To listen to "idle" events fired by {@link Ext.GlobalEvents} when other event |
|
* processing is complete and Ext JS is about to return control to the browser: |
|
* |
|
* Ext.define('AM.controller.Users', { |
|
* init: function() { |
|
* this.listen({ |
|
* global: { // Global events are always fired |
|
* idle: this.onIdle // from the same object, so there |
|
* } // are no selectors |
|
* }); |
|
* } |
|
* }); |
|
* |
|
* As this relates to components, the following example: |
|
* |
|
* Ext.define('AM.controller.Users', { |
|
* init: function() { |
|
* this.listen({ |
|
* component: { |
|
* 'useredit button[action=save]': { |
|
* click: this.updateUser |
|
* } |
|
* } |
|
* }); |
|
* }, |
|
* ... |
|
* }); |
|
* |
|
* Is equivalent to: |
|
* |
|
* Ext.define('AM.controller.Users', { |
|
* init: function() { |
|
* this.control({ |
|
* 'useredit button[action=save]': { |
|
* click: this.updateUser |
|
* } |
|
* }); |
|
* }, |
|
* ... |
|
* }); |
|
* |
|
* Of course, these can all be combined in a single call and used instead of |
|
* `control`, like so: |
|
* |
|
* Ext.define('AM.controller.Users', { |
|
* init: function() { |
|
* this.listen({ |
|
* global: { |
|
* idle: this.onIdle |
|
* }, |
|
* controller: { |
|
* '*': { |
|
* foobar: this.onAnyFooBar |
|
* }, |
|
* '#foo': { |
|
* bar: this.onFooBar |
|
* } |
|
* }, |
|
* component: { |
|
* 'useredit button[action=save]': { |
|
* click: this.updateUser |
|
* } |
|
* }, |
|
* store: { |
|
* '#qux': { |
|
* load: this.onQuxLoad |
|
* } |
|
* } |
|
* }); |
|
* }, |
|
* ... |
|
* }); |
|
* |
|
* @param {Object} to Config object containing domains, selectors and listeners. |
|
* @param {Ext.app.Controller} [controller] The controller to add the listeners to. Defaults to the current controller. |
|
*/ |
|
listen: function (to, controller) { |
|
this.eventbus.listen(to, controller || this); |
|
}, |
|
|
|
destroy: function() { |
|
var me = this, |
|
bus = me.eventbus; |
|
|
|
Ext.app.route.Router.disconnectAll(me); |
|
|
|
if (bus) { |
|
bus.unlisten(me); |
|
me.eventbus = null; |
|
} |
|
me.clearListeners(); |
|
me.callParent(); |
|
}, |
|
|
|
/** |
|
* Update the hash. By default, it will not execute the routes if the current token and the |
|
* token passed are the same. |
|
* |
|
* @param {String/Ext.data.Model} token The token to redirect to. Can be either a String |
|
* or a {@link Ext.data.Model Model} instance - if a Model instance is passed it will |
|
* internally be converted into a String token by calling the Model's |
|
* {@link Ext.data.Model#toUrl toUrl} function. |
|
* |
|
* @param {Boolean} force Force the update of the hash regardless of the current token. |
|
* |
|
* @return {Boolean} Will return `true` if the token was updated. |
|
*/ |
|
redirectTo: function(token, force) { |
|
if (token.isModel) { |
|
token = token.toUrl(); |
|
} |
|
if (!force) { |
|
var currentToken = Ext.util.History.getToken(); |
|
|
|
if (currentToken === token) { |
|
return false; |
|
} |
|
} else { |
|
Ext.app.route.Router.onStateChange(token); |
|
} |
|
|
|
Ext.util.History.add(token); |
|
|
|
return true; |
|
} |
|
});
|
|
|