-
Notifications
You must be signed in to change notification settings - Fork 16
v1 09. Controller & Component
Nalu mainly helps you to define three types of elements:
- Handler (a class, without visual components see Handler)
- Controller
- Components
This page will help you to understand how to build Controllers and Components.
If you are familiar with the MVP pattern: In Nalu a controller can be compared with the presenter and the component with the view of the MVP pattern.
Nalu requires to use the view delegate pattern.
A visual part in Nalu will always be a combination of a controller and a component. The Controller will load data, fire events, handle events and routes to other controllers. A Controller should never contain classes and interfaces of a widget library.
To create a controller, you have to:
- extend AbstractComponentController<C, V, W>
- C: type of the context
- V: type of the view (interface) that will be injected in the controller
- W: type of the base class of your widget library (for GWT & GXT: Widget, Elemento and Domino-UI: HTMLElement)
By extending AbstractComponentController you will have access to the allowing instances:
- C context: instance of the application context (Singleton)
- V view: instance of the view
- eventbus: instance of the application wide event bus
- router: instance of the router
To tell Nalu, that this class is a controller, you to annotate the class with Controller
. Nalu will automatically create an instance for each class annotated with @Controller
in case the route of the controller gets triggered. (except in cases where the controller is cached.)
A controller requires a
- a public, zero-argument constructor
- annotate the controller with
@Controller
Here is an example of a controller:
@Controller(route = "/shell/route01/route02/:id",
selector = "content",
componentInterface = IMyComponent.class,
component = MyComponent.class)
public class DetailController
extends AbstractComponentController<MyApplicationContext, IMyComponent, HTMLElement>
implements IMyComponent.Controller {
public MyController() {
}
...
}
To let Nalu automatically create a controller the controller class (which extends AbstractComponentController
) needs to be annotated with @Controller
.
The @Controller
annotation has four required attributes:
- route: the route which activates the controller. A route has two parts:
- the first part contains the path to the controller. A route starts always with a '/' and is used to separate routes
- the second part are the parameters that will be injected in the controller. To define a parameter inside a route, use '/:parameter name'. Parameters are optional
- selector: a selector defines an ID inside the DOM (using the element plugin) or an
add
-method (using the GWT-plugin) which will be called to add a widget to the DOM. - componentInterface: the type of the interface for your component
- component: the type of the component
The 'componentInterface'-attribute will be used inside the controller as reference of the component interface, where as the 'component'-attribute will be used to instantiate the component. By default Nalu uses the new
to create an instance of an component. (GWT-create()
will no longer be available in J2CL / GWT 3! And, to be ready for J2CL / GWT 3 Nalu has to avoid using GWT.create
).
In case a controller accepts parameters, you have to add the parameters to the route attribute after the route part. Parameters are defined by using '/:parameterName'.
To let Nalu inject a parameter from the route into the controller, you have to create a method, which accepts a String parameter and annotate the method using the @AcceptParameter("parameterName")
annotation.
For example, if the controller requires an 'id', the controller class will look like this:
@Controller(route = "/shell/route01/route02/:id",
selector = "content",
componentInterface = IMyComponent.class,
component = MyComponent.class)
public class MyController
extends AbstractComponentController<MyApplicationContext, IMyComponent, HTMLElement>
implements IMylComponent.Controller {
private long id;
public DetailController() {
}
@AcceptParameter("id")
public void setId(String id)
throws RoutingInterceptionException {
try {
this.id = Long.parseLong(id);
} catch (NumberFormatException e) {
// in case of an exception route to another page ...
throw new RoutingInterceptionException(this.getClass()
.getCanonicalName(),
"/person/search",
this.context.getSearchName(),
this.context.getSearchCity());
}
}
}
A Nalu controller has a life cycle. Nalu supports the methods:
-
start
: will be called after the component is created -
mayStop
: will be called before a new routing occurs. In case the method return a String and not a null value, Nalu will display the String and abort the routing -
stop
: in case of a successful routing (not interrupted by a filter or themayStop
method) this method will be called.
Every time the application gets a new route, Nalu will create a new instance of the controller (and of course of the component).
Nalu will call the onAttach
method, after the component is added to the DOM. This is an internal method, that can not be overwirtten. Use the active
-method inside the controller instead.
Nalu will call the onDetach
method, after the component is removed to the DOM. This is an internal method, that can not be overwirtten. Use the deactive
-method inside the controller instead.
Nalu will call the bind
method before the component is created. This is an internal method, that can be overwritten. That's a good place to do some preparing actions. The method is asynchron. This makes it possible to do a server call before Nalu will continue. To tell Nalu to continue, call loader.continueLoading()
. In case you want to interrupt the loading, just do not call loader.continueLoading()
and use the router to route to another place.
A controller has access to the application event bus. Nalu uses the the events of the module org.gwtproject.events
. This artifact is ready for GWT 3/J2CL and works similar like the event bus from GWT. You can add handler to the event bus and fire events. A good place to register a handler on the event bus is the start
method.
Nalu supports automatic handler removing.
In case the controller adds a handler to the event bus, add the HandlerRegistration
to the controller handler registration list. Once the controller is stopped, all handlers will be removed from the event bus.
@Override
public void start() {
// add the handler registration to the HandlerRegistrations class of this controller
// Doing that will help that once the controller gets stops all handler registrations
// will be removed!
this.handlerRegistrations.add(this.eventBus.addHandler(MyEvent.TYPE,
e -> doSomething())));
}
Nalu will automatically create a component using the Java 'new'-command and inject the component into the controller. Therefore the component needs to have a zero argument constructor.
In some cases this might be a problem.
You can tell Nalu to use a method inside the controller and create the component by your own. To do so, you have to annotate the controller with IsComponentCreator<V>
and implement a createComponent
-method.
@Controller(...)
public class MyController
extends AbstractComponentController<MyContext, IMyComponent, HTMLElement>
implements IMyComponent.Controller,
IsComponentCreator<IMyComponent> {
...
@Override
public IMyComponent createComponent() {
return new MyComponent();
}
}
The IsComponentCreator
-interface will also work with Nalu's composites!
Note: At the time Nalu calls the createComponent
-method, the context is already injected.
A component represents the visual part. This is the place where the layout is created, fields are added, etc.
To create a component, you have to:
- extend AbstractComponent<C, W>
- C: type of the controller (defined as interface)
- W: type of the base class of your widget library (for GWT & GXT: Widget, Elemento and Domino-UI: HTMLElement)
A component requires a
- a public, zero-argument constructor
- setting the element inside the
render
method usinginitElement(widget)
Nalu will, after a component is created, call the render
method. So the application is able to create the visible parts. Once you are ready with your layout, call the initElement
method and use the root element as parameter. Once, it is done, Nalu will use the element of the component and add it to the DOM.
The following image shows the execution order in case a new controller is created:
Imagine, you have a view, that looks like that:
Of course it is possible to render this view inside one component. In some cases, where your controller and component will have to much code or you want to reuse composite01 component, composite02 component and composite03 component, you can extract them in separate controller and component pairs. But this would not be handled but Nalu.
Nalu offers - with the support for Composites - a feature to help you creating such things.
Composites in Nalu are treated like controllers, but can not be used by a route. That's the different between a composite and a controller. Composites will have access to the event bus, router and context. A Composite controller behave like a normal controller. Except, that it can be activated from a controller and not from a router!
To create a composite, you have to:
- extend AbstractCompositeController<C, V, W>
- C: type of the context
- V: type of the view (interface) that will be injected in the controller
- W: type of the base class of your widget library (for GWT & GXT: Widget, Elemento and Domino-UI: HTMLElement)
By extending AbstractCompositeController you will have access to the allowing instances:
- C context: instance of the application context (Singleton)
- V view: instance of the view
- eventbus: instance of the application wide event bus
- router: instance of the router
To tell Nalu, that this class is a controller, you have to annotate the class with CompositeController
. Nalu will automatically create an instance for each class annotated with @CompositeController
.
A composite requires a
- a public, zero-argument constructor
- annotate the controller with
@CompositeController
Here is an example of a composite:
@@CompositeController(componentInterface = IMyCompositeComponent.class,
component = MyCompositeComponent.class)
public class MyComposite
extends AbstractCompositeController<MyApplicationContext, IMyCompositeComponent, HTMLElement>
implements IMyCompositeComponent.Controller {
public MyComposite() {
}
}
To let Nalu automatically create a composite the composite class (which extends AbstractCompositeController
) needs to be annotated with @CompositeController
.
The @CompositeController
annotation has two required attributes:
- componentInterface: the type of the interface for your component
- component: the type of the component
The 'componentInterface'-attribute will be used inside the composite controller as reference of the component interface, where as the 'component'-attribute will be used to instantiate the composite component. By default Nalu uses the new
to create an instance of an component. (GWT-create()
will no longer be available in J2CL / GWT 3! And, to be ready for J2CL / GWT 3 Nalu has to avoid using GWT.create
).
A Nalu composite controller has a life cycle similar the life cycle of a controller. Nalu supports the methods:
-
start
: will be called after the component is created for a controller that is not cached. -
activate
: this will be called every time a controller gets active independent from the fact, if the controller is newly created or cached. -
mayStop
: will be called before a new routing occurs. In case the method return a String and not a null value, Nalu will display the String and abort the routing -
deactivate
: this will be called every time a controller gets deactive independent from the fact, if the controller is newly created or cached. -
stop
: in case of a successful routing (not interrupted by a filter or themayStop
method) this method will be called. In case the controller is cached thestop
-method will not be called.
Every time the application composite is required by a controller, Nalu will create a new instance of the composite controller (and of course of the component).
Nalu will call the onAttach
method, after the component is added to the DOM.
Nalu will call the onDetach
method, after the component is removed to the DOM.
A controller has access to the application event bus. Nalu uses the the events of the module org.gwtproject.events
. This artifact is ready for GWT 3/J2CL and works similar like the event bus from GWT. You can add handler to the event bus and fire events. A good place to register a handler on the event bus is the start
method.
Nalu supports automatic handler removing.
In case the controller adds a handler to the event bus, add the HandlerRegistration
to the controller handler registration list. Once the controller is stopped, all handlers will be removed from the event bus.
@Override
public void start() {
// add the handler registration to the HandlerRegistrations class of this controller
// Doing that will help that once the controller gets stops all handler registrations
// will be removed!
this.handlerRegistrations.add(this.eventBus.addHandler(MyEvent.TYPE,
e -> doSomething())));
}
A component represents the visual part. This is the place where the layout is created, fields are added, etc.
A composite component looks like a normal component. To create a composite component, you have to:
- extend AbstractCompositeComponent<C, W>
- C: type of the composite controller (defined as interface)
- W: type of the base class of your widget library (for GWT & GXT: Widget, Elemento and Domino-UI: HTMLElement)
A composite component requires
- a public, zero-argument constructor
- setting the element inside the
render
method usinginitElement(widget)
Nalu will, after a composite component is created, call the render
method. So the application is able to create the visible parts. Once you are ready with your layout, call the initElement
method and use the root element as parameter. Once, it is done, Nalu will use the element of the component and add it to the DOM.
It is possible to call the getController
-method inside the render
-method to obtain values from the controller.
In order to use composite controllers in Nalu, you have to add the Composite
annotation to a controller. THe Composites
annotation will have another Composite
annotation for each composite.
Here an example of how to use composites in a controller. This example shows a controller, which uses two composites:
@Controller(route = "/shell/route01/route02/:parameter01",
selector = "selector01",
componentInterface = IMyComponent.class,
component = MyComponent.class)
@Composites({
@Composite(name = "composite01",
compositeController = MyComposite01.class,
selector = "composite01"),
@Composite(name = "composite02",
compositeController = MyComposite02.class,
selector = "composite02")
})
public class MyCompositeUsingController
extends AbstractComponentController<MyApplicationContext, IMyComponent, HTMLElement>
implements IMyComponent.Controller {
...
}
Nalu will inject the instance of the created composite controllers into the controller. So, the controller can easily access the composite. To access the instance of a composite, use this statement:
MyComposite01 instanceOfComposite01 = super.<MyComposite01>getComposite("composite01");
In some cases, especially if the selector of the composite is defined outside the DOM of the component, the composite component will not be removed if a new routing occurs. In this cases overriding the remove
method of the AbstractCompositeComponent
class will help. Just add in this method the code to remove the out most container by calling myOuterContainer.remove()
or myOuterContainer.removeFromParent()
. Nalu will call the remove
method once a composite controller gets stopped.
Starting with version 1.2.2 Nalu will support conditional composite. A conditional composite is a composite that will be insert into the screen depending on a condition. So, it is possible to decide wheather to show or not show a composite dependeing on the route, parameters or informations stored inside the context.
A conditional composite requires a class that extends AbstractCompositeCondition
. Inside this class the boolean loadComposite(String route, String... parms)
must be implemented. Returning true will tell Nalu to create the composite and it to the component. In case the value false is returned, Nalu will not create the composite and add it to the component.
The conditional class is another attribute of the Composite
- annotation called 'condition'.
Here is an example of the usage of a conditional composite.
First, create the condition:
public class MyCompositeCondition
extends AbstractCompositeCondition<MyApplicationContext> {
@Override
public boolean loadComposite(String route,
String... parms) {
if ([condition]) {
return false;
} else {
return true;
}
}
}
Use the condition inside the Composite
-annotation:
@Controller(route = "/shell/route01/route02/:parameter01",
selector = "selector01",
componentInterface = IMyComponent.class,
component = MyComponent.class)
@Composites({
@Composite(name = "composite01",
compositeController = MyComposite01.class,
selector = "composite01"),
@Composite(name = "composite02",
compositeController = MyComposite02.class,
selector = "composite02",
condition = MyCompositeCondition.class)
})
public class MyCompositeUsingController
extends AbstractComponentController<MyApplicationContext, IMyComponent, HTMLElement>
implements IMyComponent.Controller {
...
}
Now, depending of the return value of the laodComposite
-method, the composite will be created and addded or not.
Keep in mind: in case the condition class returns false, Nalu will not create an instance of the composite!
Before version 1.2.2 Nalu does not support pop-ups. Starting with version 1.2.2 Nalu gets a new controller type: PopUpController. The new controller will enable Nalu to handle pop-ups.
A PopUpController has the following features:
- has no route (and because of that: no history!)
- has no selector (Nalu will not add the component of a PopUpController to the shell, cause it is a pop up!)
- has no life-cycle
- can not be cached (cause it is cached by default)
- is triggered by an event
To create a pop-up Controller, you have to:
- extend AbstractPopUpComponentController<C, V>
- C: type of the context
- V: type of the view (interface) that will be injected in the controller
By extending AbstractPopUpComponentController you will have access to the allowing instances:
- C context: instance of the application context (Singleton)
- V view: instance of the view
- eventbus: instance of the application wide event bus
- router: instance of the router
To tell Nalu, that this class is a pop-up controller, you to annotate the class with PopUpController
. Nalu will automatically create an instance for each class annotated with @PopUpController
the first time it is requested. Once the controller is created, it will always be reused.
A controller requires a
- a public, zero-argument constructor
- annotate the controller with
@PopUpController
- extends
AbstractPopUpComponentController
Here is an example of a controller:
@PopUpController(name = "PopUpEditor",
componentInterface = IMyComponent.class,
component = MyComponent.class)
public class MyPopUpController
extends AbstractPopUpComponentController<MyContext, IMyComponent>
implements IMyComponent.Controller,
IsPopUpComponentCreator<IDetailComponent> {
public MyPopUpController() {
}
...
}
To let Nalu automatically create a pop-up controller the controller class (which extends AbstractPopUpComponentController
) needs to be annotated with @PopUpController
.
The @PopUpController
annotation has three required attributes:
- name: the name used to identify the controller
- componentInterface: the type of the interface for your component
- component: the type of the component
The 'componentInterface'-attribute will be used inside the controller as reference of the component interface, where as the 'component'-attribute will be used to instantiate the component. By default Nalu uses the new
to create an instance of an component. (GWT-create()
will no longer be available in J2CL / GWT 3! And, to be ready for J2CL / GWT 3 Nalu has to avoid using GWT.create
).
You can use the IsPopUpComponentCreator
-interface to create the component inside your controller.
Nalu will inject:
- the instance of the router
- the instance of the event bus
- the instance of the context
- the instance of the component
into the controller.
A PopUpController is not bound to a route. To trigger a PopUpController fire the ShowPopUpEvent
. The ShowPopUpEvent
takes the name of the pop-up (will be used to identify the pop-up inside the PopUpControllerFactory
) and add the necessary parameters. The name
value is mandantory where as the parameters are optional.
The following code shows how to trigger a PopUpController in Nalu:
this.eventBus.fireEvent(ShowPopUpEvent.show("PopUpEditor")
.using("id",
"4711"));
The code above triggers the PopUpController using the name 'PopUpEditor' and adds 'id' to the event. (A paraemter value must be a String)
Nalu will inject the parameters into the dataStore
inside the AbstractPopUpComponentController
-class. To retieve the value use:
String id = super.dataStore.get("id");
A Nalu pup-up controller has no life cycle. The controller (and it's component) will be created the first time an event is requesting this pop-up. Once it is created, Nalu will always reuse this instance.
Nalu will call two methods inside the PopUpController
:
-
onBeforeShow
: will be called before theshow
-method. (A good place to initialize the controller and component) -
show
: to show the controller.
It is up to the controller to show the pop-up and hide it.
Nalu will automatically create a component using the Java 'new'-command and inject the component into the controller. Therefore the component needs to have a zero argument constructor.
In some cases this might be a problem.
You can tell Nalu to use a method inside the controller and create the component by your own. To do so, you have to annotate the controller with IsPopUpComponentCreator<V>
and implement a createPopUpComponent
-method.
@PopUpController(...)
public class MyController
extends AbstractPopUpComponentController<MyContext, IMyComponent>
implements IMyComponent.Controller,
IsPopUpComponentCreator<IMyComponent> {
...
@Override
public IMyComponent createPopUpComponent() {
return new MyComponent();
}
}
Note: At the time Nalu calls the createPopUpComponent
-method, the context is already injected.