Sencha app development Best Practices at Innofied

Swarnendu De November 11, 2015

You will find lots of Sencha tutorials all over the web but only a handful of them will actually show you how to follow specific guidelines, while developing a medium level Sencha project. It is a common experience for me to witness Sencha Touch projects from different programmers, without any planning and architecture in code. The resultant output has an absolutely unmanageable code base. So, I am going to list some simple Sencha Touch coding guidelines, which will greatly help you manage your Sencha app development.

1. Write all your tpl or itemTpl in index.html:

It’s more preferable that you write the templates inside the index.html, within a script tag rather than writing within the javascript file. That will help you easily maintain your templates.

In almost all the examples and tutorials you will see people add tpl or itemTpl with lots of html tags as strings. It becomes very irritating to debug, change and format. This is because most of us use IDEs and putting html inside Javascript files makes them impossible to handle.

{

  xtype: 'container',

  tpl: Ext.create(
    'Ext.XTemplate',

    '<div>',

    '<div>Welcome</div>',

    '<div>Name : {first_name} {last_name}</div>',

    '<div>Age : {age}</div>',

    '</div>'

  )
}

Here you see that if the template string grows up it will be very hard for maintaining and reading, as maintaining indentation is very tough.

Instead of that you can write your template, within the index.html as follows:

Templates inside index.html :
<!-- here goes the container tpl in index.html file -->
<script type="text/template" id="tpl_container">
  <div>
    <div>Welcome</div>
    <div>Name : {first_name} {last_name}</div>
    <div>Age : {age}</div>
  </div>
</script>

Also, we have to use the template as:

{

  xtype: 'container',
  tpl: Ext.create('Ext.XTemplate’, 
                  document.getElementById(tpl_container).innerHTML)
}

 

2.  Always have a Utility file

It is recommended to always maintain a Utility file in your application. Here we can manage all general information like API url details, function to handle errors, showing alert messages, and other common functions that are used in many places within your application. You can make it a singleton class and put it in a separate “util” folder under “app”.

app-architecture

Now define the Util.js file

Ext.define('App.util.Util', {
  singleton: true
  // Other properties and methods here
});

 

3. Create a generic error handling function

Often our clients ask to change the display of error messages or the notifications in the app, than using the default Sencha message. Sometime it is required to add some good style or animation which may make the messages look more aligned to the application standard and design. Previously, we would mostly find that there were tons of additional error messages in our controller and had to apply changes to solve them midway. This makes it time consuming and frustrating.

Keeping that in mind, it is absolutely recommended that you create a generic error display method which will help you, in case the requirement changes and you need to implement the new functionality in one place. At Innofied, we encourage our developers to add similar method in the Utility file and display all the notifications from the same. Let’s look into a very basic error message handler.

/*

* Show error message.

* @param {String} msg The error message to show.

* @param {String} title The title text to show.

* @param {Function} cb The callback function to execute after pressing OK button.

* @param {Object} scope Scope of the callback function.

*/
showError: function (msg, title, cb, scope) {

  // Here we are using sencha’s message alert function.

  // you can implement your custom way of showing error message.
  if (msg) {
    Ext.Msg.alert(title || 'Error', msg.toString(), cb || function () {}, scope || window);
  }
}

msg is the mandatory parameter here. The title of the alert box will be “Error” by default, which is replaced if a title is passed. The callback function will be called once user presses the ok button along with window scope, in case scope is not passed to the function. You can always change the alert functionality with your custom implementation.

4. Animate and activate containers from a common function:

We at Innofied prefer to manage handling all the page transitions and activation from a common function inside Utility class. The benefits are quite high:

  1. All the transitions are managed from a single place. So, if I find a particular platform or device not supporting the animations well, I can set it off easily.

  2. Default sencha transition is ‘Slide’. You can change it to ‘Fade’ or any other by modifying it here, not having to go through all the setActiveItem functions.

  3. Maintain animation duration.

  4. Reuse code.

Take a look at the function showActiveItem below, something I found to be helpful for large navigational apps.

// Add these properties and the method in your Utility class
// Type of animation
animationType: 'slide',

// Default animation duration is 250ms. You can change it here

animationDuration: 100,

// Turn ON or OFF the transitions

enablePageAnimations: true,

// Method to activate a panel

showActiveItem: function (parentPanel, childPanel, animation) {

  animation = Ext.apply({

    type: this.animationType,

    duration: this.animationDuration

  }, animation || {});

  if (parentPanel && childPanel) {

    // enablePageAnimations property of Utility says whether to turn ON or OFF the transition

    if (this.enablePageAnimations) {

      parentPanel.animateActiveItem(childPanel, animation);

    }
    else {

      parentPanel.setActiveItem(childPanel);

    }

  }

  return this;

}

Now use this function in your controller like this:

var panel = new Ext.Container({

  html: 'Hellow World!'

});

this.getMain().add(panel);
App.util.Utility.showActiveItem(this.getMain(), panel);

 

5. Access all APIs from a single property

Most developers write proxy urls or ajax urls directly in the ‘Store’ or ‘Controller’ file as a String. It helps a lot if you maintain all the api urls in Utility and use the reference directly. Take a look below:

api property in utility class :

// A self executing function to return an object with all the api urls
api: (function () {

  // This is the base API url

  var baseUrl = 'http://www.example.com/api/';

  return {
    login: baseUrl + 'login',

    signup: baseUrl + 'signup'

  }
})()

Here api is an self executing function added in Util to return complete set of all the API urls and you can access it like this:

App.util.Util.api.login      // returns "http://www.example.com/api/login"

This way if ever you need make any changes to api details, you will never need to search within your app files – Utility is the one stop address for all the urls.

You can also achieve the same by setting get/set method for your api urls, as follows:

Ext.define('App.util.Util', {

  singleton: true,

  config: {

    baseURL: 'http://www.example.com/api',

    apiUrls: {

      login: '/login',

      signup: '/signup'

    }

  },

  constructor: function (config) {

    // Creates the getter/setter wrapper

    this.initConfig(config);

    this.callParent(arguments);

  },

  /**

    * Simple function which returns the required api url depending on the passed api name

    */

  getAPI: function (apiName) {

    if (!apiName) {
      return;
    }

    return this.getBaseURL() + this.getApiUrls()[apiName];

  }
});

Now to get an api:

App.util.Util.getAPI('login') // returns "http://www.example.com/api/login"

You can easily set the base url as shown below (if required):

// Set the base url like this
App.util.Util.setBaseURL('http://www.google.com');

 

6. Avoiding Monster/Large Controllers or Follow Modular Controllers:

Sometimes we get to see a single controller backing thousands of lines of code. This practice is very bad when it comes to debugging the code, finding a particular function or updating the functionalities.

We have included modularising controllers in Sencha’s Best Practices because maintenance is a vital part of development. Also, if the functionalities are not properly managed in separate manageable modules then at the end of the development it will be hard to find or update different sections.

For managing controllers there are two patterns:

1. Some developers prefer to have a separate controller file for each view.

2. Others combine similar views with some similar functions in a single controller file. In this approach controllers are grouped by functionalities.

We can follow any or both patterns.

Sencha facilitates inter-controller communication such as:

this.getController('SomeOtherController').runSomeFunction(myParam);

So we don’t need to worry about being unable to call the other controller’s function, with current parameter. It can be done either as shown above or by calling an app level event, letting another controller listen for it:

MyApp.getApplication().fireEvent('myevent');

7. Create mixins where extension is not a good fit:

While developing apps, developers have to face one problem quite a lot i.e. good fit. Using ‘theming’, Sencha Touch gives us the flexibility to create ‘mixins’ to fix the problem of good fit. Themes in Sencha Touch are a powerful way to quickly change the overall look and feel of your application. Each component has its own mixins and variables for controlling styles. This means you can override these variables or use mixins to customise your own theme.

The first thing we need to cover is a basic overview of the tools used by Sencha Touch, that make theming your application possible: SASS and Compass.

SASS + Compass = themes

Sencha Touch themes take SASS and Compass one step further, providing variables and mixins whose functionality is specific to Sencha Touch. The JavaScript portion of Sencha Touch generates highly complex HTML, in order to display the various components, such as toolbars and panels. Rather than having to learn all of the intricate classes and HTML tricks used by Sencha Touch, you can simply use the appropriate mixins to change the appearance of your application.

You can also use mixins to create additional options for the UI configuration option (beyond the simple light and dark values that we have seen previously). For example, we can modify the color of our toolbar by adding a new mixin to our customTheme.sass file.

In your customTheme.sass file, locate the line that says the following:

@import 'sencha-touch/default/all';

@include sencha-toolbar-ui('subnav', #625546, 'matte');

This code tells SASS to create a new UI option for the toolbar. Our new option will be called subnav, and it will have a base color of #625546. The last option sets the style for the gradient.

Once you have saved the file, you will need to compile the stylesheet, using the compass compile command on the command line.

8. Memory leaks caused by the inability to cleanup unused components:

Some of the apps become slower & slower with prolonged use. The main reason behind this is the failure to cleanup unused components as a user navigates throughout the app, from a view to another.

In example 1 below, each time the user taps on a list item, a new panel is created. If the user taps the list items hundred times, all of them will be created but none of them will be destroyed. The app looks visually correct because only the last created panel is visible, while the others are hidden. As a new panel is created without destroying the old panel, the memory utilization for the app will be increased with the creation of each panel. This will lead to slower operation or a browser crash.

Example 2 is better because the panel is created once when the list is initialized and also reused every time the user taps on a list item. The panel is destroyed whenever the list is destroyed.

Ext.define('MyApp.view.UserList', {

  extend: 'Ext.dataview.DataView',

  alias: 'widget.userlist',

  config: {

    itemTpl: '<div>{name} is {age} years old</div>',

    store: aroundStore,

    listeners: {

      itemtap: function (list, index, target, record, e, eOpts) {

        var query = Ext.ComponentQuery.query('panel[name="pop_up"]');

        if (query.length > 0)

        {

          for (var i = 0; i < query.length; i++)

          {

            query[i].hide();

          }

        }

        var panel = Ext.create('Ext.Panel', {

          name: 'pop_up',

          html: record.getData().name

        });

        Ext.Viewport.add(panel);

        panel.show();

      }

    },

  }
});

Example 1 BAD: A Panel will be created on every tap and never be destroyed.

Ext.define('MyApp.view.UserList', {

  extend: 'Ext.dataview.DataView',

  alias: 'widget.userlist',

  config: {

    itemTpl: '<div>{name} is {age} years old</div>',

    store: aroundStore,

    listeners: {

      activate: function () {

        this.panel = Ext.create('Ext.Panel');

        Ext.Viewport.add(this.panel);

      },

      itemtap: function (a, index, target, record, e, eOpts) {

        this.panel.setHtml(record.getData().title);

        this.panel.show();

      }

      destroy: function () {

        this.panel.destroy();

      }

    }

  }

});

Example 2. BEST:  Panel will be created when the list is created and be reused each time. When the list is destroyed, so is the panel.

Conclusion

There are many developers around the world, working on Sencha app development projects. Along with writing the code we should make sure to keep it well structured and maintainable. If you follow the guidelines mentioned in this post, it will help us to write a better code which is easy to understand and reuse as well. Hope this post helps you to structure and manage your code in a better way.