Using Beans with a PolymerTemplate Model

For an introduction to templates see Creating A Simple Component Using the Template API and template models see Binding Model Data in a PolymerTemplate.

Using beans in models provides you with an easy way of defining your model or reusing existing beans for the model. You can use any bean together with the model as long as it has a public no-arg constructor.

A typical use case would be a form where you want to edit the contents of one or more entities. The template for a form for editing a Person bean might for instance look like:

<dom-module id="my-form">
    <template>
        <div>
            <div>
                <span>First name</span>
                <input value="[[person.firstName]]" />
            </div>
            <div>
                <span>Last name</span>
                <input value="[[person.lastName]]" />
            </div>
            <div>
                <span>Age</span>
                <input value="[[person.age]]" />
            </div>
        </div>
        <div>
            <button on-click="setNameToJeff">Set Name To Jeff</button>
        </div>
    </template>

    <script>
        class MyForm extends Polymer.Element {
            static get is() {
                return 'my-form'
            }
        }

        customElements.define(MyForm.is, MyForm);
    </script>
</dom-module>

If you have a Person bean that looks like:

public class Person {
    private String firstName, lastName;
    private int age;
    private Long id;

    public Person() {
        // Needed for TemplateModel
    }

    public Person(String firstName, String lastName, int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public int getAge() {
        return age;
    }

    public Long getId() {
        return id;
    }
}

You can specify your model to use a Person bean by adding a setter and a getter for it:

public interface FormModel extends TemplateModel {
  @Exclude("id")
  void setPerson(Person person);
  Person getPerson();
}
Tip
You can use the @Include annotation and provide a list of properties that you want import (all other properties will be excluded) or @Exclude for a list you want to exclude. In this specific case the @Include({"firstName","lastName","age"}) would be the same as the shown @Exclude("id") annotation.
Tip
You can include and exclude sub bean properties by using the dot-annotation, e.g. @Exclude("account.password").
Note
You don’t have to declare the Person type as a class. It may be declared as an interface. And there is no need to create its instance and use setter. You just may call getPerson() and the model will return for you an empty proxy object which you can use to set its property directly.
Note
When setting backend data directly to the model, you must exclude any bean properties that would lead to circular dependencies. For instance, if a Patient bean would have a property Doctor doctor, the Doctor bean must not have a property of type Patient (or as a generic type for a collection property), as that would lead to a circular dependency.

In the template you can initialize the model by setting a new person instance to it:

public class Form extends PolymerTemplate<FormModel> {
    public Form() {
        Person person = new Person("John", "Doe", 82);
        getModel().setPerson(person);
    }
}
Note
If you later on update the Person person bean created in the constructor, nothing will happen to the model. The bean values are copied by setPerson and the bean is not attached to the model in any way.

To update the values in the model, you can use getModel().getPerson() or getModel().getProxy("person", Person.class) to get a proxy Person object, which is attached to the model. Any changes you do to that proxy object will automatically update the model:

public class Form extends PolymerTemplate<FormModel> {
    @EventHandler
    public void setNameToJeff() {
        getModel().getPerson().setFirstName("Jeff");
    }
}
Note
Your bean will never be stored as a bean in the model, instead the individual parts of the bean will be stored. No method will ever return the original bean to you.
Note
The proxy bean returned by the getter is not meant to be passed on to an EntityManager or similar. It is purely meant for updating the values in the model.
Warning
There is at the time of writing no way to get a detached bean from the model.

When wanting to use model data with an entity manager you need to re-instantiate a new entity and set the values using the getters for the item gotten from the model.

Note! in the example that we can’t send the Person object from the model directly to the service as the the object is proxied and only returns data when the getters are used.

public class OrderForm extends PolymerTemplate<FormModel> {

    public interface FormModel extends TemplateModel {
      @Exclude("id")
      void setPerson(Person person);
      Person getPerson();
    }

    public OrderForm() {
        Person person = new Person("John", "Doe", 82);
        getModel().setPerson(person);
    }

    @EventHandler
    public void submit() {
        Person person = getModel().getPerson();
        getService().placeOrder(new Person(person.getFirstName(), person.getLastName(), person.getAge()));
    }

    private OrderService getService() {
        // Implementation omitted
        return new OrderService();
    }
}