-
Notifications
You must be signed in to change notification settings - Fork 16
15. Multi Module Support
There are several reasons to divide an application into several modules. Reasons might be:
- separation of work
- reuse of code
- faster J2CL compiles.
In the current implementation, you can have a common module, but you do not have to! We will take later a look on how to set up projects.
First we need to create a module. A Nalu module does not implement the IsApplication
-interface. Instead, it extends the IsModule
-interface. This interface needs a @Module
-annotation.
The @Module
-annotation takes two attributes:
- name: the name of the module.
- context: the context of the module
Example:
@Module(name = "myModule01",
context = MyModule01Context.class)
public interface MyModule01
extends IsModule<MyModuleContext> {
}
Next we need to tell the application, that there are modules to load. To do so, we add the @Modules
-annotation to the IsApplication
-interface.
Example:
@Application(startRoute = "/loginShell/login",
context = MyApplicationContext.class)
@Modules({ MyModule01.class,
MyModule02.class })
interface MyApplication
extends IsApplication {
}
The code above tells Nalu, that there are two modules to load:
- MyModule01
- MyModule02
Nalu will add all components, routes, etc. of the modules to the application configuration, so that the modules can be used from the main application.
To fire an event that is available in all client modules and the main application, Nalu provides an event class called NaluApplicationEvent
. This event takes a String, which should be used to describe the event type and accepts a variable numbers of data - which will be stored inside a map. The map is implemented as a Map<String, Object
. Using the key, you can access the map. The map will always return an Object
, so you have cast the return value before using it.
F.e.: If you want to update the selected navigation item in the main module (assuming that the main model will provide navigation) from a client submodule, fire a NaluApplicationEvent
, set the event string to 'selectNavigationItem' and add the identifier what item inside the navigation is to select as data inside the event store.
The NaluApplicationEvent
-class uses a builder pattern. So it is quite easy to create a NaluApplicationEvent.
Here an example of a NaluApplicationEvent, that is named 'selectNavigationItem' and has one data entry: the value 'home' with key 'navigationItem'.
this.eventBus.fireEvent(NaluApplicationEvent.create()
.event("selectNavigationItem")
.data("navigationItem", "home"));
To catch the NaluApplicationEvent, just add a handler to the event bus which will listen to NaluApplicationEvent.TYPE
. Cause every application event will be of type NaluApplicationEvent.TYPE
, you need to check the value of the attribute. Once catching the event, it is necessary to check if the event is the one of the types you are looking for by comparing the event name with the name of the event you want to catch!
this.eventBus.addHandler(NaluApplicationEvent.TYPE,
e -> {
if ("selectNavigationItem".equals(e.getEvent())) {
String selectedItem = (String) e.getData("navigationItem");
// do something ...
}
});
Note: Keep in mind, that you have to cast the stored object to the right type before using it.
To avoid the need of a common module which contains an application wide context, all of your contexts need to extend AbstractModuleContext
. The AbstractModuleContext
-class owns a data map object wrapped inside a ContextDataStore
. This data map object will be injected in every context. All data contained in this store is application wide available.
A simple context might look like that:
public class MyApplicationContext
extends AbstractModuleContext {
private final static String ATTRIBUTE_KEY = "attribute";
public MyApplicationContext() {
}
public String getAttribute() {
return (String) this.getContext().get(MyApplicationContext.ATTRIBUTE_KEY);
}
public void setAttribute(String attribute) {
this.getContext().put(MyApplicationContext.ATTRIBUTE_KEY, attribute);
}
}
Of course, you can save the code for the getter- and setter-methods and access directly the map:
String attribute = (String) myApplicationContext.getContext().get(MyApplicationContext.ATTRIBUTE_KEY);
In case you need value only available in the current module or the main module, you can add them as a normal attribute with Getter- and Setter-methods:
public class MyApplicationContext
extends AbstractModuleContext {
private final static String ATTRIBUTE_KEY = "attribute";
private String localAttribute;
public MyApplicationContext() {
}
public String getAttribute() {
return (String) this.getContext().get(MyApplicationContext.ATTRIBUTE_KEY);
}
public void setAttribute(String attribute) {
this.getContext().put(MyApplicationContext.ATTRIBUTE_KEY, attribute);
}
public String getLocalAttribute() {
return this.localAttribute;
}
public void setLocalAttribute(String localAttribute) {
this.localAttribute = localAttribute;
}
}
This implementation might look a little bit boiler-plated, but it helps you to avoid a common project where all modules and the main module depend on!
The module loaders will be called after the loader of the main module! Inside the application module, you can add a post loader, so that - after all module loaders are executed - another application loader will be executed.
There are several ways to set up a Nalu multi module project.
In case your project is based on the Thomas Broyer GWT Maven Archetype it is quite easy to work with Nalu modules. Use the shared-module as copy base for your new module and add it as source project to your client-module pom (similar to the shared-module).
Keep in mind, that your package inside the modules should be the same as the one inside the client-module.
For example:
-
client-module-package:
com.github.nalukit.app.client
-
shared-module-package:
com.github.nalukit.app.shared
-
modules-module-package:
com.github.nalukit.app.module
Besides that, you need to add: to your module descriptor.
Using this set up, you can edit sources inside your module. Reloading the browser will recompile the sources of the module too!