2.2 How to build a module

UI5 recommend to use UIComponent.js and manifest.json to setup the project. My implementation follows it. [NOTE: UIComponent.js in UI5 is a different concept from the UI Component design we mentioned in part 1. The corresponding terminology is called UI Control in UI5. SAP UI5 has a very robust UI Control library.]

I leveraged UIComponent.js to implement a module design. The benefit of UIComponent.js are:

It is isolate with other modules from the framework level.

It can be easily replaced like you plug in/out a USB drive.

Each module loads their own css file and i18n labels.

Each module bundles their own resource files, it brings better network performance.

Each module can be tested separately.

2.3 How to render and display a module into ApplicationContainer

I have created a ComponentLoader control. ApplicationContainer composes ComponentLoader. It uses ComponentLoader to render and display the corresponding component into a specific DIV/control. Please check my demo code for more details.

2.4 Navigation

UI5 uses route pattern to represent the browser url address. Let take a close look at the route patterns:

Student module has 2 routes and the patterns are: “edit/{id}” and “studentSearch”.

Exam module has 2 routes and the patterns are: “edit/{id}” and “search”.

ApplicationContainer has only one pattern “{module}&:all*:”

As you can see both Student and Exam module has route pattern “edit/{id}”. So when you put “edit/{id}” in the browser url address bar, ApplicationContainer has no idea which module it should pick and to initialize. So how do we resolve this issue? I came up some approaches below:

Approach 1:

According to SAPUI5 document, you can define a parent route in the module route config file. But this is not a good idea because this module will be coupled with one particular parent module. It will restrict the module reusability. The child(Student and Exam module) should never aware of the parent(ApplicationContainer).

Approach 2(Demo used this approach):

So I have created a new router called ModularRouter which extends from the sap.m.routing.Router. ModularRouter does two things:

ModularRouter adds a prefix to the module’s route pattern at run time. The prefix value is passed by the creator of the module, ApplicationContainer in this case. ModularRouter accepts a callback function which will be called after the router is initialized. This is very useful when you want to do something after you created a module and rendered into the page.

Let’t take a look at the ApplicationContainer manifest.json:

"sap.ui5": {

"_version": "1.1.0",

"componentUsages": {

"student": {

"name": "com.haojia.test.student",

"componentData": {

"routePatternPrefix": "student&/"

}

},

"exam": {

"name": "com.haojia.test.exam",

"componentData": {

"routePatternPrefix": "exam&/"

}

}

},

"dependencies": {

"minUI5Version": "1.30",

"libs": {

"sap.m": {}

},

"components": {

"com.haojia.test.student": {}

}

},

"rootView": "com.haojia.test.applicationContainer.view.App",

"routing": {

"config": {

"routerClass": "com.haojia.test.util.ModularRouter",

"viewType": "XML",

"viewPath": "com.haojia.test.adminLayout.view",

"transition": "show",

"controlId": "app",

"clearTarget": true,

"controlAggregation": "pages"

},

"routes": [{

"name": "App",

"pattern": "{module}&:all*:"

}]

}

}

Pay attention to the bold text. As a parent module, it defines Student module as a dependencis. And passes routePatternPrefix through componentData. By doing so, we can dynamically add a prefix to the route pattern to resolve multiple module pattern conflicts issue.

Each module has its own router and pattern definition. But the browser url bar is global to every module. So that’s been said, one url can be matched by multiple modules. So it is possible that pattern X can be matched by ApplicationContainer and Student module at same time. To leverage that, ApplicationContainer has only one route pattern: “{module}&:all*:”. This is a fuzzy pattern match which will match every module.

Let’s take a real url example, assume the current url in browser address bar is http://www.haojia.space#/student&/edit/abc

Because ApplicationContainer’s route pattern is “{module}&:all*:”, pattern will match and the {module} parameter is “student”. ApplicationContainer will create and render Student module based on the {module} parameter. Then Student module is been created. the edit route pattern was “edit/{id}” but because of the routePatternPrefix we defined in manifest.json, its runtime route pattern is now “student&/edit/{id}”. Bingo, that url will be matched to edit route without conflicts now!

2.5 Navigation between modules

Modules don’t know each other. But what if you need to navigate from module A to module B? We use SAP EventBus to do the cross module communications. Module A publishes a event to ApplicationContainer. Then ApplicationContainer renders the destination module B accordingly.