Introducing React ReExt – Sencha Ext JS Components in React! LEARN MORE

Implementing User Extensions for Sencha Architect 3.0 Preview

August 14, 2013 107 Views
Show

This article discusses some specific technical challenges that we faced and overcame during the development of the User Extensions feature in Architect 3.

A Brief Introduction To Sencha Architect

First, a little background: Sencha Architect is a visual tool for building HTML5 applications using Ext JS and Sencha Touch frameworks. Three of our main goals for Architect are: to be the best way to build a Sencha app; to generate clean code exhibiting the best practices for using our frameworks; and to be enable developers and designers to work together closely.

Architect 3 advances all of these goals, as you can see in the Architect 3 preview which we first announced at SenchaCon 2013 and then released as a public preview in early August. The major new features we introduced in the preview are: Application Templates and User Extensions, which strongly address our first two goals; and Theming Support, which addresses the third goal.

Theming Support is all about empowering designers to work alongside developers to create visually appealing applications that match a company’s style guidelines and align with their sense of style. Too often the feedback loop between designers and developers takes too long. We knew we could improve this process.

Theming
Templates
Extensions

We wanted to create a single tool that allowed each of these parties to collaborate and contribute in their area of expertise. Application Templates and User Extensions advance the first two goals by increasing the reach of Architect and helping to ensure good code generation. We wanted to make sure that the code generated from the design view is very clean and looks like an advanced developer may have written it. We have had experiences working with many tools that generate unreadable code, and we know it doesn’t have to be this way. The basic idea of User Extensions is to bring code created elsewhere into Architect in an easy and useful way while maintaining full design-time support. The core idea of Application Templates is to allow the user to select from common application patterns when creating a new project. Note that the preview build doesn’t yet allow creation of user defined Application Templates, but the final 3.0 version will. All of these features have been requested by users, and we’re pleased to deliver them.

Our Experience Implementing User Extensions

Adding support for User Extensions proved to be quite a challenge for the Sencha Architect architecture, and we hope you learn from our experience.

Stepping back for a moment: the entire Architect user interface is an HTML5 application built using Ext JS. We distribute Architect as a binary executable/installer that we have wrapped up with Sencha Desktop Packager, our own special version of Chromium which adds in APIs to access native functionality. Some of the native system functionality we use is: reading, writing, and deleting files from the filesystem and manipulating the native window menu. By actually using a browser to render our controls, we are able to provide the rich fidelity that many design tools lack. And because we know the application will be running in a specific browser, we can leverage all of the latest HTML5 features that are available.

We distribute Architect as a binary executable/installer that we have wrapped up with Sencha Desktop Packager, our own special version of Chromium which adds in APIs to access native functionality.”

Getting back to our experience of enabling user extensions in Architect, we needed to run user-provided code within the application. This user code could literally be anything. We knew this code would have to be sandboxed from our privileged APIs. Imagine installing a user extension that suddenly deleted files in your home directory!

In order to isolate the code which is running in the design view (which we call the canvas), we used an iframe along with the HTML5 sandbox attribute. The sandbox attribute enables users to configure how the iframe will behave. In our case, we set it to allow-scripts, so JavaScript can execute within the iframe. Check out this section of HTML5 Rocks which explains the granular controls provided by the sandbox attribute. By default, when setting the sandbox attribute, the iframe cannot reach out to the parent or top level frames. This allows us to load code into the canvas iframe and disallow access to our privileged APIs. In our application, the frames still need to communicate in some way because they will not be able to access any properties stored on the frame. We are using the HTML5 standard Web Messaging (or cross-document messaging) to communicate back and forth via window.postMessage. We created a CrossWindowMessaging class to abstract all of the communication between the frames.

The CrossWindowMessaging class allowed us to easily do eventing between frames while still supporting most of the features available in Ext.util.Observable. Below is brief glimpse at the interface by way of code:

// first we setup the messaging interface inside each document
// remember these are separate documents and have been sandboxed
// so that you cannot access the JS properties or DOM content
// within them

// Execute the following code in the containing outer document
var outerMessaging = Ext.create(‘sencha.CrossWindowMessaging’, {
targetWindow: iframe.contentWindow
});

// Execute the following code in the inner document (iframe)
var innerMessaging = Ext.create(‘sencha.CrossWindowMessaging’, {
targetWindow: window.parent
});

// subscribe to the events occurring within the iframe document
outerMessaging.on(‘myevent’, myEventHandler, this, {buffer: 100});

// notify the zero or many subscribers that an event has occurred
innerMessaging.notify(‘myevent’, ‘arg1’, ‘arg2’);
innerMessaging.notify(‘myevent’, ‘arg1’, ‘arg2’);

The messaging class also allowed us to invoke functions between the two frames asynchronously.

// Implement a method that can be invoked across the frames
outerMessaging.implement(‘myMethod’, function(arg1, arg2) {
return ‘myReturnValue’;
});

// Invoke a method across the frames
var promise = innerMessaging.call(‘myMethod’, ‘arg1’, ‘arg2’);
promise.then(function(value) {
// value will equal ‘myReturnValue’ as seen in the implementation

});

Because this approach made all of our previously synchronous APIs become asynchronous, we quickly ran into deeply nested sets of function callbacks. This required a shift in our development paradigm, so we began returning promises from all of our cross frame method invocations. We encountered this issue at about the same time as the Sencha Touch team. Interestingly, each of our teams independently chose to use promises, and these implementations should see a convergence in the near future. Promises make it easy to express all of the necessary steps required in a very procedural manner when you need to take the return values from a number of asynchronous calls and do something with them in a series. Anyone who has done a bit of development with Node.js will recognize this problem which quickly surfaces when doing a lot of asynchronous development.

Below is a small snippet of code from Architect that illustrates the use of promises.

return me.promiseAll(xds.DefaultStores.map(function(storeConfig) {
return me.messaging.call(
‘registerStore’,
‘defaultstore:’ + storeConfig.storeId,
me.encodeCanvasConfig(storeConfig)
);
}))
.then(function() {
// Register all user models
return xds.app.manager.Model.updateAllCanvasInstances();
})
.then(function() {
// Register all user stores
return xds.app.manager.Store.updateAllCanvasInstances();
})
.then(function() {
me.ready = true;
// If an activeInstance was set before the framework loaded, force it to get rendered
if (me.activeInstance) {
me.setInstance(me.activeInstance, true);
}
}, function(ex) {
// Re-throw exceptions
throw ex;
});

Imagine what this would look like if we used nested callbacks for each one of these function calls. It would be completely unmanageable! Promises has helped us clean up our code base and solve the problem of organizing our asynchronous calls which constantly go back and forth between the iframes.

We hope that this insight into the challenges we encountered while developing User Extensions in Architect can help you in your own projects.

Lastly, we know that there is a rich community of users that has developed user extensions for Ext JS and Touch. We would love if you followed the Creating User Extensions and Integrating User Extensions guides to make your user extensions available as .aux files (Architect User Extension) and share them with the community. Please provide any feedback that you may have with us at the Architect 3.0 Forum.