Page Manager
Page Manager is an essential part of IMA.js. It's something like a puppeteer that manipulates with pages and views. Once a router matches URL to one of route's path the page manager takes care of the rest.
Managing process
If the new matched route has onlyUpdate
option set to true
and the controller and view hasn't changed the route transition is dispatched only through update
method of the controller.
In every other case the manager goes through it's full process:
Unload previous controller and extensions - To make room for the new, manager has to get rid of the old controller and extensions. First calls
deactivate
method on every extension registered in the old controller and then the same method on the controller itself. Same process follows withdestroy
method.Clear state and unmount view - After unloading controller and extensions the page state is cleared and view (starting from ManagedRootView) is unmounted. However if the DocumentView, ViewAdapter and ManagedRootView are the same for the new route the view is cleared rather then unmounted. This way you can achieve component persistency.
Loading new controller and extensions - After the manager is done with clearing previous resource it initializes the new ones. First the
init
method is called on controller then on every extension (Extensions may be initialized during the controllersinit
method call). When the initialization is complete manager starts loading resources viaload
method of the controller and extensions. For detailed explanation see theload
method documentation.Rendering new view - After the
load
method has been called a view for the controller is rendered. It doesn't matter if all promises returned by theload
method have been resolved. The process of handling promises is described in theload
method documentation. Following rendering process is described on a page Rendering process and View & Components.
Intervene into the process
It's possible for you to intervene into the process before it starts and after it finished. One way is to listen to BEFORE_HANDLE_ROUTE
and AFTER_HANDLE_ROUTE
dispatcher events. However from inside event listeners you cannot intercept or modify the process. For this purpose we've introduced PageManagerHandlers in v16
PageManagerHandlers
PageManagerHandler is a simple class that extends ima/page/handler/PageHandler
. It can obtain dependencies through dependency injection. Each handler should contain 4 methods:
1. init()
method
For purpose of initializing.
2. handlePreManagedState()
method
This method is called before the page manager start taking any action. It receives 3 arguments managedPage
, nextManagedPage
and action
. managedPage
holds information about current page, nextManagedPage
about following page. Each of the "managed page" arguments has following shape:
{
controller: ?(string|function(new: Controller)), // controller class
controllerInstance: ?Controller, // instantiated controller
decoratedController: ?Controller, // controller decorator created from controller instance
view: ?React.Component, // view class/component
viewInstance: ?React.Element, // instantiated view
route: ?Route, // matched route that leads to the controller
options: ?RouteOptions, // route options
params: ?Object<string, string>, // route parameters and their values
state: {
activated: boolean // if the page has been activated
}
}
and finally the action
is an object describing what triggered the routing. If a PopStateEvent
triggered the routing the action object will look like this: { type: 'popstate', event: PopStateEvent }
otherwise the event
property will contain MouseEvent
(e.g. clicked on a link) and type
property will have value 'redirect'
, 'click'
or 'error'
.
3. handlePostManagedState()
method
This method is a counterpart to handlePreManagedState()
method. It's called after page transition is finished. It receives similar arguments (managedPage
, previousManagedPage
and action
). previousManagedPage
holds information about previous page.
Note:
handlePreManagedState()
andhandlePostManagedState()
methods can interrupt transition process by throwing an error. The thrown error should be instance ofGenericError
with a status code specified. That way the router can handle thrown error accordingly.
4. destroy()
method
For purpose of destructing
Registering PageManagerHandlers
PageManagerHandlers have their own registry PageHandlerRegistry. Every handler you create should be registered as a dependency of this registry.
// app/config/bind.js
import { PageHandlerRegistry, Window } from '@ima/core';
import MyOwnHandler from 'app/handler/MyOwnHandler';
export let init = (ns, oc, config) => {
// ...
if (oc.get(Window).isClient()) { // register different handlers for client and server
oc.inject(PageHandlerRegistry, [MyOwnHandler]);
} else {
oc.inject(PageHandlerRegistry, []);
}
};
Note: Handlers are executed in series and each one waits for the previous one to complete its task.
PageNavigationHandler
With introduction of PageManagerHandlers in v16 we've moved some functionality to predefined handler PageNavigationHandler. This handler takes care of saving scroll position, restoring scroll position and settings browser's address bar URL. You're free to extend it, override it or whatever else you want.
PageNavigationHandler is registered by default, but when you register your own handlers you need to specify PageNavigationHandler as well.
import { PageHandlerRegistry, PageNavigationHandler } from '@ima/core';
import MyOwnHandler from 'app/handler/MyOwnHandler';
export let init = (ns, oc, config) => {
// ...
oc.inject(PageHandlerRegistry, [PageNavigationHandler, MyOwnHandler]);
};