Nested Polymer Elements and Data Binding in Google’s Dart

Nesting Polymer Elements in Dart can be difficult sometimes. I want to give you some ideas how to nest polymer elements in Dart and how to realize data binding between multiple nested elements. Let’s get started:

I would like to seperate between two different ways of nesting elements:

The first way is to nest the element in the actual HTML Code – that’s what I will refer to as „external nesting“:

<parent-element>
  <child-element></child-element>
</parent-element>

But this will not display the child element. Moving the child element into the parent’s shadowDom will do it for you. This is done in the created constructor:

ParentElement.created() : super.created() {
  while(children.length > 0) {
    shadowRoot.append(children[0]);
  }
}

Note that iterating over the list will NOT work as this will result in an exception because we remove elements from the list while iterating over it.

The second way is what I will refer to as „internal“ nesting. The nesting happens in the template tag of our custom element.

<link rel="import" href="child-element.html">
<polymer-element name="parent-element">
  <template>
    <child-element></child-element>
  </template>
</polymer-element>

When using the first approach there is no direct data binding possible. That means the following code does not work:

<parent-element boolattr stringattr="Test">
  <child-element stringattr="{{ stringattr }}"></child-element>
</parent-element>

Let’s look at a full nesting example:

main.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Dart polymer nesting elements</title>
    <link rel="import" href="parent-element.html">
    <link rel="import" href="child-element.html">
  </head>
  <body>
    <parent-element boolattr stringattr="Test">
      <child-element></child-element>
    </parent-element>
    <script type="application/dart" src="dart_polymer_nesting_elements.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </body>
</html>

This example is fairly simple. I want to illustrate how to nest and bind data between elements.

main.dart

import 'package:polymer/polymer.dart';
import 'parent-element.dart';
import 'dataobject.dart';
import 'dart:html';

void main() {
  initPolymer();
  ParentElement parent = querySelector('parent-element');
  DataObject dataObject = new DataObject();
  parent.dataObject = dataObject;
}

The corresponding dart file looks like this. After we initialized the „polymer engine“ we can query for the element, create a DataObject and assign the dataObject to the parent element.

dataobject.dart

library dataobject;

import 'package:observe/observe.dart';

class DataObject extends Observable {
  @observable List list = toObservable([]);
  @observable Map map = toObservable({});
  @observable String name = "DataObject";
}

This dataobject will demonstrate how to bind a custom object between elements. There are three class attributes: a list, a map and a simple string.

Custom Elements

Both the parent and the child are fairly similar. They have two published attributes (stringattr and boolattr). They have to be annotated with @published. Make sure to initialize the boolattr with false. The third attribute refers to the dataobject. The parent element has it annotated with @observable because we will assign it from main.dart. The child element gets the reference to the dataobject by passing it as an attribute. Though the child element uses @published.

The child element also modifies the dataobject. You can change the name variable with an input and add elements to the list.

parent-element.dart

library parentelement;

import 'package:polymer/polymer.dart';
import 'dataobject.dart';

@CustomTag('parent-element')
class ParentElement extends PolymerElement {
  @published String stringattr;

  @published bool boolattr = false;

  @observable DataObject dataObject;

  ParentElement.created() : super.created() {
    print(this.toString() + " created");

    while(children.length > 0) {
      shadowRoot.append(children[0]);
    }
  }

  void enteredView() {
    print(this.toString() + " enteredView");
  }
}

parent-element.html

<link rel="import" href="child-element.html">
<polymer-element name="parent-element">
  <template>
    <div>Parent String Attr: {{ stringattr }}</div>
    <div>Parent Bool Attr {{ boolattr }}</div>
    <div>Parent.dataObject.list = <template repeat="{{ datetime in dataObject.list }}">{{datetime}}, </template></div>
    <div>Parent.dataObject.name = {{ dataObject.name }}</div>
    <hr>
    <child-element boolattr?="{{ boolattr }}" stringattr="{{ stringattr }}" dataObject="{{dataObject}}"></child-element>
  </template>
  <script type="application/dart" src="parent-element.dart"></script>
</polymer-element>

child-element.dart

library childelement;

import 'package:polymer/polymer.dart';
import 'dataobject.dart';

@CustomTag('child-element')
class ChildElement extends PolymerElement {
  @published String stringattr;

  @published bool boolattr = false;

  @published DataObject dataObject;

  ChildElement.created() : super.created() {
    print(this.toString() + " created");
  }

  void enteredView() {
    print(this.toString() + " enteredView");
  }

  void addElement() {
    dataObject.list.add(new DateTime.now());
    print(dataObject.list);
  }
}

child-element.html

<polymer-element name="child-element">
  <template>
    <div>Child String Attr: {{ stringattr }}</div>
    <div>Child Bool Attr {{ boolattr }}</div>
    <div>Child.dataObject.list = <template repeat="{{ datetime in dataObject.list }}">{{datetime}}, </template></div>
    <div>Child.dataObject.name = {{ dataObject.name }}</div>
    <div><input type="text" value="{{ dataObject.name }}"></div>
    <button on-click="{{addElement}}">Add Element to List</button>
  </template>
  <script type="application/dart" src="child-element.dart"></script>
</polymer-element>

I hope this helps you to understand how to nest elements in dart+polymer. If you have questions feel free to add comments.

8 Gedanken zu „Nested Polymer Elements and Data Binding in Google’s Dart

  1. I’m not sure but I thing the question mark for boolean attributes is only necessary for DOM attributes that switch between true/false by the presence/absence of the attribute like i think „disabled“. Would you mind verifying with your demo app?

    • Hi Günter,

      but isn’t that what I am doing here:

      <parent-element boolattr>
      

      boolattr is present, so the presence means it should be true. Removing it should make it false then. This works as intended. But maybe you mean something else?

      Regards,
      Robert

  2. Does this example still works for you?

    In child element for dataObject I always have String value – ‚[Object object]‘

    Not an instance of a custom Object.

    • Hi Victor,

      yes, this example is still working except that you have to manually call

      parent.notifyPropertyChange(#dataObject, null, dataObject);

      after you assign the DataObject instance to the parent element. If you use the latest Polymer version you don’t have to do that!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Please enter the captcha *