/**
* 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 view controller is specified on a view, events and other handlers that use strings as
* values will be automatically connected with the appropriate methods in the controller's class.
*
* Sample usage:
*
* @example
* Ext.define('MyViewController', {
* extend : 'Ext.app.ViewController',
* alias: 'controller.myview',
*
* // This method is called as a "handler" for the Add button in our view
* onAddClick: function() {
* Ext.Msg.alert('Add', 'The Add button was clicked');
* }
* });
*
* Ext.define('MyView', {
* extend: 'Ext.Panel',
* controller: 'myview',
*
* items: [{
* xtype: 'button',
* text: 'Add',
* handler: 'onAddClick', // calls MyViewController's onAddClick method
* }]
* });
*
* Ext.onReady(function() {
* new MyView({
* 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,
/**
* @property factoryConfig
* @inheritdoc
*/
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 6.5.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 beforeInit
*
* 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 init
*
* 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 initViewModel
*
* 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;
},
/**
* @method getReferences
* @inheritdoc Ext.mixin.Container#method!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;
}
/* eslint-disable-next-line vars-on-top */
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);
}
}
}
});