Blog
Tutorial (basic learning) » 13. Controllers interaction

13. Controllers interaction

Last modified by kashi on 2015/01/30, 13:10

Overview

This chapter describes controller's parent-child and sibling relationships and interaction between them via a sample application.

Sample application

You can see the sample application below:
 Recipe (sample) » Controllers interaction sample
This sample uses h5.debug.js that supports debugging in hifive.

This is a Youtube video player application, containing the following functions.
design-controller1_en.png

  • Keyword searching
    • Search for Youtube video by keyword. Search result will be displayed on the left hand side.
  • Video playing
    • Play video selected from search results or favorite list.
  • Displaying currently playing video information.
    • Display title and contributor's comments.
  • Displaying comments of currently playing video
  • Video manipulating
    • Basically, you can play or stop the player via a group of buttons.
  • Favorite list
    • Register/remove video to/from favorite list.

Controllers used in sample application

This application embeds multiple controllers in a single page. For 5 major functions (video search, video playing, video manipulation, favorite list managing, comment display), 1 controller is created for each of them.

In addition, a "parent controller" is created to manage interaction between the controllers.

design-controller2_en.png

Parent-child relationship of the controller is as below:

design-controller3_en.png

FavoriteController (favorite list controller) contains right-click menu display function and carousel list function (a view function that loops through a list by sliding or dragging operation) as common components. Each of them is treated as a child controller.

PageController that manages the page will manage the child controllers, parent controller will manage interaction between sibling controllers. 

Unless parent controller is created, if interaction between the controllers are done by calling each other's method, this would lead to the following disadvantages:

  • Since dependency relationship is complex, if changes in specifications are made to view, much effort would be required for maintenance.
  • If those are common view components, it would be difficult to re-use. 

Defining a child controller

If a property ended with '~~Controller' of a specific controller is assigned a controller definition object, those controllers would become parent and child and would both be controllized. Both controllers' life cycle event will then be executed and event handler will be registered.

(function(){
  // Defining a child controller
   var screenController = {
        __name: 'youtube.controller.ScreenController',

       // ~ omitted ~
   };
   // Set screenController as global
   h5.core.expose(screenController);
});

(function(){
     // Parent controller
   var pageControlelr = {
        __name: 'youtube.controller.PageController',
       // Set ScreenController as a child controller
       _screenController: youtube.controller.ScreenController,

       // ~ omitted ~
   };
    h5.core.expose(pageController);
});

$(function(){
   // Binding controller
   h5.core.controller('body', youtube.controller.PageController);
});

If a controllized controller definition object contains a child controller, the child controller will also be controllized. In the sample code above, PageController has a child controller (ScreenController). Since ScreenController is also controllized, life cycle event of ScreenController (__ready, etc.) and event handler defined within ScreenController will become active.

Regarding controllization behavior of parent and child controllers, seeParent and child controllers lifecycle (underconstruction) .

In this example, PageController and ScreenController are bound to body element, PageController will contain an instance of ScreenController (able to refer to).

Defining a child controller meta attribute

By default, child controller is also bound to root element of parent controller, thus root element of child controller and parent controller is the same.

However, using __meta property child controller can be bound to different element other than root element of parent controller.

For more details, seeConfigurable properties of controller meta attribute .

Controllers interaction

If you want to implement interaction between a controller and its siblings, trigger an event to inform the parent controller. Parent controller receives the event and calls method of child controller.

By triggering an event to inform parent controller, child controller will not depend on parent controller and siblings controller methods and can be used on other views.

Actual implementation example is described as below. This example of interaction plays the video selected from search results.

design-controller4_en.png

When video listed in search results in SearchController is clicked, an event is triggered. As result, an event that informs parent controller will also be triggered.

// SearchController
'li click': function(cotnext, $el) {
   var videoId = $el.data('videoid'); // Get videoID
   // Trigger 'loadById' event. Pass video ID to argument.
   this.trigger('loadById' ,{
        id: id
    });
},

Using this.trigger(), you can trigger an event originating from root element of the controller. In this sample application, it is used to trigger loadById event.

The argument passed to loadById event handler is passed to argument 2 of trigger().

For more details about trigger(), See API document Controller.trigger.

PageController will call child controller method in response to the triggered event.

// PageController
'{rootElement} loadById': function(context) {
   // Get video ID from context.evArg.
   var id = context.evArg.id;

   // Play video. Call ScreenController method to play the video
   this._screenController.loadById(id);
},

If loadById event is triggered, PageController will call method of ScreenController, which is its child controller, and allow ScreenController to start loading the video.

Object passed to argument 2 of trigger() is stored in context.evArg. Get video ID from context.evArg then pass to ScreenController.loadById().

// ScreenController
loadById: function(context){
   // Start loading and playing video
},

As soon as ScreenController calls loadById, the video starts loading and playing.

This way SearchController (child controller) can execute ScreenController method by triggering event without having to call sibling and parent controller's methods.

The general workflow is:

1.Use trigger() to trigger event if you want to interact with other controllers. Pass argument if necessary. 

  1. Define parent controller's action when event triggered in event handler. Call child controller's method where necessary in order to respond to the event. 
  2. Include method that is assumed to be called from outside in child controller.
    Follow the above steps to implement interaction.

When the controllers in sample application are combined, the result would be as follows:
design-controller5_en.png

Controller functions used for interaction

Referring to child controller

You can refer to child controller from parent controller via property that defines child controller.

// Refer to child controller from parent controller
var pageController = {
    __name: 'youtube.controller.PageController',
   /**
     * Child controller
     */

    _screenController: youtube.controller.ScreenController,

    hoge: function(context){
       // Refer to child controller from parent controller, then call child controller's methods
       this._screenController.loadById();

       // Get child controller root element
       var screenElm = this._screenController.rootElement;

       // Get element by using selector that is within the range of root element of child controller
       var $screenTitle = this._screenController.$find('.title');
    }
}

Referring to parent element

You can get parent controller from child controller using  parentController property.

// Example: refer to parent controller from child controller
var screenController = {
    __name; 'youtube.controller.ScreenController',
    hoge: function(id) {
       // Get parent controller
       var parentCtrl = this.parentController;

      // Get parent controller root element
      var parentCtrl.rootElement;

      // Get element by using selector that is within the rage of root element of parent controller
      var $hoge = parentCtrl.$find('.hoge');
    }
}

Parent controller can have multiple instances of child controller, however, child controller can have only 1 parent controller (one-to-many).

Referring to root controller

Controller parent-child relationship can be nested. You can create parent, child, grandchild controllers.

In sample application, controller parent-child relationship is nested as below:

PageController -> FavoriteController -> [ContextMenuController, LoopCarouselController]

As the relationship is nested, you can refer to root controller via  rootController  property.

var contextMenuController = {
    __name: 'h5.ui.ContextMenuController',
    hoge: function(){
       this.parentController;  // FavoriteController
       this.rootController;    // PageController
   }
};

var favoriteController = {
    __name: 'youtube.controller.FavoriteController',
   // FavoriteController includes ContextMenuController as child controller
   _contextMenuController: contextMenuController,

    hoge: function(){
       this.parentController;  // PageController
       this.rootController;    // PageController
   }
};

var pageController = {
    __name: 'youtube.controller.PageController',
    _favoriteController: favoriteController,

    hoge: function(){
       this.parentController;  // null
       this.rootController;    // PageController (similar to "this")
   }
};

The relationship between parent controller and root controller is described as below:

design-controller7_en.png

Dividing controllers as parent and childs

Even when controllers' parent-child relationship does not exist, you still don't have to define all the functions in a single controller. However, it is recommended that they should be designed as loosely related controllers. General-purpose components can be re-used, and it would be much comfortable to respond when changes in specifications are made to combined controllers.

Dividing controllers based on function units

By dividing controllers based on function basis, when a function is added to or removed from view, you will simply need to replace the controller and slightly modify the parent controller.

If all the functions are defined in a single controller, you have to rewrite all the code where combination had been defined.

Dividing controllers by extracting common components

The component that holds general-purpose functions that can be re-used in another view, should be treated as a separate controller.

For example, in sample application, FavoriteController has LoopCarouselController as a child controller. LoopCarouselController is the controller (carousel component) that enables user to loop through by scrolling back and forth a list of DOM elements.

In general, LoopCarouselController is carousel component, a general-purpose component that can display not only videos in favorite list, but also any DOM elements as carousel.

As a result, "function that displays favorite list as carousel component" is divided into FavoriteController, which manages favorite list, and LoopCarouselController, which manages carousel component.

design-controller6_en.png

For more details regarding controller design, see Development guide - Controller design(under constructing).


Copyright (C) 2012-2017 NS Solutions Corporation, All Rights Reserved.