Using Model Encoders with a PolymerTemplate Model
Sometimes you may want to use ready made beans in your model which you can’t control (i.e. they are given to you as binary class files). But their structure may be inappropriate for your template model used in a user interface representation.
It usually happens if you use data base backend with JPA entities. Such entities usually have unique identifier with Long
type. Long
type is not supported by Flow at all (it can’t be mapped correctly to any client type). So you have to exclude any property with this type (see @Exclude
in Using Beans with a PolymerTemplate Model).
But what if you need this property on the client side for some reason (e.g. you want this identifier of a bean to be able to reference it)? In this case you can use @Encode
annotation. You can just encode a Long
value to a String
value and it will be sent to the client-side as a string. Then once the client side sends this value back it will be decoded back to a Long
.
Here is the Person
JPA entity class with the model class :
Java
@Entity
public class Person implements Serializable {
@Id
@GeneratedValue
private Long id;
public Long getId() {
return id;
}
}
public interface MyModel extends TemplateModel {
@Encode(value=LongToStringEncoder.class, path="id")
void setPerson(Person person);
Person getPerson();
}
And here is its encoder:
Java
public class LongToStringEncoder implements ModelEncoder<Long, String> {
@Override
public String encode(Long modelValue) {
return Optional.ofNullable(modelValue).map(Object::toString)
.orElse(null);
}
@Override
public Long decode(String presentationValue) {
return Optional.ofNullable(presentationValue).map(Long::valueOf)
.orElse(null);
}
}
Now you can access id
property of the Person
bean in your code on the client side in the usual way but it will have the String
type.
Note | The annotation parameter path=”id” is used to address the id sub-property of the person property. By default the path value is “” which means that an encoder is applied to the property itself. |
You can use any type to encode to any supported type. Another usecase for encoders may be splitting one property value to several sub-properties to use them in different UI controls instead of one. E.g. we may want to have 3 input fields for a person birthday instead of one: one for the day, month and year. So you have a Date
property in your model:
Java
public interface MyModel extends TemplateModel {
Date getBirthDate();
@Encode(DateToDateBeanEncoder.class)
void setBirthDate(Date birthDate);
}
Then you may define a bean DateBean
:
Java
public class DateBean implements Serializable {
private String day;
private String month;
private String year;
public String getDay() {
return day;
}
public void setDay(String day) {
this.day = day;
}
public String getMonth() {
return month;
}
public void setMonth(String month) {
this.month = month;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
}
and the encoder class:
Java
public class DateToDateBeanEncoder implements ModelEncoder<Date, DateBean> {
@Override
public DateBean encode(Date modelValue) {
if (modelValue == null) {
return null;
}
DateBean bean = new DateBean();
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(modelValue);
bean.setDay(Integer.toString(calendar.get(Calendar.DAY_OF_MONTH)));
bean.setMonth(Integer.toString(calendar.get(Calendar.MONTH) + 1));
bean.setYear(Integer.toString(calendar.get(Calendar.YEAR)));
return bean;
}
@Override
public Date decode(DateBean presentationValue) {
if (presentationValue == null) {
return null;
}
int year = Integer.parseInt(presentationValue.getYear());
int day = Integer.parseInt(presentationValue.getDay());
int month = Integer.parseInt(presentationValue.getMonth()) - 1;
Calendar calendar = GregorianCalendar.getInstance();
calendar.set(year, month, day);
return calendar.getTime();
}
}
Now you can use the following HTML template file for your component (here is only the template
snippet):
HTML
<template>
<div style="width: 200px;">
<label>Birth date:</label>
<label for="day">Enter your birthday:</label><paper-input id="day" value="{{birthDate.day}}"></paper-input>
<label for="month">Enter the month of your birthday:</label><paper-input id="month" value="{{birthDate.month}}"></paper-input>
<label for="year">Enter the year of your birthday:</label><paper-input id="year" value="{{birthDate.year}}"></paper-input>
<button on-click="commit" id="commit">Commit</button>
</div>
</template>
So here one Date
property is encoded to 3 sub-properties: the day, month and year. Each of them has its own editor but on the server side it’s still the same one property birthDate
.
Note | Please note that you still need use your original property name birthDate in this example to access to sub-properties. So those 3 sub-properties requires prefix which is the original property name and its name is still the same birthDate (and not a dateBean e.g.). |