Support and Documentation

View-model overlays

A view-model overlay is a mechanism for adding data to a view. Brightspot "overlays" the additional data on top of the views' data produced by the view models. The data may be related to the underlying model producing the view, or it could be used to provide common data across all views. For example, you can easily add a permalink to all of your project's pages with a view-model overlay. This feature also saves you the effort of possibly extending existing models or creating a new class hierarchy to add the object to many view models.

Direct view model overlay value

You can directly add a specific view model overlay key's value to every view. The following example describes how to create a view model overlay that inserts a permalink into each view. This example works best if you define a default URL for the site as described in Settings for the Global site or Creating sites.

Step 1: Create model

You can use any new or existing model with an overlay. No extra coding is required. For an example of a simple model, the snippet Simple content type with text and image fields.

Step 2: Create view model overlay

Example 32. Creating a view model overlay
package content.article;

import com.psddev.cms.view.ViewModelOverlay;
import com.psddev.dari.db.State;
import com.psddev.dari.util.CompactMap;
import java.util.Map;
import java.util.function.Supplier;
import com.psddev.cms.db.Directory;

public class PermalinkViewModelOverlay implements ViewModelOverlay { 1

    @Override
    public Map<String, Supplier<Object>> create(Object model, String viewTemplate) {
        Map<String, Supplier<Object>> overlay = new CompactMap<>(); 2
        overlay.put("fullPermalink",() -> State.getInstance(model).as(Directory.ObjectModification.class).getFullPermalink()); 3
        return overlay;
    }
}

1

Declares a class that implements ViewModelOverlay. Any class that implements this interface injects its key-value pairs into every view.

2

Creates a map of key-value pairs.

3

Retrieves the current page's permalink, and assigns it to the key fullPermalink. (For this example to work you also need to configure the Default Site URL as explained in Settings for the Global site.) You can add any number of key-value pairs to the overlay.



Step 3: Create data file

You can use any new or existing Styleguide JSON data file. No extra coding is required. For an example of a simple data file, see the snippet Sample data file.

Step 4: Insert overlay key Into template

Referring to the snippet Creating a view model overlay, you now have access to the key fullPermalink in every view by inserting that key as a placeholder in a Handlebars template. When rendering the view, Brightspot retrieves the value for the placeholder without your implementing a method in any view model.

<html>
    <body>
        <div class="Article">
            <div class="Article-headline">
                <h1>{{headline}}</h1>
            </div>
            <div class="Article-body">
                {{body}}
            </div>
            <div class="Article-body">
                Permalink to this item: {{fullPermalink}}
            </div>
        </div>
    </body>
</html>

In the previous snippet, the placeholder {{fullPermalink}} is the key contained in the overlay.

view-model-overlay-rendered.svg
Indirect view model overlay value

You may need to overlay a view on a given view's value. To do this, you create a view model overlay that returns an object for a particular key. When the view model overlay adds the object to the view model, it converts the object to a view using the view system to find an appropriate view model. The view model for this particular object must be marked with the ViewModelOverlayValueEntryView interface to indicate to the view system the appropriate view to use for the object in the context of the view model overlay.

The following example describes how to use a view model overlay to add a footer to an existing view for articles. The example assumes you already have a working parent model, view, and view model into which you want to insert the overlay's view. For an example of creating a simple model, view, and view model for an article, see the Tutorial.

Step 1: Create child model

Create a model Footer.java for the footer.

import com.psddev.cms.db.Content;
import com.psddev.dari.util.StorageItem;

public class Footer extends Content {

    private StorageItem imageFile;
    private String caption;

    /* Getters and setters */

}

Step 2: Create child template

Create a template Footer.hbs for the footer. The overlay injects this template as it renders the parent view.

<div class="Footer">
    <p class="Footer-p">{{caption}}</p>
    <p class="Footer-p"><img src="{{imagefile}}"/></p>
</div>

Step 3: Create child data file

In the same directory as Footer.hbs, create the corresponding data file Footer.json.

{
  "_template": "Footer.hbs",
  "caption": "{{words(4)}}",
  "imagefile": "{{image(100, 100)}}"
}

Step 4: Create view model overlay

Declare a view model overlay class FooterViewModelOverlay, and insert an instance of the footer's model into the overlay.

import com.psddev.cms.view.ViewModelOverlay;
import com.psddev.dari.util.CompactMap;
import com.psddev.dari.util.UrlStorageItem;

public class FooterViewModelOverlay implements ViewModelOverlay { 1

    @Override
    public Map<String, Supplier<Object>> create(Object model, String viewTemplate) {

        if (model instanceof Footer) { 2
            return null;
        }

        Map<String, Supplier<Object>> overlay = new CompactMap<>(); 3

        Footer footer = new Footer(); 4
        UrlStorageItem file = new UrlStorageItem();
        file.setPath("http://cdn.perfectsense.psdweb.psdops.com/6d/5d/efd1da434d6abd8b1b08b8b29c4c/brightspot-logo-183.png");
        footer.setImageFile(file);
        footer.setCaption("This web site delivered by:");
        overlay.put("footer", () -> footer); 5
        return overlay;
    }

}

1

Declares a class that implements ViewModelOverlay. Any class that implements this interface makes its overlay map available to calling parent views.

2

Tests if the current model is a Footer. If it is, do not inject the view model into the current view model. (Without this test, Brightspot enters an infinite loop, always inserting a footer view model into another footer view model.)

3

Instantiates an overlay map.

4

Instantiates the child model and set its properties.

5

Adds the model to the overlay map using the key footer.

Step 5: Create child view model

In the same directory as Footer.java, create a view model FooterViewModel.java.

package content.article;

import com.psddev.cms.view.ViewModel;
import com.psddev.cms.view.ViewModelOverlayValueEntryView;
import styleguide.content.article.FooterView;

public class FooterViewModel extends ViewModel<Footer> implements FooterView, ViewModelOverlayValueEntryView { 1

    @Override
    public CharSequence getImagefile() {
        return model.getImageFile().getPublicUrl();
    }

    @Override
    public CharSequence getCaption() {
        return model.getCaption();
    }
}

1

Adds the marker interface ViewModelOverlayValueEntryView. Brightspot uses this marker as an indication to insert the rendered view in any parent view that calls the corresponding overlay key you created in Step 4.

Step 6: Add overlay key to parent template

Open the template for which you want to include the overlay, and use the overlay key used in Step 4.

<html>
    <body>
        <div class="Article">
            <div class="Article-headline">
                <h1>{{headline}}</h1>
            </div>
            <div class="Article-body">
                {{body}}
            </div>
            <div>
                {{footer}} 1
            </div>
        </div>
    </body>
</html>

1

At run time, Brightspot replaces the placeholder {{footer}} with the entire template created in Step 2, along with the associated view model logic and styling.

view-model-overlay-value-entry-view.svg