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
deactivatemethod on every extension registered in the old controller and then the same method on the controller itself. Same process follows withdestroymethod. -
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
initmethod is called on controller then on every extension (Extensions may be initialized during the controllersinitmethod call). When the initialization is complete manager starts loading resources vialoadmethod of the controller and extensions. For detailed explanation see theloadmethod documentation. -
Rendering new view - After the
loadmethod has been called a view for the controller is rendered. It doesn't matter if all promises returned by theloadmethod have been resolved. The process of handling promises is described in theloadmethod 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 ofGenericErrorwith 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]);
};