You're viewing an older version of Polymer. Please see Polymer 2.0 for the latest.

The data system provides methods for making observable changes to an element's model data (properties and subproperties). Use these methods to make observable changes to arrays and object subproperties.

Related concepts:

A data path is a series of path segments. In most cases, each path segment is a property name. The data APIs accept two kinds of paths:

  • A string, with path segments separated by dots.

  • An array of strings, where each array element is either a path segment or a dotted path.

The following all represent the same path:

"one.two.three"
["one", "two", "three"]
["one.two", "three"]

There are a few special types of path segments.

  • Wildcard paths (like foo.*) represent all changes to a given path and its subproperties, including array mutations.
  • Array mutation paths (like foo.splices) represent all array mutations to a given array.
  • Array item paths (like foo.11) represent an item in an array.

Use the get method to retrieve a value based on its path.

// retrieve a subproperty by path
var value = this.get('myProp.subProp');
// Retrieve the 11th item in myArray
var item = this.get(['myArray', 11])

Use the set method to make an observable change to a subproperty.

// clear an array
this.set('group.members', []);
// set a subproperty
this.set('profile.name', 'Alex');

Calling set has no effect if the value of the property or subproperty hasn't changed. In particular, calling set on an object property won't cause Polymer to pick up changes to the object's subproperties, unless the object itself changes. Likewise, calling set on an array property won't cause Polymer to pick up array mutations.

// DOES NOT WORK—use notifyPath instead
this.profile.name = Alex;
this.set('profile', this.profile);

// DOES NOT WORK—use notifySplices instead
this.users.push({name: 'Grace'});
this.set('users', this.users);

Related tasks:

After making changes to an object subproperty, call notifyPath to make the change observable to the data system.

this.profile.name = Alex;
this.notifyPath('profile.name');

When calling notifyPath, you need to use the exact path that changed. For example, calling this.notifyPath('profile') doesn't pick up a change to profile.name because the profile object itself hasn't changed.

If multiple subproperties have changed, or you don't know the exact changes, see Override dirty checking.

Use Polymer's array mutation methods to make observable changes to arrays.

If you manipulate an array using the native methods (like Array.prototype.push), you can notify Polymer after the fact.

Note that Polymer's array handling has the following constraints:

  • Array items must be unique. The data system uses object identity to compare array items, so array items must be unique.

  • Primitive array items are not supported. This is because primitives (like number, string and boolean values) with the same value are represented by the same object. Consider an array of numbers:

    this.numbers = [1, 1, 2];
    

    The data system can't handle changes to this array property, because the first two items aren't unique.

You can work around these constraints by wrapping primitives in objects to ensure uniqueness:

this.numbers = [{ value: 1}, {value: 1}, {value: 3}];

When modifying arrays, a set of array mutation methods are provided on Polymer element prototypes which mimic Array.prototype methods, with the exception that they take a path string as the first argument. The path argument identifies an array on the element to mutate, with the following arguments matching those of the native Array methods.

These methods perform the mutation action on the array, and then notify other elements that may be bound to the same array of the changes. You must use these methods when mutating an array to ensure that any elements watching the array (via observers, computed properties, or data bindings) are kept in sync.

Every Polymer element has the following array mutation methods available:

  • push(path, item1, [..., itemN])
  • pop(path)
  • unshift(path, item1, [..., itemN])
  • shift(path)
  • splice(path, index, removeCount, [item1, ..., itemN])

Example:

<dom-module id="custom-element">
  <template>
    <template is="dom-repeat" items="[[users]]">{{item}}</template>
  </template>

  <script>
    Polymer({

      is: 'custom-element',

      addUser: function(user) {
        this.push('users', user);
      },

      removeUser: function(user) {
        var index = this.users.indexOf(user);
        this.splice('users', index, 1);
      }

    });
  </script>
</dom-module>

Whenever possible you should always use Polymer's array mutation methods. However, this isn't always possible. For example, you may be using a third-party library that does not use Polymer's array mutation methods. In these scenarios you can call notifySplices after each mutation to ensure that any Polymer elements observing the array are properly notified of the changes.

The notifySplices method requires the array mutations to be normalized into a series of splice operations. For example, calling shift on an array removes the first element of the array, so is equivalent to calling splice(0, 1).

Splices should be applied in index order, so that the element can update its internal representation of the array.

If you don't know the exact changes that occurred (for example, if you manipulate the array using a third-party library), you can force the data system to refresh the entire array—see Override dirty checking.

To retrieve an array item by key, you can simply use the get method described in Get a value by path.

var item = this.get(['myArray', key]);

In some situations, such as inside an observer, you may have an array key or the array item itself, but not have its index. If you have the key or the full path to the item, use get to look up the item. Then use the standard array indexOf method to determine the index.

// Delete an item, based on the item's key
var item = this.get(['myArray', key]);
var index = this.myArray.indexOf(item);
if (index != -1) {
  this.splice('myArray', index, 1, )
}

When processing an observable change, Polymer performs dirty checking and doesn't produce any property effects if the value at the specified path hasn't changed.

Sometimes, when you have a number of changes to an object or array, or you don't know the exact changes to the object or array, it's desirable to skip the dirty checking.

To force the data system to skip the dirty check, set a path to an empty object or array, then back to the original object or array. This makes the data system re-evaluate all property effects related to that path and its subpaths.

// Force data system to pick up subproperty changes
var address = this.address;
this.address = {};
this.address = address;
// Force data system to pick up array mutations
var array = this.myArray;
this.myArray = [];
this.myArray = array;

This pattern also works with set:

// Force data system to reevaluate a subproperty
var members = this.group.members;
this.set('group.members', []);
this.set('group.members', members);

If the property is a large array or a complicated object, this process may be expensive.

Use the linkPaths method to associate two paths. Use linkPaths when an element has two paths that refer to the same object, as described in Two paths referencing the same object.

When two paths are linked, an observable change to one path is observable on the other path, as well.

linkPaths('selectedUser', 'users.1');

Both paths must be relative to the same element. To propagate changes between elements, you must use a data binding.

To remove a path linkage, call unlinkPaths, passing in the first path you passed to linkPaths:

unlinkPaths('selectedUser');