The term component is a developer term that here refers to Java objects, not templating components.
A component provides a service such as keeping a registry of templates.
Components expose their functionality to other components through an
interface or an API. A typical component depends on a number of other
components and benefits from dependency injection as Magnolia will
connect components to satisfy their dependencies. Components are either
singletons, which means that they are around for the lifetime of the
server, or they are tied to shorter durations such as the duration of an
HTTP request or a user session. The duration is known as scope.
Apps and components
Each app has its own component provider that uses components in the
app-<app name> group. Each subapp has a component provider that uses
components described for it. Component providers have the app component
provider as their parent:
Defining app components in a module descriptor
Define your components in the module descriptor. Here is a minimal
module descriptor that doesn’t declare any components yet. It just
declares the module name and version.
A minimal module descriptor
<!DOCTYPE moduleSYSTEM"module.dtd"><module><name>ui-helloworld-app</name><displayName>Hello World App</displayName><description>The Famous Hello World App</description><version>1.0</version></module>Copy
Defining an app or subapp as a component
Best practice
Defining something as a component allows other developers to quickly change
the implementation. For this reason, you should define anything that is
likely to be changed or customized later as a component. For example,
define your apps and subapps as components. Views are also common
customization points so define the views returned by subapps as
components. Any components you define are available for injection. This
is also true for components further up the hierarchy in parent
components.
In the module descriptor file, add a section for each component. Name
the components using the following convention:
app-<app name>
app-<app name>-<subapp name>
The <app name> is the name configured in the app configuration in
AdminCentral. For example:
Use a
Maven
archetype to get a ready-made Magnolia module structure. An archetype
has a top-level project and a webapp configured as a Maven submodule.
Structure your app project in the IDE as shown in the example below.
Put the app and subapp classes in the same folder.
Export the app configuration and the
app launcher
configuration into XML and put them in the resources folder. The
module descriptor file goes in resources/META-INF.magnolia.
App project structure in your IDE:
Implementing an app
Create a class that implements the App interface, or extend the
BaseApp class. The second option is more convenient.
In your app class, implement any custom methods or functionality you
need.
Inject the AppContext and AppView as a dependency.
This is only needed if you want to extend the App by custom functionality.
Otherwise use info.magnolia.ui.framework.app.BaseApp as the app class in the configuration.
Implementing an app class and injecting a dependency on AppContext.
To start an app, you can either search for it In the Find Bar or click
the app launcher icon (mgnl-app-launcher: [])and then click the
icon. To do this programmatically, you can trigger a LocationChange
event by calling LocationController.goTo() with the location as an
argument.
The AppController will now take care of opening the correct app and
subapp based on the app name and subapp ID as defined in the
configuration.
To hook into the starting cycle or location handling of an app:
When an app stops, you get a callback to the stop method to do
cleanup. The cleanup can involve closing files, closing network
connections and so on.
Stopping an app
@Overridepublicvoidstop(){
// code to extend default behavior
}Copy
Handling location changes
When your app is requested with a location fragment, you might need to
handle changing the location. In your app class, override the
locationChanged method.
Remember, if the subapp is requested with a location that is already
open in a running subapp, the Magnolia Shell takes care of bringing that
subapp to focus instead of starting a new one.
SubApp
Subapp is an app that runs in its own tab. For example, the Pages app
always has the main subapp open in the first tab and multiple detail
subapps for each page that is edited. The main subapp has the label
Pages while the detail subapps are labeled according to the page
title, for example Travel Home.
When the subapp starts it must return a view. The view draws a user
interface on the tab so that there is something for the user to interact
with. The view takes up the whole tab and adds a caption.
Implementing a SubApp
Create a class that implements the SubApp interface or extend the
BaseSubApp class. The second option is more convenient. In your subApp
class, implement any custom methods or functionality you need. Inject
the SubAppContext and a view associated with the subApp as a
dependency.
The default implementation for starting and changing the location of
Apps is being delegated to the actual subApp defined in the location.
If a subApp with the referenced subApp Id is already running, it will
be asked whether it actually supports the location.
In this case all locations with a reference to its subApp Id will be
handled by this subApp instance. By overriding this method and make it
return false you would open a new tab for every location change the
AppController gets which is targeted to this subAppId. To open a new tab
based on the parameters passed to the subApp, it would look like this:
To hook into the starting cycle or location handling of an App:
publicclassMySubAppextendsBaseSubApp{
...
@OverrideprotectedvoidonSubAppStart(){
// code to extend default behavior
}
@OverrideprotectedvoidonSubAppStop(){
// code to extend default behavior
}
@OverridepublicvoidlocationChanged(Location location){
super.locationChanged(location);
// code to extend default behavior
}
}Copy
Module dependencies
You need to declare your dependencies in the module descriptor. You can
define runtime or install time dependencies, not build dependencies. If
you define a dependency on another module, then this module will be
installed and started before your module. In order to develop a basic
app, add the following section to your module descriptor:
This is a step-by-step guide on how to implement a basic hello world
app. When started, it shows a main subApp containing two buttons.
Clicking on a button should open a new greeter subApp greeting the
user associated with the button.
Main SubApp
The logic is taken care of by the HelloWorldMainSubApp which adds
buttons to the view and listens to actions by implementing the
Listener interface.
In addition to the SubAppContext and the View we also inject the
LocationController which will be used to open the greeter subApp.
Creating a main subapp
publicclassHelloWorldMainSubAppextendsBaseSubAppimplementsHelloWorldMainSubAppView.Listener{
private LocationController locationController;
@InjectpublicHelloWorldMainSubApp(final SubAppContext subAppContext, HelloWorldMainSubAppView view, LocationController locationController){
super(subAppContext, view);
this.locationController = locationController;
}
@OverrideprotectedvoidonSubAppStart(){
getView().setListener(this);
getView().addUser("Lisa");
getView().addUser("Mark");
}
@Overridepublic HelloWorldMainSubAppView getView(){
return (HelloWorldMainSubAppView) super.getView();
}
/**
* When a button gets clicked in the {@link HelloWorldMainSubAppView} it will call back to this method.
* The {@link LocationController#goTo(info.magnolia.ui.framework.location.Location)} will cause the app framework to handle the location change event.
*/@OverridepublicvoidgreetUser(String user){
locationController.goTo(new DefaultLocation(DefaultLocation.LOCATION_TYPE_APP, getAppContext().getName(), "greeter", user));
}
}Copy
A simple interface providing methods to add buttons in the view.
View Interface and Listener for actions in the view
When the user clicks one of the buttons, a new location is requested.
The AppController will take care of redirecting it to the hello world
app and launching the greeter subApp.
A simple interface providing a method to set the name.
The HelloWorldGreeterSubApp will override the default caption of the
tab by the name passed as a parameter and set it on the view.
Creating a main subapp
publicclassHelloWorldGreeterSubAppextendsBaseSubApp{
private String name;
@InjectprotectedHelloWorldGreeterSubApp(final SubAppContext subAppContext, final HelloWorldGreeterSubAppView view){
super(subAppContext, view);
}
/**
* Extracts the name to greet from the {@link Location}s parameter and passes it to the {@link HelloWorldGreeterSubAppView}.
*/@OverrideprotectedvoidonSubAppStart(){
this.name = getCurrentLocation().getParameter();
getView().setGreeting(name);
}
@Overridepublic HelloWorldGreeterSubAppView getView(){
return (HelloWorldGreeterSubAppView)super.getView();
}
/**
* Used to set the label on the tab.
*/@Overridepublic String getCaption(){
return name;
}
/**
* In case there is a subApp instance running, here the decision is made whether this instance will handle the new {@link Location}.
*/@OverridepublicbooleansupportsLocation(Location location){
String newUser = location.getParameter();
return getCurrentLocation().getParameter().equals(newUser);
}
}Copy