ViewModel functionality in DD4T 2.0 for Java Part III – Complex Field Types

In part II of these series on ViewModel functionality in dd4t–2-java, it was explained how to create ViewModel classes and how to implement basic field types for the mapping between Tridion fields and DD4T ViewModel properties.

This post handles the more complex field types. The reason we make a distinction between ‘simple’ and ‘complex’ is mainly because the complex field types have more options to be deserialized into and need quite some help from DD4T to know what they will become.

To recap, the list of complex field types look like this:

Tridion Field Type DD4T Java Field Type DD4T API Field Type
Multimedia Link ViewModel or MultiMediaImpl or ComponentImpl ComponentLinkField
Component Link ViewModel or ComponentImpl ComponentLinkField
Embedded Schema ViewModel or EmbeddedField EmbeddedField
Keyword Keyword or KeywordImpl KeywordField

Multimedia Links

Multimedia links can be converted into a number of types. The first option is the easiest: simply add a class property in your view model with type: Multimedia. This then gives you back a MultimediaImpl object:

@ViewModel
public class Generic extends TridionViewModelBase {
    @ViewModelProperty (entityFieldName = “multimedialink”)
    private Multimedia multimedia;
}

Secondly, it’s also possible to map the linked Multimedia Component to a default Component object:

@ViewModel
public class Generic extends TridionViewModelBase {
    @ViewModelProperty (entityFieldName = “multimedialink”)
    private Component multimedia;
}

The resulting ComponentImpl object allow you access to all data this Multimedia Component contains – including things like metadata and Schema information.

Finally, it is also possible to map a ViewModel to a Multimedia Component. This allows you to create lightweight ViewModels, while also being able to access metadata. The drawback is that you have to create all needed default properties DD4T gives you in your ViewModel yourself. These are Url, Height, Width, Size, Alt, MimeType, FileExtension and FileName:

@ViewModel
public class Image extends TridionViewModelBase {

    @ViewModelProperty(isMetadata = true)
    private String myMetadataField;

    @ViewModelProperty(entityFieldName = "Url")
    private String url;

    @ViewModelProperty(entityFieldName = "Height")
    private int height;

    @ViewModelProperty(entityFieldName = "Width")
    private int width;

    @ViewModelProperty(entityFieldName = "Size")
    private Integer size;

    @ViewModelProperty(entityFieldName = "Alt")
    private String alt;

    @ViewModelProperty(entityFieldName = "MimeType")
    private String mimeType;

    @ViewModelProperty(entityFieldName = "FileExtension")
    private String fileExtension;

    @ViewModelProperty(entityFieldName = "FileName")
    private String fileName;
}

Component Links

The deserialization of Component content coming from Component Links is done in the same way as for parent Components. This means you can simply add a class property of type Component in order to access the Component content:

@ViewModel
public class Generic extends TridionViewModelBase {
    @ViewModelProperty (entityFieldName = “componentlink”)
    private Component componentContent;
}

It is also perfectly possible to use ViewModels:

@ViewModel
public class Generic extends TridionViewModelBase {
    @ViewModelProperty (entityFieldName = “componentlink”)
    private ComponentLinkModel componentLink;
}

One subtle difference in deserialization of Component Links is that if the property for the Component Link is of a type that is a ViewModel, the Databind framework will attempt to build a model of that field type, using the full data of the linked Component data. That means that there is no need to set the rootElementNames property on that ViewModel.

Embedded Fields

Embedded content can be mapped in a ViewModel by simply adding an EmbeddedField ViewModelProperty:

public class Generic extends TridionViewModelBase {
    @ViewModelProperty
    List<EmbeddedField>embedded;
}

In your view you can then loop over the embedded values as shown in the example below:

<c:forEach var="embeddedField" items="${Generic.embedded.embeddedValues}">
    <%– fieldkey here must be a valid content key in the data –%>
    ${embeddedField.content.fieldkey}
</c:forEach>

While possible, this does look a little clunky. Setting up an Strongly Typed POJO is easier in usage and is fully supported. It has some limitations however: in deserializing the Json into ViewModels for Embedded fields, only the values set in the EmbeddedValues node are used. This means that most of the metadata on the Embedded field is lost, but usually, this should not really be an issue. To create ViewModels, as with multimedia and normal Components, you need to create a separate class where the embedded field can be serialized into:

public class Generic extends TridionViewModelBase {
    @ViewModelProperty
    List<EmbeddedField> embedded;
}

@ViewModel (rootElementNames = {“EmbeddableTest”})
public class EmbeddedOne extends TridionViewModelBase {
    @ViewModelProperty
    private String testfieldOne;

    // Nested embedded schema
    @ViewModelProperty
    private EmbeddedTwo embeddableTwo;
}

@ViewModel(rootElementNames = {“embeddableTestTwo”})
public class EmbeddedTwo extends TridionViewModelBase {
    @ViewModelProperty
    private String testfieldTwo;
}

As you can see in the code above, the databind framework also supports having ViewModels for nested embedded schemas, so creating a ViewModel which can be used in a parent ViewModel which is an Embedded Schema node works fine. A couple of things must be noted:

  • The EmbeddedValues collection node basically now is the starting point to start deserializing. This is a big contrast with using the EmbeddedField class, where that same EmbeddedValues collection is inside the EmbeddedField.
  • Since we now use the EmbeddedValues node as deserialization root and there usually is more than one embedded value coming from a Tridion Component, the handiest thing to do here is to define the embedded field as a List<EmbeddedOne>.
  • It is then possible to use Strong types in your value collection as well, as shown above in EmbeddedOne and EmbeddedTwo classes.
  • It’s a lot cleaner to read values from embedded fields – in JSTL, it is now possible to do:
<c:forEach var="embeddedField" items="${Generic.embedded}">
    ${embeddedField.testfieldOne}
    ${embeddedField.embeddableTwo.testfieldTwo}
</c:forEach>

Keyword fields

In contrast with the other complex field types, deserializing Keyword fields into ViewModels is not supported, but could work. In other words: simply use class properties of the Keyword type to get access to keywords:

public class Generic extends TridionViewModelBase {
    @ViewModelProperty
    private Keyword keyword;
}

About Raimond Kempees

Raimond Kempees is an independent CMS consultant and has for the past 10 years been involved in many fast-paced web projects all over the world, specializing among others in the SDL Tridion platform and building integrations on just about any development platform known to man.