Indirect View Model Overlay Value

You may need to overlay a View on a given View’s value. To do this, you create a ViewModel overlay that returns an object for a particular key. When the ViewModel overlay adds the object to the ViewModel, it converts the object to a View using the View system to find an appropriate ViewModel. The ViewModel 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 ViewModel overlay to add a footer to an existing View for articles. The example assumes you already have a working parent Model, View, and ViewModel into which you want to insert the overlay’s view. For an example of creating a simple Model, View, and ViewModel for an article, see the tutorial Hello World.

Step 1: Create Child Model

Create a Model Footer.java for the footer.

package content.article;

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 ViewModel Overlay

Declare a ViewModel overlay class FooterViewModelOverlay, and insert an instance of the footer’s Model into the overlay.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package content.article;

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

import java.util.Map;
import java.util.function.Supplier;

public class FooterViewModelOverlay implements ViewModelOverlay {

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

        if (model instanceof Footer) {
            return null;
        }

        Map<String, Supplier<Object>> overlay = new CompactMap<>();
        Footer footer = new Footer();
        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);
        return overlay;
    }

}

In the previous snippet—

  • Line 10 declares a class that implements ViewModelOverlay. Any class that implements this interface makes its overlay map available to calling parent Views.
  • Lines 15–17 test if the current Model is a Footer. If it is, do not inject the ViewModel into the current ViewModel. (If we do not perform this test, Brightspot enters an infinite loop, always inserting a Footer ViewModel into another Footer ViewModel.)
  • Line 19 instantiates an overlay map.
  • Lines 20–24 instantiate the child Model and set its properties.
  • Line 25 adds the Model to the overlay map using the key footer.

Step 5: Create Child ViewModel

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
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 {

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

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

In the previous snippet, line 7 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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<html>
    <body>
        <div class="Article">
            <div class="Article-headline">
                <h1>{{headline}}</h1>
            </div>
            <div class="Article-body">
                {{body}}
            </div>
            <div>
                {{footer}}
            </div>
        </div>
    </body>
</html>

In the previous snippet, line 11 contains the placeholder {{footer}}. At run time, Brightspot replaces the placeholder with the entire template created in Step 2, along with the associated ViewModel logic and styling.

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

See also: