plugin_controller_post

Created: 2014-05-19 03:20
Updated: 2014-05-20 18:04

README.md

Using plugins as controllers for Ext.Components

Rally publishes a public javascript AppSDK to enable customers with the ability to easily access their user story data within custom applications. There must be a clear separation between view code and model code within the SDK in order to make the SDK components more reusable in different contexts and provide easier application development.

ExtJs with MVC

First off, "MVC" in this post refers to unopinionated, generic MVC. View == UI, Model == application logic, Controller == glue between View and Model. "Component" refers to a class that extends Ext.Component.

There are 2 popular ways to implement MVC with ExtJs applications. Sencha provides an MVC implementation distributed with ExtJs and DeftJs is a third-party library. I have experimented with both of these MVC implementations, and found them lacking.

Sencha's built-in MVC implementation requires a specific directory structure and configuration with Ext.application. These restrictions make it difficult to adapt the pattern to existing code bases.

Sencha's controllers also encourage code that listens for events fired from a Container's child Components. This is a code smell. Controller code should never "know" about child Components, because refactorring the UI structure will break the controller. Replacing a text input with a slider shouldn't force you to change controller code.

DeftJs is easier to adapt to existing code, but there are some odd object life-cycle issues that make it difficult to work with. Components must mixin Deft's Controllable class and DeftJs controllers must be configured when a Component is defined, rather than when it is instantiated. If you find the need to re-use a Component with two different controllers, you will need to create two separate classes, each defined with a different DeftJS controller.

A Deft controller's init method is not called until after the Component has been rendered, which makes things rather challenging if the controller needs to hook into any pre-render events such as staterestore or render.

Plugins as Controllers

I've found that standard Ext Plugins are a great alternative to Sencha and DeftJs controllers. Plugins fit nicely into the Component life cycle. They can be configured at instantiation time, so reusing the same UI with different behavior is trivial.

Multiple Plugins can be configured for a single Component. This may seem like an odd property for a controller, but it's actually quite nice to split code into multiple controllers for Components that have many different behaviors.

A Plugin's init method is called during the Component's constructor call, so it can respond to any Component events fired during or after the Component's initComponent call. A Plugin's destroy method is called during the Component's destroy call, which allows for easy cleanup of listeners and models orchestrated by the Plugin.

So how does it Work?

Wire up controller Plugins just like any other Plugin.

	/**
	 * Controller Plugin
	 */
	Ext.define('MyViewController', {
		extend: 'Ext.AbstractPlugin',
		alias: 'plugin.myviewcontroller',

		/**
		 * @cfg {Ext.data.Store}
		 */
		store: null,

		init: function(cmp) {
			// add listeners to respond to UI events
			cmp.on('save', this.onSave);
		},

		onSave: function(cmp, data) {
			// respond to UI events by calling public view and model methods
			cmp.setLoading(true);
			this.store.add(new this.store.model(data))
			this.store.sync({
				success: function() {
					cmp.setLoading(false);
					cmp.showNotification('Record Saved');
				}
			});
		}	
	});
	/**
	 * View Component
	 */
	Ext.define('MyView', {
		extend: 'Ext.Component',
		alias: 'widget.myview',

		items: [{
			xtype: 'button',
			label: 'Save'
		}],

		initComponent: function() {
			this.callParent(arguments);

			this.addEvents(['save']);

			// button click is abstracted into semantic event to avoid
			// controller having to know UI implementation details
			this.down('button').on('click', function() {
				this.fireEvent('save', {...});
			});
		},

		showNotification: ...
	});
	// instantiate view with controller configuration
	var cmp = container.add('myview', {
		plugins: [{
			ptype: 'myviewcontroller',
			store: myStore
		}]
	});

Components architected with a plugin controller like the example above become easier to maintain and easier to consume. This method of adding controllers to Components is relatively painless to adapt to existing code. We are actively refactorring legacy code within Rally's SDK to use this architecture.

Cookies help us deliver our services. By using our services, you agree to our use of cookies Learn more