13. Controllers interaction
- Overview
- Sample application
- Controllers used in sample application
- Defining a child controller
- Defining a child controller meta attribute
- Controllers interaction
- Dividing controllers as parent and childs
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.
- 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.
Parent-child relationship of the controller is as below:
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.
// 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.
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.
'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.
'{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().
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.
- 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.
- 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:
Controller functions used for interaction
Referring to child controller
You can refer to child controller from parent controller via property that defines child 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.
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.
__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:
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.
For more details regarding controller design, see Development guide - Controller design(under constructing).