874 lines
24 KiB
Markdown
Executable File
874 lines
24 KiB
Markdown
Executable File
# Data system concepts
|
|
|
|
1. Observers- Callbacks invoked when data changes.
|
|
|
|
2. Computed properties- Virtual properties computed based on other properties, and recomputed when the input data changes.
|
|
|
|
3. Data bindings- Annotations that update the properties, attributes, or text content of a DOM node when data changes.
|
|
|
|
Simple element:
|
|
|
|
class NameCard extends PolymerElement {
|
|
constructor() {
|
|
super();
|
|
this.name = {first: 'Kai', last: 'Li'};
|
|
}
|
|
static get template() {
|
|
return html `
|
|
<div>[[name.first]] [[name.last]]</div>
|
|
`;
|
|
}
|
|
}
|
|
customElements.define('name-card', NameCard);
|
|
|
|
|
|
1. The `<name-card>` element has a name property that refers to a JavaScript object.
|
|
|
|
|
|
2. The `<name-card>` element hosts a local DOM tree, that contains a single <div> element.
|
|
|
|
|
|
3. Data bindings in the template link the JavaScript object to the <div> element.
|
|
|
|
|
|
For example,
|
|
|
|
the `<name-card>` element has data bindings for the paths "name.first" and "name.last"
|
|
|
|
|
|
## Observable changes
|
|
-Setting a direct property of the element.
|
|
|
|
this.owner = 'Jane';
|
|
|
|
-Setting a subproperty of the element using a two way data binding.
|
|
|
|
<local-dom-child name="{{hostProperty.subProperty}}"></local-dom-child>
|
|
|
|
## Unobservable changes
|
|
|
|
-Setting a subproperty of an object.
|
|
|
|
// unobservable subproperty change
|
|
this.address.street = 'Elm Street';
|
|
|
|
-Mutating an array:
|
|
|
|
// unobservable change using native Array.push
|
|
this.users.push({ name: 'Maturin'});
|
|
|
|
## Mutating objects and arrays observably
|
|
Polymer provides methods for making observable changes to subproperties and arrays:
|
|
|
|
// mutate an object observably
|
|
this.set('address.street', 'Half Moon Street');
|
|
|
|
// mutate an array observably
|
|
this.push('users', { name: 'Maturin'});
|
|
|
|
#### Batched property changes
|
|
|
|
You can atomically set multiple properties using the setProperties method.
|
|
|
|
this.setProperties({item: 'Orange', count: 12 });
|
|
|
|
For example,
|
|
|
|
// observer fires twice
|
|
this.a = 10;
|
|
this.b = 20;
|
|
|
|
// observer fires once
|
|
this.setProperties({a: 10, b: 20});
|
|
The two types of data binding annotations are:
|
|
|
|
|
|
### 1 - Automatic,
|
|
which allows upward (target to host) and downwards (host to target) data flow. Automatic bindings use double curly brackets ({{ }}):
|
|
|
|
<my-input value="{{name}}"></my-input>
|
|
### 2 - One-way,
|
|
which only allows downwards data flow. Upward data flow is disabled. One-way bindings use double square brackets ([[ ]]).
|
|
|
|
<name-tag name="[[name]]"></name-tag>
|
|
|
|
#### Example of one-way binding (downward)
|
|
|
|
class XHost extends PolymerElement {
|
|
static get template() {
|
|
return html`
|
|
<x-target some-prop="[[value]]"></x-target>
|
|
`;
|
|
}
|
|
}
|
|
....................
|
|
|
|
...
|
|
class XTarget extends PolymerElement {
|
|
static get properties() {
|
|
return {
|
|
someProp: {
|
|
type: String //no notify: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
...
|
|
|
|
...
|
|
class XHost extends PolymerElement {
|
|
static get template() {
|
|
return html`
|
|
<!-- changes to "value" propagate downward to "someProp" on target -->
|
|
<!-- changes to "someProp" are not notified to host due to notify:falsey -->
|
|
<x-target some-prop="{{value}}"></x-target>
|
|
`;
|
|
}
|
|
}
|
|
...
|
|
|
|
#### Example of one-way binding (upward, child-to-host)
|
|
|
|
|
|
...
|
|
class XTarget extends PolymerElement {
|
|
static get properties() {
|
|
return {
|
|
someProp: {
|
|
type: String,
|
|
notify: true,
|
|
readOnly: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
...
|
|
customElements.define('x-target', XTarget);
|
|
...
|
|
|
|
class XHost extends PolymerElement {
|
|
static get template() {
|
|
return html`
|
|
<!-- changes to "value" are ignored by child because "someProp" is read-only -->
|
|
<!-- changes to "someProp" propagate upward to "value" on host -->
|
|
<x-target some-prop="{{value}}"></x-target>
|
|
`;
|
|
}
|
|
}
|
|
...
|
|
customElements.define('x-host', XHost);
|
|
|
|
#### Example of no data flow / nonsensical state
|
|
|
|
...
|
|
class XTarget extends PolymerElement {
|
|
static get properties() {
|
|
return {
|
|
someProp: {
|
|
type: String,
|
|
notify: true,
|
|
readOnly: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
...
|
|
customElements.define('x-target', XTarget);
|
|
...
|
|
|
|
class XHost extends PolymerElement {
|
|
static get template() {
|
|
return html`
|
|
<!-- changes to "value" are ignored by child because "someProp" is read-only -->
|
|
<!-- changes to "someProp" don't propagate upward because of the one-way binding -->
|
|
<x-target some-prop="[[value]]"></x-target>
|
|
`;
|
|
}
|
|
}
|
|
...
|
|
customElements.define('x-host', XHost);
|
|
|
|
|
|
## Property effects
|
|
|
|
1. Computed properties.
|
|
2. Data bindings.
|
|
3. Reflected values.
|
|
4. Observers.
|
|
5. Change notification events.
|
|
|
|
|
|
#### Data bindings
|
|
|
|
Two-way property binding:
|
|
|
|
target-property="{{hostProperty}}"
|
|
|
|
One-way property binding:
|
|
|
|
target-property="[[hostProperty]]"
|
|
|
|
Attribute binding:
|
|
|
|
target-attribute$="[[hostProperty]]"
|
|
|
|
# Work with object and array data
|
|
|
|
Get a value by path
|
|
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])
|
|
|
|
### Set a property or subproperty by path
|
|
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');
|
|
|
|
### Working with arrays
|
|
Example
|
|
|
|
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
|
|
import '@polymer/polymer/lib/elements/dom-repeat.js';
|
|
|
|
class XCustom extends PolymerElement {
|
|
static get template() {
|
|
return html`
|
|
<template is="dom-repeat" items="[[users]]"><p>{{item}}</p></template>
|
|
<p><button on-click="addUser">add</button><button on-click="removeUser">remove</button><button on-click="_reset">reset</button></p>
|
|
`;
|
|
}
|
|
constructor() {
|
|
super();
|
|
this._reset();
|
|
}
|
|
addUser() {
|
|
var user = "wing sang";
|
|
this.push('users', user);
|
|
}
|
|
removeUser() {
|
|
var user = "wing sang";
|
|
var index = this.users.indexOf(user);
|
|
this.splice('users', index, 1);
|
|
}
|
|
_reset() {
|
|
this.users=["wing sang", "deshaun", "kelley"];
|
|
}
|
|
}
|
|
customElements.define('x-custom', XCustom);
|
|
|
|
### Batch multiple property changes
|
|
|
|
Use **setProperties** method to make a batch change to a set of properties. This ensures the property changes run as a coherent set.
|
|
|
|
this.setProperties({
|
|
date: 'Jan 17, 2017',
|
|
verified: true
|
|
});
|
|
|
|
**setProperties** supports an optional **setReadOnly** flag as the second parameter. If you need to set read-only properties as part of a batch change, pass true for the second parameter:
|
|
|
|
this.setProperties({
|
|
date: 'Jan 17, 2017',
|
|
verified: true
|
|
}, true);
|
|
|
|
#### Link two paths to the same object
|
|
|
|
linkPaths('selectedUser', 'users.1');
|
|
|
|
|
|
To remove a path linkage, call **unlinkPaths**, passing in the first path you passed to linkPaths:
|
|
|
|
unlinkPaths('selectedUser');
|
|
|
|
|
|
# Observers and computed properties
|
|
- Simple observers observe a single property.
|
|
- Complex observers can observe one or more properties or paths.
|
|
|
|
## Simple observers
|
|
Simple observers are declared in the properties object, and always observe a single property.
|
|
Simple observers only fire when the property itself changes. They don't fire on subproperty changes, or array mutation. If you need these changes, use a complex observer with a wildcard path
|
|
|
|
### Observe a property
|
|
Define a simple observer by adding an observer key to the property's declaration, identifying the observer method by name.
|
|
|
|
Example
|
|
|
|
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
|
|
class XCustom extends PolymerElement {
|
|
static get properties() {
|
|
return {
|
|
active: {
|
|
type: Boolean,
|
|
// Observer method identified by name
|
|
observer: '_activeChanged'
|
|
}
|
|
}
|
|
}
|
|
// Observer method defined as a class method
|
|
_activeChanged(newValue, oldValue) {
|
|
this.toggleClass('highlight', newValue);
|
|
}
|
|
}
|
|
customElements.define('x-custom', XCustom);
|
|
|
|
|
|
The observer method is usually defined on the class itself, although an observer method can also be defined by a superclass, subclass, or a class mixin, as long as the named method exists on the element.
|
|
|
|
## Complex observers
|
|
Complex observers are declared in the observers array. Complex observers can monitor one or more paths. These paths are called the observer's dependencies.
|
|
|
|
static get observers() {
|
|
return [
|
|
// Observer method name, followed by a list of dependencies, in parenthesis
|
|
'userListChanged(users.*, filter)'
|
|
]
|
|
}
|
|
|
|
Each dependency represents:
|
|
|
|
- A specific property (for example, firstName).
|
|
|
|
- A specific subproperty (for example, address.street).
|
|
|
|
- Mutations on a specific array (for example, users.splices).
|
|
|
|
- All subproperty changes and array mutations below a given path (for example, users.*).
|
|
|
|
### Observe changes to multiple properties
|
|
|
|
To observe changes to a set of properties, use the observers array.
|
|
|
|
Observers do not receive old values as arguments, only new values. Only single-property observers defined in the properties object receive both old and new values.
|
|
|
|
|
|
Example
|
|
|
|
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
|
|
class XCustom extends PolymerElement {
|
|
|
|
static get properties() {
|
|
return {
|
|
preload: Boolean,
|
|
src: String,
|
|
size: String
|
|
}
|
|
}
|
|
|
|
// Each item of observers array is a method name followed by
|
|
// a comma-separated list of one or more dependencies.
|
|
static get observers() {
|
|
return [
|
|
'updateImage(preload, src, size)'
|
|
]
|
|
}
|
|
|
|
// Each method referenced in observers must be defined in
|
|
// element prototype. The arguments to the method are new value
|
|
// of each dependency, and may be undefined.
|
|
updateImage(preload, src, size) {
|
|
// ... do work using dependent values
|
|
}
|
|
}
|
|
|
|
customElements.define('x-custom', XCustom);
|
|
|
|
|
|
## Observe sub-property changes
|
|
|
|
- Define an observers array.
|
|
- Add an item to the observers array. The item must be a method name followed by a comma-separated list of one or more paths. For example, **onNameChange(dog.name) for one path, or onNameChange(dog.name, cat.name) for multiple paths**. **Each path is a sub-property** that you want to observe.
|
|
- Define the method in your element prototype. When the method is called, the argument to the method is the new value of the sub-property.
|
|
|
|
|
|
Example
|
|
|
|
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
|
|
|
|
class XCustom extends PolymerElement {
|
|
static get template () {
|
|
return html`
|
|
<!-- Sub-property is updated via property binding. -->
|
|
<input value="{{user.name::input}}">
|
|
`;
|
|
}
|
|
static get properties() {
|
|
return {
|
|
user: {
|
|
type: Object,
|
|
value: function() {
|
|
return {};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Observe the name sub-property on the user object
|
|
static get observers() {
|
|
return [
|
|
'userNameChanged(user.name)'
|
|
]
|
|
}
|
|
// For a property or sub-property dependency, the corresponding
|
|
// argument is the new value of the property or sub-property
|
|
userNameChanged: function(name) {
|
|
if (name) {
|
|
console.log('new name: ' + name);
|
|
} else {
|
|
console.log('user name is undefined');
|
|
}
|
|
}
|
|
}
|
|
customElements.define('x-custom', XCustom);
|
|
|
|
## Observe array mutations
|
|
|
|
static get observers() {
|
|
return [
|
|
'usersAddedOrRemoved(users.splices)'
|
|
]
|
|
}
|
|
|
|
- indexSplices. The set of changes that occurred to the array, in terms of array indexes. Each indexSplices record contains the following properties:
|
|
- index. Position where the splice started.
|
|
- removed. Array of removed items.
|
|
- addedCount. Number of new items inserted at index.
|
|
- object: A reference to the array in question.
|
|
- type: The string literal 'splice'.
|
|
|
|
|
|
|
|
Example
|
|
|
|
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
|
|
class XCustom extends PolymerElement {
|
|
static get properties() {
|
|
return {
|
|
users: {
|
|
type: Array,
|
|
value: function() {
|
|
return [];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Observe changes to the users array
|
|
static get observers() {
|
|
return [
|
|
'usersAddedOrRemoved(users.splices)'
|
|
];
|
|
}
|
|
// For an array mutation dependency, the corresponding argument is a change record
|
|
usersAddedOrRemoved(changeRecord) {
|
|
if (changeRecord) {
|
|
changeRecord.indexSplices.forEach(function(s) {
|
|
s.removed.forEach(function(user) {
|
|
console.log(user.name + ' was removed');
|
|
});
|
|
for (var i=0; i<s.addedCount; i++) {
|
|
var index = s.index + i;
|
|
var newUser = s.object[index];
|
|
console.log('User ' + newUser.name + ' added at index ' + index);
|
|
}
|
|
}, this);
|
|
}
|
|
}
|
|
ready() {
|
|
super.ready();
|
|
this.push('users', {name: "Jack Aubrey"});
|
|
}
|
|
}
|
|
customElements.define('x-custom', XCustom);
|
|
|
|
|
|
### Observe all changes related to a path
|
|
|
|
To call an observer when any (deep) sub-property of an object or array changes, specify a path with a wildcard (*).
|
|
|
|
When you specify a path with a wildcard, the argument passed to your observer is a change record object with the following properties:
|
|
|
|
- path. Path to the property that changed. Use this to determine whether a property changed, a sub-property changed, or an array was mutated.
|
|
- value. New value of the path that changed.
|
|
- base. The object matching the non-wildcard portion of the path.
|
|
|
|
Example
|
|
|
|
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
|
|
|
|
class XCustom extends PolymerElement {
|
|
static get template() {
|
|
return html`
|
|
<input value="{{user.name.first::input}}" placeholder="First Name">
|
|
<input value="{{user.name.last::input}}" placeholder="Last Name">
|
|
`;
|
|
}
|
|
static get properties() {
|
|
return {
|
|
user: {
|
|
type: Object,
|
|
value: function() {
|
|
return {'name':{}};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
static get observers() {
|
|
return [
|
|
'userNameChanged(user.name.*)'
|
|
]
|
|
}
|
|
userNameChanged(changeRecord) {
|
|
console.log('path: ' + changeRecord.path);
|
|
console.log('value: ' + changeRecord.value);
|
|
}
|
|
}
|
|
customElements.define('x-custom', XCustom);
|
|
|
|
static get properties() {
|
|
return {
|
|
firstName: {
|
|
type: String
|
|
},
|
|
lastName: {
|
|
type: String
|
|
}
|
|
}
|
|
}
|
|
|
|
static get observers() {
|
|
return [
|
|
'nameChanged(firstName, lastName)'
|
|
]
|
|
}
|
|
|
|
nameChanged: function(firstName, lastName) {
|
|
console.log('new name:', firstName, lastName);
|
|
}
|
|
|
|
## Computed properties
|
|
|
|
Note: The definition of a computing function looks like **the definition of a multi-property observer**, and the two act almost identically. The only difference is that the computed property function returns a value that's exposed as a virtual property.
|
|
|
|
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
|
|
|
|
class XCustom extends PolymerElement {
|
|
static get template() {
|
|
return html`
|
|
<p>My name is <span>{{fullName}}</span></p>
|
|
`;
|
|
}
|
|
static get properties() {
|
|
return {
|
|
first: String,
|
|
last: String,
|
|
fullName: {
|
|
type: String,
|
|
// when `first` or `last` changes `computeFullName` is called once
|
|
// and the value it returns is stored as `fullName`
|
|
computed: 'computeFullName(first, last)'
|
|
}
|
|
}
|
|
}
|
|
computeFullName(first, last) {
|
|
return first + ' ' + last;
|
|
}
|
|
}
|
|
customElements.define('x-custom', XCustom);
|
|
|
|
## Dynamic observer methods
|
|
For example,
|
|
|
|
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
|
|
class NameCard extends PolymerElement {
|
|
static get properties() {
|
|
return {
|
|
// Override default format by assigning a new formatter
|
|
// function
|
|
formatter: {
|
|
type: Function
|
|
},
|
|
formattedName: {
|
|
computed: 'formatter(name.title, name.first, name.last)'
|
|
},
|
|
name: {
|
|
type: Object,
|
|
value() {
|
|
return { title: "", first: "", last: "" };
|
|
}
|
|
}
|
|
}
|
|
}
|
|
constructor() {
|
|
super();
|
|
this.formatter = this.defaultFormatter;
|
|
}
|
|
defaultFormatter(title, first, last) {
|
|
return `${title} ${first} ${last}`
|
|
}
|
|
}
|
|
customElements.define('name-card', NameCard);
|
|
|
|
Setting a new value for formatter causes the formattedName property to update, even if the name property doesn't change:
|
|
|
|
nameCard.name = { title: 'Admiral', first: 'Grace', last: 'Hopper'}
|
|
console.log(nameCard.formattedName); // Admiral Grace Hopper
|
|
nameCard.formatter = function(title, first, last) {
|
|
return `${last}, ${first}`
|
|
}
|
|
console.log(nameCard.formattedName); // Hopper, Grace
|
|
|
|
|
|
### Add a simple observer dynamically
|
|
You can create a simple observer dynamically using the _createPropertyObserver instance method. For example:
|
|
|
|
this._observedPropertyChanged = (newVal) => { console.log('observedProperty changed to ' + newVal); };
|
|
this._createPropertyObserver('observedProperty', '_observedPropertyChanged', true);
|
|
|
|
### Add a complex observer dynamically
|
|
You can create a computed property dynamically using the _createMethodObserver instance method. For example:
|
|
|
|
this._createMethodObserver('_observeSeveralProperties(prop1,prop2,prop3)', true);
|
|
|
|
### Add a computed property dynamically
|
|
You can create a computed property dynamically using the _createComputedProperty instance method. For example:
|
|
|
|
this._createComputedProperty('newProperty', '_computeNewProperty(prop1,prop2)', true);
|
|
|
|
|
|
# Data Bindings
|
|
|
|
A data binding connects data from a custom element (the host element) to a property or attribute of an element in its local DOM (the child or target element).
|
|
|
|
static get template() {
|
|
return html`
|
|
<target-element target-property="{{hostProperty}}"></target-element>
|
|
`;
|
|
}
|
|
|
|
### Bind to a target property
|
|
|
|
<target-element name="{{myName}}"></target-element>
|
|
|
|
This example binds the target element's name property to the host element's myName property.
|
|
|
|
|
|
### Bind to text content
|
|
To bind to a target element's textContent, you can simply include the annotation or compound binding inside the target element.
|
|
|
|
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
|
|
|
|
class UserView extends PolymerElement {
|
|
static get properties() {
|
|
return {
|
|
name: String
|
|
};
|
|
}
|
|
static get template() {
|
|
return html`
|
|
<div>[[name]]</div>
|
|
`;
|
|
}
|
|
}
|
|
customElements.define('user-view', UserView);
|
|
<!-- usage -->
|
|
<user-view name="Samuel"></user-view>
|
|
|
|
### Bind to a target attribute
|
|
|
|
To bind to an attribute, add a dollar sign ($) after the attribute name:
|
|
|
|
<div style$="color: {{myColor}};">
|
|
|
|
Attribute binding results in a call to:
|
|
|
|
element.setAttribute(attr,value);
|
|
|
|
As opposed to:
|
|
|
|
element.property = value;
|
|
|
|
For example:
|
|
|
|
static get template() {
|
|
return html`
|
|
<!-- Attribute binding -->
|
|
<my-element selected$="[[value]]"></my-element>
|
|
<!-- results in <my-element>.setAttribute('selected', this.value); -->
|
|
|
|
<!-- Property binding -->
|
|
<my-element selected="{{value}}"></my-element>
|
|
<!-- results in <my-element>.selected = this.value; -->
|
|
`;
|
|
}
|
|
|
|
### Bind to a host sub-property
|
|
|
|
main-view.js
|
|
|
|
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
|
|
import './user-view.js';
|
|
|
|
class MainView extends PolymerElement {
|
|
static get properties() {
|
|
return {
|
|
user: {
|
|
type: Object,
|
|
value: function () { return { given: "San", family: "Zhang" }; }
|
|
}
|
|
};
|
|
}
|
|
static get template() {
|
|
return html`
|
|
<user-view given="{{user.given}}" family="{{user.family}}"></user-view>
|
|
`;
|
|
}
|
|
}
|
|
customElements.define('main-view', MainView);
|
|
|
|
user-view.js
|
|
|
|
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
|
|
|
|
class UserView extends PolymerElement {
|
|
static get properties() {
|
|
return {
|
|
given: String,
|
|
family: String
|
|
};
|
|
}
|
|
static get template() {
|
|
return html`
|
|
<div>[[given]] [[family]]</div>
|
|
`;
|
|
}
|
|
}
|
|
customElements.define('user-view', UserView);
|
|
|
|
index.html
|
|
|
|
<main-view></main-view>
|
|
|
|
## Logical not operator
|
|
|
|
static get template() {
|
|
return html`
|
|
<my-page show-login="[[!isLoggedIn]]"></my-page>
|
|
`;
|
|
}
|
|
|
|
### Computed bindings
|
|
|
|
<div>[[_formatName(first, last, title)]]</div>
|
|
|
|
A computed binding is useful if you don't need to expose a computed property as part of the element's API, or use it elsewhere in the element. Computed bindings are also useful for filtering or transforming values for display.
|
|
|
|
Example
|
|
|
|
class XCustom extends PolymerElement {
|
|
static get properties() {
|
|
return {
|
|
given: String,
|
|
family: String
|
|
};
|
|
}
|
|
|
|
_formatName(given, family) {
|
|
return `${family}, ${given}`;
|
|
}
|
|
|
|
static get template() {
|
|
return html`
|
|
My name is <span>[[_formatName(given, family)]]</span>
|
|
`;
|
|
}
|
|
}
|
|
customElements.define('x-custom', XCustom);
|
|
|
|
### Literal arguments to computed bindings
|
|
|
|
|
|
Example:
|
|
|
|
static get template() {
|
|
return html`
|
|
<span>{{translate('Hello\!\, nice to meet you', given, family)}}</span>
|
|
`;
|
|
}
|
|
|
|
### Compound bindings
|
|
You can combine string literals and bindings in a single property binding or text content binding. For example:
|
|
|
|
<img src$="https://www.example.com/profiles/[[userId]].jpg">
|
|
|
|
<span>Name: [[lastname]], [[firstname]]</span>
|
|
|
|
|
|
## Binding to arrays and array items
|
|
|
|
<!-- Don't do this! This format doesn't work -->
|
|
<span>{{array[0]}}</span>
|
|
<!-- Don't do this! Data may display, but won't be updated correctly -->
|
|
<span>{{array.0}}</span>
|
|
|
|
### Bind to an array item
|
|
You can use a computed binding to bind to a specific array item, or to a subproperty of an array item, like array[index].name.
|
|
|
|
|
|
class XCustom extends PolymerElement {
|
|
static get properties() {
|
|
return {
|
|
myArray: {
|
|
type: Array,
|
|
value: [{ name: 'Bob' }, { name: 'Wing Sang' }]
|
|
}
|
|
};
|
|
}
|
|
// first argument is the change record for the array change,
|
|
// change.base is the array specified in the binding
|
|
arrayItem(change, index, path) {
|
|
// this.get(path, root) returns a value for a path
|
|
// relative to a root object.
|
|
return this.get(path, change.base[index]);
|
|
}
|
|
ready() {
|
|
super.ready();
|
|
// mutate the array
|
|
this.unshift('myArray', { name: 'Fatma' });
|
|
// change a subproperty
|
|
this.set('myArray.1.name', 'Rupert');
|
|
}
|
|
static get template() {
|
|
return html`
|
|
<div>[[arrayItem(myArray.*, 0, 'name')]]</div>
|
|
<div>[[arrayItem(myArray.*, 1, 'name')]]</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
|
|
## Two-way binding to a non-Polymer element
|
|
|
|
Example
|
|
|
|
<!-- Listens for `input` event and sets hostValue to <input>.value -->
|
|
<input value="{{hostValue::input}}">
|
|
|
|
<!-- Listens for `change` event and sets hostChecked to <input>.checked -->
|
|
<input type="checkbox" checked="{{hostChecked::change}}">
|
|
|
|
<!-- Listens for `timeupdate ` event and sets hostTime to <video>.currentTime -->
|
|
<video url="..." current-time="{{hostTime::timeupdate}}">
|