Advanced view modeling
This section describes some advanced techniques for working with view models.
ModelWrapper
Some models wrap models which themselves wrap models. For example, an article can contain two types of image models, one a closeup and another a panorama. Each of those models wraps an image model that is comprised of the raw image and a caption. For the article to access the image—
The article first accesses the intermediate model (CloseUp or Panorama).
From the intermediate model, accesses the image itself.
Brightspot provides a convenience function unwrap
that saves the second step. Placing unwrap
in the intermediate model allows access to the image directly.
Referring to the previous illustration—
Article
includes an abstract marker classImageOption
. This is a useful technique for easily including additional classes intoArticle
: any concrete subclass ofImageOption
automatically appears as an option inArticle
.Each concrete subclass of
ImageOption
,CloseUp
, andPanorama
implementsunwrap
. This method returns theImage
wrapped in either model.
The following example describes how to implement unwrap
as envisioned in the previous illustration.
Step 1: Implement low-level model
import com.psddev.cms.db.Content; import com.psddev.dari.util.StorageItem; public class Image extends Content { private StorageItem rawImage; private String caption; /* Getters and setters */ }
Step 2: Implement intermediate-level models
Step 3: Implement abstract model
import com.psddev.cms.db.Content; import com.psddev.cms.view.ModelWrapper; import com.psddev.dari.db.Recordable; @Recordable.Embedded public abstract class ImageOption extends Content implements ModelWrapper, Directory.Item { }
The previous snippet is an abstract marker class. Any of its concrete subclasses become available to its enclosing class.
Step 4: Implement top-level model
import com.psddev.cms.db.Content; public class Article extends Content implements Page, ImageOption { private ImageOption imageOption; 1 public ImageOption getImageOption() { return imageOption; } }
Declares the |

Step 5: Implement low-level template
This file is saved as Image.hbs
.
<div> <img src="{{imageSrc}}"> </div> <div> {{caption}} </div>
Step 6: Implement low-level data file
This file is saved as Image.json
.
{ "_template": "Image.hbs", 1 "imageSrc": "http://url/to/any/image.jpg", "caption": "Static caption" }
Provides the link between the image's view and the template. |
Step 7: Implement low-level view model
The following snippet provides the data for the embedded image's view.
import com.psddev.cms.db.ImageTag; import com.psddev.cms.view.ViewModel; import styleguide.content.article.ImageView; public class ImageViewModel extends ViewModel<Image> implements ImageView { public String getImageSrc() { 1 return new ImageTag.Builder(model.getRawImage()).toUrl(); } public String getCaption() { 2 return model.getCaption(); } }
Because of the implementation of unwrap
, there is no need to create a view model for the intermediate-level CloseUp
or Panorama
.
Rendering rich text with RichTextViewBuilder
By default, Brightspot's rich-text editors store escaped HTML tags. For example, rich-text editors store <b>bold text</b></b>
as <b>bold text</b>
. To ensure unescaped HTML tags appear in your rendered pages, you need to use the RichTextViewBuilder.buildHtml
method. The following example shows how to implement a rich-text editor and render its output.
Step 1: Render string field as rich-text editor
import com.psddev.cms.db.Content; import com.psddev.cms.db.ToolUi; public class Article extends Content implements Page { @ToolUi.RichText private String body; }
In the previous snippet, the annotation @ToolUi.RichText
renders the body
field as a rich-text editor. For detailed information about customizing the appearance and functionality of the rich-text editor, see Rich text.

For additional information about this annotation, see @ToolUi.RichText.
Step 2: Implement templates
The following file is saved as Article.hbs
.
<div> {{headline}} {{body}} </div>
The following file is saved as Article.json
.
{ "_template": "Article.hbs", "headline": "This is a placeholder for the headline", "body": "This is a placeholder for rich text" }
Step 3: Implement view model
import com.psddev.cms.rte.RichTextViewBuilder; import com.psddev.cms.view.ViewModel; import styleguide.content.article.ArticleView; public class ArticleViewModel extends ViewModel<Article> implements ArticleView { @Override public CharSequence getBody() { 1 return RichTextViewBuilder.buildHtml(model.getBody(), 2 rte -> createView(RichTextElementView.class, rte)); } }

Conditional redirect
Because the onCreate
event occurs before Brightspot generates any of the view's components, you can use it to perform conditional redirects.
import com.psddev.cms.view.ViewModel; import com.psddev.cms.view.ViewResponse; public class ArticleViewModel extends ViewModel<Article> implements ArticleView { protected void onCreate(ViewResponse response) { 1 if (somestatement == true) { response.redirectTemporarily("http://domain/temporary-redirect-page.html"); throw response; 2 } } }
The | |
Terminates the |