About bundles
An app built on top of the App Runtime is composed out of several bundles. Bundles contain the code and resources to build up the whole app. A single bundle typically provides one feature or tool.
Here are some examples of what a bundle could provide:
-
Display a welcome window with configurable content when an app is started.
-
Display a form and send its data to a backend service.
-
Add geometries provided by a web service to the map.
map.apps comes with a bunch of bundles pre-installed that allow to compose rich mapping applications.
You can browse the bundles of a map.apps instance using the JS Registry service: http(s)://<host>/<mapapps-context>/resources/jsregistry/root
.
Examples:
-
https://demos.conterra.de/mapapps/resources/jsregistry/root (con terra’s public instance)
-
http://locahost:9090/resources/jsregistry/root (your development instance, for example)
Structure of a bundle
A bundle is folder that has the following structure:
mybundle/
├── nls/
│ ├── de/
│ │ └── bundle.js
│ └── bundle.js
├── AClass.js
├── Activator.js
├── manifest.json // required
└── module.js
mybundle/
-
Folder containing all files of a bundle. Should be the same name as the bundle defines in the
manifest.json
file. nls/
-
Folder containing all internationalization (i18n) language files.
The details of internationalization are described in the chapter Bundle internationalization. manifest.json
-
The bundle manifest file.
See below for a small introduction into the bundle manifest. module.js
-
The bundle layer file that triggers loading of all JavaScript files required by the bundle.
The section Bundle layer file explains all the details and options. AClass.js
-
Implementation class with certain functionality. Often such classes provide the implementation of a component declared in the bundle manifest.
Activator.js
-
A bundle activator implementation class. A bundle activator can hook into the bundle’s lifecycle and implement special behavior on bundle startup and shutdown.
Bundle manifest
The most important file of a bundle is the manifest.json
file, the bundle manifest.
The bundle manifest provides bundle metadata and defines the functionality provided by the bundle.
A pretty simple bundle manifest might look like this:
manifest.json
file{
"name": "mybundle", (1)
"version": "1.0", (2)
"components": [ (3)
{
"name": "ComponentA",
"impl": "AClass"
}
]
}
1 | The official name of the bundle, this should match the name of the folder (required). |
2 | The version identifier of the bundle (required). |
3 | An array of component configurations declared by the bundle. |
The name of a bundle is used as an identifier within the App Runtime and must be unique among all bundles. Each bundle has to define a version number. It should be incremented if the bundle is changed.
A manifest can have a lot of properties that are listed in the reference of manifest properties.
Bundle dependencies
If a bundle requires another bundle to be installed in the app to work correctly, this requirement should explicitly be expressed in the bundle manifest:
{
...
"dependencies": {
"map-init" : "^4.14.0" // <name> : <version range expression>
},
...
"optionalDependencies": {
"coordinatetransformer" : "^4.14.0"
},
...
}
The App Runtime tests if the required bundles are installed and ensures that theses bundles are started before the bundle that is declaring the dependency. If a bundle has an implementation class dependency to another bundle (if it needs a class of another bundle), declare this dependency to ensure that the needed class is loaded before the bundle is started.
Bundle layer file
When the App Runtime first starts a bundle it looks for the bundle layer file and loads it. This bundle layer file is responsible to load all required JavaScript files of the bundle. A file is required to be loaded if it provides a class that implements a component or bundle activator declared in the bundle manifest.
By default a bundle layer file is assumed to be named module.js
.
It typically looks like this:
export { MyService, AnotherService } from "./Services";
export { FooActivator as Activator } from "./activators";
It just re-exports all required classes, optionally making them available under a different name.
Older bundles might use other styles to load the right classes:
|
Now, consider the following bundle with the module.js
file outlined above:
mybundle/
├── activators.js
├── manifest.json
├── module.js
├── Services.js
└── util.js
The bundle manifest declares two components and an activator:
{ ...
"activator": "Activator", // required class: Activator
"components": [
{
"name": "MyService" // required class: MyService
},
{
"name": "Service2", // required class: AnotherService
"impl": "AnotherService"
}
]
...
}
The bundle layer file module.js
loads all required classes, so the App Runtime can start the bundle.
Bundles without a layer file
Some bundles don’t need to provide a layer file.
This is mainly because they implement a library or define an API, and don’t declare any components themselves.
Instead they are dependencies for other bundles and get imported directly.
In that case you need to set "layer" : ""
in the bundle manifest.
Otherwise the App Runtime will try to load a file named module.js
and fail with an error if it does not exist.
Bundle internationalization
Bundles support internationalization, so you can display messages in bundles, based on the user-selected language, for example.
Defining resources
To provide language-specific strings, a bundle must have a folder nls/
containing bundles.js
files structured like this:
mybundle/
└── nls/ // fixed folder name for i18n resources
├── de/ // German i18n resource directory
│ └── bundle.js // German i18n resource file
└── bundle.js // root i18n resource file (en)
The root i18n file mybundle/nls/bundle.js
contains the English resources.
Only this root i18n file needs to wrap the resource properties in a root
element:
export default {
root: {
bundleName: "My Bundle",
bundleDescription: "The bundle provides some functionality."
},
"de": true // declare other available languages; only required in this root i18n file
};
The file mybundle/nls/de/bundle.js
contains the German resources.
export default {
bundleName: "Mein Modul",
bundleDescription: "Das Modul bietet Funktionen."
};
Referencing resources
A common requirement is to access i18n properties from component instances.
But you can also use ${resourceKey}
expressions to access i18n properties directly inside the bundle manifest.
The next sample shows how to implement a language-aware bundle title:
manifest.json
file{
...
"title": "${bundleName}",
...
}
Bundle lifecycle
A bundle has a defined lifecycle that starts when it is installed into the App Runtime. The lifecycle defines the states a bundle can be in. You can hook into the lifecycle of a bundle by implementing a bundle activator or by listening to bundle events.
The following diagram illustrates a bundle’s states and their transitions:
State | Description |
---|---|
INSTALLED |
This is the initial state of a bundle after the installation into the runtime. |
RESOLVED |
This state is reached during the first start of the bundle. It means that the bundle layer is loaded successfully. |
STARTING |
The bundle is in the start process. |
ACTIVE |
The bundle is successfully started and ready to operate. |
STOPPING |
The bundle is shutting down. |
UNINSTALLED |
The bundle is uninstalled from the runtime and cannot be restarted again. |
Bundle activator
The bundle activator is a special class that a bundle can optionally provide. The App Runtime calls the bundle activator when the bundle that declares it is started or stopped.
A bundle activator needs to be declared in the bundle manifest using the activator
property:
{
...
"activator" : "AClassName"
...
}
The App Runtime calls the start
or stop
method of the activator class.
export default class {
start(bundleContext) {
// do something
}
stop(bundleContext) {
// clean up
}
}
A bundle activator can return a Promise in its start
method.
The App Runtime waits until the Promise is resolved before setting the state of the bundle to ACTIVE.
import Promise from "apprt-core/Promise";
export default class {
start() {
return new Promise((resolve) => {
setTimeout(function() {
resolve(true);
}, 100);
});
}
}
The main class for interacting with the App Runtime is the BundleContext
, which provides all methods to manipulate bundle states, register for framework events or use the service layer.