Skip to content

v1 09. Controller & Component

Frank Hossfeld edited this page Nov 28, 2019 · 1 revision

Controllers and Components

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.

Controller

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.

Defining a Controller

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() {
 }

 ...

}

Controller annotation

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).

Parameters

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());
   }
 }
}

Life Cycle

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 the mayStop 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).

onAttach Method

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.

onDetach Method

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.

bind Method (since v1.2.1)

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.

Event Bus

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.

Handler Management

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())));
 }

Component Creation

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.

Component

A component represents the visual part. This is the place where the layout is created, fields are added, etc.

Defining a component

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 using initElement(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.

Execution Order

The following image shows the execution order in case a new controller is created:

Execution Order

Composite

Imagine, you have a view, that looks like that:

Route Flow

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!

Creating a Composite

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() {
  }


 }

CompositeController annotation

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).

Life Cycle

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 the mayStop method) this method will be called. In case the controller is cached the stop-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).

onAttach Method

Nalu will call the onAttach method, after the component is added to the DOM.

onDetach Method

Nalu will call the onDetach method, after the component is removed to the DOM.

Event Bus

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.

Handler Management

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())));
 }

Composite Component

A component represents the visual part. This is the place where the layout is created, fields are added, etc.

Defining a Composite Component

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 using initElement(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.

Adding Composites to a 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.

Conditional Composite (since v1.2.2)

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!

PopUpController (since v1.2.2)

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

Defining a PopUpController

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() {
 }

 ...

}

PopUpController annotation

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.

Trigger a PopUpController

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");

Life Cycle

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 the show-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.

Component Creation

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.

Clone this wiki locally