Building settings based on modules
Problem description
Settings are often modular. They could be based on different level of modularity :
- depending on if module has been taken for this profile
- depending on back-end configuration (for example FAQs or channel scan)
- depending on hardware capability (for example HDMI CEC depends on TV capability)
You should avoid having all settings screen and service in one module for two main reasons :
- Build size. Most settings are mandatory module but some are not. So you need to avoid to embed code that does not make sense for a platform.
- Logic. Settings of a module should be part of the module responsible for the feature.
But you also need to mutualize screens and behaviors because there is a similar root to all screens.
For example, this is the first level of settings :

And this is a specific screen that is a classic multi choice settings :

Proposed solution
To define settings structure, use SharedObject in modules.
SharedObject structure and logic
Each module can define SharedObject of type SETTINGS. This will be considered as first level of the settings. Then id of this “first level” could be used as type of other SharedObject to define “subLevel”. For example, based on above schema we have
- SETTINGS
- SETTINGS.PARENTAL_CONTROL
- SETTINGS.PARENTAL_CONTROL.AGE_RATING
- SETTINGS.PARENTAL_CONTROL.CHANNEL_LOCK
- SETTINGS.ACCESSIBILITY
- SETTINGS.ACCESSIBILITY.SYSTEM_LANGUAGE
- SETTINGS.ACCESSIBILITY.AUDIO_LANGUAGE
- SETTINGS.SYSTEM_INFO
For the need of the example, use 2 methods in it :
- getPreviewAsync : will allow to retrieve preview value for an entry if it exists. It can call service of the module if required
- isEnabledAsync : will allow to check if the settings is enabled (i.e. should be visible). For example, CEC will depend on hardware capability, FAQ on BE results…
SettingsTreeService
Create a SettingsTreeService in SettingsModule. Its role is to build settings architecture and allow screens to retrieve those information. It will expose 3 methods :
getEntry: should allow to retrieve information relative to anEntrybased on its idgetChildren: should allow to retrieve children of anEntrybased on its id- call
getSharedObjectand mapSharedObjectto aSettingsEntry(be carefullSharedObjectare mixins, so you should call.create()on them) - should only build a
SettingsEntryif call toisEnabledAsyncreturnstrue - should call
getPreviewAsyncand set result inpreviewValue(do not put methods in aModel) - can do specific stuff based on id (for example FAQ will be dynamically created and can’t be based on
SharedObject)
- call
getParent: should allow to retrieve parent of enEntrybased on its id
Screens
To mutualize screen behaviour it is important that you create multiple AbstractSettingsScren. Then you will be able to inherit from those AbstractSettingsScreen to build SettingsScreen of your module. Do not declare all SettingsScreen in SettingsModule. It is very important to use modularization for screens.
As soon as you arrive at a specific screen, use routing to move to another settings. It will allow you to only keep action and logic specific to the selected settings in one screen. Otherwise, it will become harder and harder to maintain.
Do not forget to add settings module as dependencies in your package.json of your module (modules/myModule/package.json) :
"wtvDependencies": {
"settings": "1.0.0"
}Conclusion
- Mutualise setting screens and views in
SettingsModuleand then define specific settings screens in modules that inherits or uses element of Settings module. - Define settings screens in each module and use routing
- Handle settings choices inside module responsible for the feature instead of inside the module responsible for the settings
- Use
SharedObjectto avoid full module loading for settings tree building