Creating Template Contents Dynamically Based on a List of Items

Polymer provides you a way to generate elements based on a list of items using template repeater (dom-repeat).

<dom-module id="employees-list">
    <template>
        <table>
            <tr on-click="processElement">
                <th>Name</th><th>Title</th><th>Email</th>
            </tr>
            <template is="dom-repeat"  items="[[employees]]">
                    <tr on-click="handleClick" id="[[item.name]]">
                        <td>{{item.name}}</td>
                        <td>{{item.title}}</td>
                        <td>{{item.email}}</td>
                    </tr>
            </template>
        </table>
    </template>
    <script>
        class EmployeesList extends Polymer.Element {
            static get is() {return 'employees-list'}
        }
        customElements.define(EmployeesList.is, EmployeesList);
    </script>
</dom-module>

The above template might look like this once populated with a list of employees:

NameTitleEmail

John D

Developer

jd@foo.bar

Jane D

Designer

janed@foo.bar

Mike D

Architect

mikey@foo.bar

The dom-repeat element marks the content that is generated for each item in a list. In the above example the table’s rows and everything inside the row element <tr>…​</tr> is created for each item in the list.

The value of the items attribute declares the items to loop. The item property will be set on each instance’s binding scope so that sub-properties binding should be used.

Note
Refer to dom-repeat element documentation for details.
Note
Not all tags do allow dom-repeat elements inside. In this cases the alternative <template is="dom-repeat"> has to be used.

Populating the List of Items

You should declare a method in the template’s model interface for setting the list of beans that should be shown. The name of the method should match the name in the dom-repeat definition; data for [[employees]] is set by a method named setEmployees.

public class EmployeesTable extends PolymerTemplate<EmployeesModel> {
  public interface EmployeesModel extends TemplateModel {
      @Include({ "name", "title", "email" })
      void setEmployees(List<Employee> employees);

      List<Employee> getEmployees();
  }

  public void setEmployees(List<Employee> employees) {
      getModel().setEmployees(employees);
  }

  public List<Employee> getEmployees() {
      return getModel().getEmployees();
  }
}
Note
The @Include annotation is used here to limit which properties to import into the model. This is done to exclude the id property whose type is unsupported. Alternatively you can use a @Exclude("id") in this specific case.

The Employee bean should have getters corresponding the properties used inside the dom-repeat definition in the template, e.g. getName() for employee.name.

public class Employee {
    private String name;
    private String title;
    private String email;
    private long id;

    public Employee(String name, String title, String email, long id) {
        this.name = name;
        this.title = title;
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public String getTitle() {
        return title;
    }

    public String getEmail() {
        return email;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }
}
Note
Setters are not required here. The template engine will use only the getter to fetch values from employee beans.

The list property updates are propagated only from the server side to the client side. The two-way data binding doesn’t work with the list property. It means that client side changes in the list property are not sent to the server.

In the example below the update in the messages property won’t be sent to the server side if method addItem is called.

class MyTemplate extends Polymer.Element {
    static get properties() {
        return {
            messages: {
                type: Array,
                value: [],
                notify: true
            }
        };
    }
    addItem() {
        this.push('messages', 'foo');
    }
}

Updating the Items

The beans that you add to the model using the setEmployees() method are used to populate the model only. It means that any update made to the bean will not update the model. To be able to update the model items you should use getEmployees() which returns bean proxies which are connected to the model. Any change made to the proxy instance will be reflected to the model.

Here is the way to update the title for all items:

public void updateTitle() {
    getEmployees().forEach(employee -> employee.setTitle("Mr."));
}
Note
You can also use setEmployees() method with a new list of updated beans to repopulate the model. This is not very convenient if you want to update only a single item or a single property.

Accessing item indices

As you may have noticed, there is an event handler in the demo. If you’re unfamiliar with event handlers, please reference the tutorial: Handling User Events in a Template

This event handler is used to demonstrate a shorthand that allows us to access current item index, by annotating the event handler with @RepeatIndex annotation:

@EventHandler
public void processElement(@RepeatIndex int itemIndex) {
    System.out.println(getEmployees().get(itemIndex).getName());
}
Note
There is a limitation: parameter type should be either int or Integer.