UI Context API

This page describes the purpose, scope and use cases of the UIComponentContext in the Magnolia 6 UI framework.

The UiComponentContext is used to share a state between UI components.

Example
import info.magnolia.ui.framework.ContextProperty;
import info.magnolia.ui.framework.UiComponentContext;

public interface ExampleContext extends UiComponentContext {
    ContextProperty<String> value(); //implementation of ContextProperty is created at runtime
}

UiComponentContext has to be injected into a constructor, typically a constructor of a component or an action.

import info.magnolia.ui.ValueContext;
import javax.inject.Inject;

public class Injection {

    @Inject
    public Injection(ValueContext<String> valueContext) {
        //...
    }
}

Scope

UIComponent can create instances of UiComponentContext which are shared by child components.

import info.magnolia.ui.UIComponent;
import info.magnolia.ui.ValueContext;

import javax.inject.Inject;

import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;

public class ParentComponent extends VerticalLayout implements UIComponent {

    public ParentComponent() {
        final ValueContext<String> valueContext = bindContext(ValueContext.class);
        valueContext.set("sharedValue");
        //the ValueContext instance created above gets injected into child components:
        final ChildComponent componentA = create(ChildComponent.class);
        final ChildComponent componentB = create(ChildComponent.class);
        addComponents(componentA, componentB);
    }

    public static class ChildComponent extends Label {
        @Inject
        public ChildComponent(ValueContext<String> valueContext) {
            setCaptionAsHtml(true);
            setValue(valueContext.getSingleOrThrow());
        }
    }
}

Observation

ContextProperty can react upon value changes:

import info.magnolia.ui.ValueContext;
import info.magnolia.ui.framework.ContextProperty;

import javax.inject.Inject;

import com.vaadin.ui.Notification;

public class Observation {

    @Inject
    public Observation(ValueContext<?> valueContext) {
        final ContextProperty<?> current = valueContext.current();
        current.observeNullable(strings -> Notification.show(strings + " are selected!"));
    }
}

Use cases

  • ValueContext, holds the selection or value that the related UI components operate with.

  • LocaleContext, used in various editors which:

    • Allow editing localized content.

    • Support switching between supported languages within the same session.

  • FilterContext, a data filter context. Typically, it is mutated by the UI and consumed by Vaadin data providers in order to filter the displayed data sets accordingly.

  • info.magnolia.ui.contentapp.ContentBrowserSubApp.LocationContext or info.magnolia.ui.contentapp.detail.ContentDetailSubApp.LocationContext, holds the location of a browser or detail subapp.

In the Browser subapp

The ValueContext and LocaleContext are injected into the Browser subapp. As these contexts have not yet been bound in the parent component, the injection mechanism creates new instances.

These contexts are synchronized. The ValueContext changes are reflected into the location and vice versa.

Grid view

The ValueContext changes (the server side updates such a new item creation, for example) are propagated to a grid selection and a grid selection (when the user selects an item, for example) is propagated to the ValueContext). This mechanism ensures that the ValueContext — injected for example into the actions — points to an up-to-date selection.

The FilterContext holds column filter updates and propagates those into the grid’s data provider.

Action bar

Action is a typical consumer of an injected UIContext.

import info.magnolia.ui.ValueContext;
import info.magnolia.ui.api.action.AbstractAction;

import javax.inject.Inject;
import javax.jcr.Node;
import javax.jcr.RepositoryException;

public class ExampleAction extends AbstractAction<ActionDefinition> {

    private final ValueContext<Node> valueContext;

    @Inject
    public ExampleAction(ActionDefinition actionDefinition, ValueContext<Node> valueContext) {
        super(actionDefinition);
        this.valueContext = valueContext;
    }

    @Override
    public void execute()  {
        final Node currentSelection = valueContext.getSingleOrThrow();
        try {
            final Node newSelection = currentSelection.addNode("aNewChildNode", "mgnl:content");
            newSelection.getSession().save();
            valueContext.set(newSelection);
        } catch (RepositoryException e) {
            throw new RuntimeException(e);
        }
    }
}

In dialogs

Dialogs opened in the grid view are populated from the same instance of the ValueContext.

Link field choosers are a good example of a component specific UIContext. The contexts in the choosers should not be synchronized with the contexts of the underlying grid from which the dialog is opened.
Even though the detail subapps are opened from the same browser, each subapp holds its own ValueContext. Therefore, the form can be written to its own underlying item.
Feedback