/** * A view controller is a controller that can be attached to a specific view * instance so it can manage the view and its child components. Each instance of the view * will have a new view controller, so the instances are isolated. * * When a controller is specified on a view, the view automatically becomes a {@link Ext.container.Container#referenceHolder}, * so it will receive inline events declared on the view. Sample usage: * * @example * Ext.define('User', { * extend: 'Ext.data.Model', * fields: ['name', 'phone'] * }); * * Ext.define('UserListController', { * extend : 'Ext.app.ViewController', * alias: 'controller.userlist', * * init: function(view) { * this.userCount = 0; * var users = [], * i; * * for (i = 0; i < 5; ++i) { * users.push(this.getUser()); * } * view.getStore().add(users); * }, * * onAddClick: function() { * this.addUser(); * }, * * onDeleteClick: function() { * var view = this.getView(), * selected = view.getSelectionModel().getSelection()[0], * store = view.getStore(); * * store.remove(selected); * }, * * onSelectionChange: function(selModel, selections) { * this.lookupReference('delete').setDisabled(selections.length === 0); * }, * * getUser: function() { * ++this.userCount; * return { * name: 'User ' + this.userCount, * phone: this.generatePhone() * }; * }, * * addUser: function() { * this.getView().getStore().add(this.getUser()); * }, * * generatePhone: function() { * var num = '', * i; * * for (i = 0; i < 7; ++i) { * num += Ext.Number.randomInt(0, 9); * if (num.length === 3) { * num += '-'; * } * } * return num; * } * }); * * Ext.define('UserList', { * extend: 'Ext.grid.Panel', * controller: 'userlist', * * tbar: [{ * text: 'Add', * listeners: { * click: 'onAddClick' * } * }, { * text: 'Delete', * reference: 'delete', * listeners: { * click: 'onDeleteClick' * } * }], * store: { * model: 'User' * }, * selModel: { * type: 'rowmodel', * listeners: { * selectionchange: 'onSelectionChange' * } * }, * columns: [{ * flex: 1, * dataIndex: 'name', * text: 'Name' * }, { * flex: 1, * dataIndex: 'phone', * text: 'Phone' * }] * }); * * Ext.onReady(function() { * new UserList({ * renderTo: Ext.getBody(), * width: 400, * height: 200 * }); * }); */Ext.define('Ext.app.ViewController', { extend: 'Ext.app.BaseController', alias: 'controller.controller', requires: [ 'Ext.app.domain.View' ], mixins: [ 'Ext.mixin.Factoryable' ], isViewController: true, factoryConfig: { // configure Factoryable type: 'controller' }, config: { /** * @cfg {Object} bindings * A declarative set of bindings to the {@link #getViewModel} for this * controller. The key should be the method, the value should be * the bind statement: * * Ext.define('MyApp.TestController', { * extend: 'Ext.app.ViewController', * * bindings: { * onTotalChange: '{total}', * onCoordsChange: '({x}, {y})', * onProductChange: { * amount: '{qty}', * rating: '{rating}' * } * }, * * onTotalChange: function(total) { * console.log(total); * }, * * onCoordsChange: function(coords) { * console.log('The coordinates are: ', coords); * }, * * onProductChange: function(productInfo) { * console.log('Amount: ', productInfo.amount, ' Rating: ', productInfo.rating); * } * }); * * @since 7.0.0 */ bindings: { $value: null, lazy: true }, closeViewAction: 'destroy' }, view: null, constructor: function(config) { this.compDomain = new Ext.app.domain.View(this); this.callParent([config]); }, /** * @method * * Called before the view initializes. This is called before the view's * initComponent method has been called. * @param {Ext.Component} view The view * @protected */ beforeInit: Ext.emptyFn, /** * @method * * Called when the view initializes. This is called after the view's initComponent * method has been called. * @param {Ext.Component} view The view * @protected */ init: Ext.emptyFn, /** * @method * * Called when the view model instance for an attached view is first created. * @param {Ext.app.ViewModel} viewModel The ViewModel * @protected */ initViewModel: Ext.emptyFn, /** * Destroy the view controller. */ destroy: function() { var me = this, domain = me.compDomain, bind, b, key; if (me.$hasBinds) { bind = me.getBindings(); for (key in bind) { b = bind[key]; if (b) { b.destroy(); } } } if (domain) { domain.unlisten(me); domain.destroy(); } me.compDomain = me.view = null; me.callParent(); }, /** * This method closes the associated view. The manner in which this is done (that is, * the method called to close the view) is specified by `closeViewAction`. * * It is common for views to map one or more events to this method to allow the view * to be closed. */ closeView: function () { var view = this.getView(), action; if (view) { action = this.getCloseViewAction(); view[action](); } }, control: function(selectors, listeners) { var obj = selectors; if (Ext.isString(selectors)) { obj = {}; obj[selectors] = listeners; } this.compDomain.listen(obj, this); }, listen: function(to, controller) { var component = to.component; if (component) { to = Ext.apply({}, to); delete to.component; this.control(component); } this.callParent([to, controller]); }, applyId: function(id) { if (!id) { id = Ext.id(null, 'controller-'); } return id; }, /** * @inheritdoc Ext.container.Container#getReferences * @since 5.0.0 */ getReferences: function () { var view = this.view; return view && view.getReferences(); }, /** * Get the view for this controller. * @return {Ext.Component} The view. */ getView: function() { return this.view; }, /** * Gets a reference to the component with the specified {@link Ext.Component#reference} * value. * * The method is a short-hand for the {@link #lookupReference} method. * * @param {String} key The name of the reference to lookup. * @return {Ext.Component} The component, `null` if the reference doesn't exist. * @since 6.0.1 */ lookup: function (key) { var view = this.view; return view && view.lookup(key); }, /** * Gets a reference to the component with the specified {@link Ext.Component#reference} * value. * * The {@link #lookup} method is a short-hand version of this method. * * @param {String} key The name of the reference to lookup. * @return {Ext.Component} The component, `null` if the reference doesn't exist. * @since 5.0.0 */ lookupReference: function (key) { return this.lookup(key); }, /** * Get a {@link Ext.data.Session} attached to the view for this controller. * See {@link Ext.Component#lookupSession}. * * @return {Ext.data.Session} The session. `null` if no session is found. * * @since 5.0.0 */ getSession: function () { var view = this.view; return view && view.lookupSession(); }, /** * Get a {@link Ext.app.ViewModel} attached to the view for this controller. * See {@link Ext.Component#lookupViewModel}. * * @return {Ext.app.ViewModel} The ViewModel. `null` if no ViewModel is found. * * @since 5.0.0 */ getViewModel: function () { var view = this.view; return view && view.lookupViewModel(); }, /** * Get a {@link Ext.data.Store} attached to the {@link #getViewModel ViewModel} attached to * this controller. See {@link Ext.app.ViewModel#getStore}. * @param {String} name The name of the store. * @return {Ext.data.Store} The store. `null` if no store is found, or there is no * {@link Ext.app.ViewModel} attached to the view for this controller. * * @since 5.0.0 */ getStore: function(name) { var viewModel = this.getViewModel(); return viewModel ? viewModel.getStore(name) : null; }, /** * Fires an event on the view. See {@link Ext.Component#fireEvent}. * @param {String} eventName The name of the event to fire. * @param {Object...} args Variable number of parameters are passed to handlers. * @return {Boolean} returns false if any of the handlers return false otherwise it returns true. * @protected */ fireViewEvent: function (eventName, args) { var view = this.view, result = false, a = arguments; if (view) { if (view !== args) { a = Ext.Array.slice(a); a.splice(1, 0, view); // insert view at [1] } result = view.fireEvent.apply(view, a); } return result; }, /** * @method setBind * @hide */ applyBindings: function(bindings) { if (!bindings) { return null; } var me = this, viewModel = me.getViewModel(), getBindTemplateScope = me.getBindTemplateScope(), b, fn, descriptor; me.$hasBinds = true; //<debug> if (!viewModel) { Ext.raise('Cannot use bind config without a viewModel'); } //</debug> for (fn in bindings) { descriptor = bindings[fn]; b = null; if (descriptor) { b = viewModel.bind(descriptor, fn, me); b.getTemplateScope = getBindTemplateScope; } bindings[fn] = b; } return bindings; }, //========================================================================= privates: { view: null, /** * Set a reference to a component. * @param {Ext.Component} component The component to reference * @private */ attachReference: function (component) { var view = this.view; if (view) { view.attachReference(component); } }, getBindTemplateScope: function() { // This method is called as a method on a Binding instance, so the "this" pointer // is that of the Binding. The "scope" of the Binding is the controller. return this.scope; }, initBindings: function() { // Force bind creation this.getBindings(); }, /** * Sets the view for this controller. To be called by the view * when it initializes. * @param {Object} view The view. * * @private */ setView: function(view) { this.view = view; if (!this.beforeInit.$nullFn) { this.beforeInit(view); } } }});