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 Tutorial.

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 View-Model Overlay

Declare a view-model 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 view model into the current view model. (If we do not perform this test, Brightspot enters an infinite loop, always inserting a footer view model into another footer view model.)
  • 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 View Model

In the same directory as Footer.java, create a view model 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 view-model logic and styling.

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

See also: