EnyoJS 2.7 Tutorial : Controls & Modules

Today’s tutorial is more like a refresh course of the previous tutorial back in version 2.2 days. In my typical EnyoJS 2.7 project, I further classify two different “kinds” other than viewpages…

  1. Control – a custom UI related kind that can be re-use in View Pages. e.g. NavBar.js, ColorfulPicker.js, ToolBar.js
  2. Module – a kind which contains only methods and logic. Used normally for wrappers, utility and helpers. e.g. NavigationService.js WebService.js

I’m going to start with writing a generic toolbar, a control; that can be reuse throughout the whole app. Everything is linked up using NavigationService.js which is a module. List of files involved…

  1. PreLoaderPage.js
  2. NavigationService.js
  3. MasterPage.js
  4. DetailPage.js
  5. ToolBar.js

PreLoaderPage.js

var kind = require('enyo/kind');
var Button = require('enyo/button');
var FittableRows = require('layout/FittableRows');
var NavigationService = require('../modules/NavigationService');


var MasterPage = require('../viewpages/MasterPage');
var DetailPage = require('../viewpages/DetailPage');

module.exports = kind({
    name: 'PreLoaderPage',
    routes: {
        "MasterPage": new MasterPage(),
        "DetailPage": new DetailPage()
    },
    kind: FittableRows,
    classes: 'enyo-fit',
    components:[
        {
          content:'Loading...'
        }
   ],
   create: function(){
           this.inherited(arguments);
           NavigationService.Init(this.routes);
   },
   rendered : function(){
           this.inherited(arguments);
           // Once everything is rendered and loaded. Move to MasterPage();
           // GotoPage will match string into the required reference of the page
           // and render it into body.
           NavigationService.GotoPage("MasterPage");
   }
});

Next, this is how MasterPage.js looked like (see pic). We are going to create some array of data and list it using (layout/List). A ToolBar.js is reuse in both MasterPage.js and DetailPage.js

MasterPage

MasterPage.js

var kind = require('enyo/kind');
var Button = require('enyo/button');
var FittableRows = require('layout/FittableRows');
var ToolBar = require('../controls/ToolBar');
var List = require('layout/List');
var NavigationService = require('../modules/NavigationService');

module.exports = kind({
    name: 'FirstPage',
    data: [
        { "label":"Apple", "description":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." },
        { "label":"Orange", "description":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." },
        { "label":"Strawberry", "description":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."},
        { "label":"Pineapple", "description":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." },
        { "label":"Grapes", "description":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." },
        { "label":"Kiwi", "description":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." },
        { "label":"Pear", "description":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." },
        { "label":"Durian", "description":"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." }

    ],
    kind: FittableRows,
    components:[
        {
                kind:ToolBar,
                name:"toolBar"
        },
        {
            kind:List,
            name:"listFruits",
            classes:"listView",
            fit:true,
            count:0, 
            onSetupItem: "setupItem", 
            components: [
                {
                    // This is a template for a single List CellItem
                    name: "listFruitsItem", 
                    classes:'listItemContainer', 
                    ontap:'handle_ListItemTapped', 
                    components: [ 
                        {
                            // This is a template for a single List CellItem's Content
                            name: "listItemTitle",  
                            content:"Set Title..."
                        }
                    ]
                }
            ]
        }
   ],
   create: function(){
       this.inherited(arguments);
       this.$.toolBar.hideNavButton("btnLeft");
       this.$.toolBar.hideNavButton("btnRight");
       this.$.toolBar.setTitle("MasterPage");
   },
   rendered : function(){
       this.inherited(arguments);
       //There's no changes in good old List component. It will fire event called onSetupItem 
       //when count property changes. "onSetupItem" will then, inserts data into the template
       //and populates the list. Just give it an array count of your desired datasource.
       this.$.listFruits.setCount(this.data.length);
       // Because data is populated after List is Rendered (drawn), we will have to refresh it again
       // using render()
       this.$.listFruits.render();
   },
   setupItem: function(inSender,inEvent) {
       // Each time setupItem is fire, inEvent will return the corresponding array index which
       // matches the datasource count. inEvent.index
       this.$.listItemTitle.setContent(this.data[inEvent.index].label);
   },
   handle_ListItemTapped(inSender,inEvent){
       //Like how setupItem works inEvent will return an index of the item tapped.
       console.log(inEvent.index);
       //We then use NavigationService to traverse to DetailPage, passing a record
       NavigationService.GotoPage("DetailPage", this.data[inEvent.index]);
   }
});

NavigationService.js

I have modified NavigationService.js (since last tutorial) to include some simple form of parameter passing.

var kind = require('enyo/kind');
var Component = require('enyo/Component');
module.exports = kind( {
    name: 'NavigationService',
    statics:{
        Routes:null,
        Index:0,
        NavigationStack:[],
        Init:function( routes ){
            this.Routes = routes;
        },
        PushPage: function( pageName, param ){
            //NavigationStack is to keep track of history of navigation.
            this.NavigationStack.push(this.Routes[pageName]);
            this.Index = this.NavigationStack.length-1;
            if (arguments.length>1){
                this.NavigationStack[this.Index].data = param;    
            } 
            this.NavigationStack[this.Index].renderInto(document.body);
        },
        PopPage: function(){
            this.NavigationStack.pop();
            this.Index = this.NavigationStack.length-1;
            this.NavigationStack[this.Index].renderInto(document.body);
        },
        GotoPage: function( pageName, param ){
            // Manually travel into the page without updating Navigation Stack
            if (arguments.length>1){
                // Optionally we can pass over parameter to another viewpage.
                this.Routes[pageName].data = param;
                this.Routes[pageName].renderInto(document.body);
            } else {
                this.Routes[pageName].renderInto(document.body);
            }
        }
        
    }
});

ToolBar.js

var kind = require('enyo/kind');
var Button = require('enyo/button');
var FittableColumns = require('layout/FittableColumns');

module.exports = kind({
    name: 'ToolBar',
    kind: FittableColumns,
    classes: 'toolBar',
    components:[
        {
            kind:Button,
            name:"btnLeft",
            classes:"btnArrow",
            ontap:"handle_btnLeftTapped",
            content: "<" 
         }, 
         { 
            name:"txtTitle", 
            fit:true, classes:"headerTitle", 
            content:"Set Page Title..." 
         }, 
         { 
            kind:Button, 
            name:"btnRight", 
            classes:"btnArrow", 
            ontap:"handle_btnRightTapped", 
            content: ">"
        },
   ],
   create: function(){
           this.inherited(arguments);
   },
   rendered : function(){
           this.inherited(arguments);
   },
   setTitle: function(value){
        //This (this.$. is a standard enyo name reference
        //Similar to jQuery $("#elementId") 
        this.$.txtTitle.setContent(value);
   },
   hideNavButton: function( buttonName ){
        //Using this.$[string] enyo will look for matching (eval) variable for buttonName string.
        this.$[buttonName].hide();
   },
   handle_btnLeftTapped: function(inSender,inEvent){
        //If you are within a control, you can bubble a custom event so that your parent view page
        //can attach it's listener to.
        this.bubble("onBtnLeftTap",inSender);
   },
   handle_btnRightTapped: function(inSender,inEvent){
        //Just bubble all the way up to parent.
        this.bubble("onBtnRightTap",inSender);
   }
});

Code above shows, you can actually bubble custom events to the view pages.

DetailPage.js

var kind = require('enyo/kind');
var Button = require('enyo/button');
var FittableRows = require('layout/FittableRows');
var ToolBar = require('../controls/ToolBar');
var NavigationService = require('../modules/NavigationService');

module.exports = kind({
    name: 'DetailPage',
    kind: FittableRows,
    data: null,
    classes: 'enyo-fit',
    components:[
        {
                kind:ToolBar,
                name:"toolBar",
                //We attached customEvent name here as well. 
                //Since only left button is visible, we just use
                //onBtnLeftTap.
                onBtnLeftTap:"handle_onBtnLeftTapped"
                
        },
        {
                fit:true,
                classes:"center-content",
                components:[
                    {
                        tag:"p",
                        name:"txtDescription",
                        content:"Set Description Here..."
                    }
                ]
        }
   ],
   create: function(){
        this.inherited(arguments);
        this.$.toolBar.hideNavButton("btnRight");
   },
   rendered : function(){
        this.inherited(arguments);
        if (this.data != null ){
            this.$.toolBar.setTitle(this.data.label);
            this.$.txtDescription.setContent(this.data.description);
        }
   },
   handle_onBtnLeftTapped:function(inSender,inEvent){
            NavigationService.GotoPage("MasterPage");
   }
});

That’s it for today, download source code reference here into project folder named “simple”

EnyoJS 2.7 Module and Control (566 downloads)

Comments

comments

One thought on “EnyoJS 2.7 Tutorial : Controls & Modules

Leave a Reply

Your email address will not be published. Required fields are marked *