GraphQL Learn (1) - Queries and Mutations
在此页面上,你将详细了解如何查询GrahQL服务器。
字段(Fields)
最简单的,GraphQL是关于要求对象上的特定字段。我们先来看一个非常简单的查询,当我们运行它时得到结果:
{
hero {
name
}
}
{
"data": {
"hero": {
"name": "R2-D2"
}
}
}
在此页面上,你将详细了解如何查询GrahQL服务器。
最简单的,GraphQL是关于要求对象上的特定字段。我们先来看一个非常简单的查询,当我们运行它时得到结果:
{
hero {
name
}
}
{
"data": {
"hero": {
"name": "R2-D2"
}
}
}
在此页面上,您将了解有关GraphQL类型系统的所有知识,以及如何描述可查询哪些数据。 由于GraphQL可以与任何后端框架或编程语言一起使用,因此我们将远离实现特定的详细信息,仅讨论概念。
如果您以前看过GraphQL查询,那么你应该知道GraphQL查询语言基本上是在对象上选择字段。 所以在以下查询中:
{
hero {
name
appearsIn
}
}
通过使用类型系统,可以预先确定GraphQL查询是否有效。 这样可以让服务器和客户端有效地通知开发人员在创建无效查询时,无需在运行时检查。
对于我们的星球大战示例,文件starWarsValidation-test.js包含许多无效的查询,可以用来测试当前实现的验证器。
首先,我们来看一个复杂的有效查询。 这是一个嵌套查询,类似于上一节的一个示例,但将重复的字段分解成一个片段:
经过验证,GraphQL查询由GraphQL服务器执行,然后返回一个与查询形状相同的结果,通常为JSON。
GraphQL无法执行没有类型系统的查询,让我们使用类型系统例子来说明执行查询,这个例子是我们教程中使用的类型系统中的一部分:
type Query {
human(id: ID!): Human
}
type Human {
name: String
appearsIn: [Episode]
starships: [Starship]
}
enum Episode {
NEWHOPE
EMPIRE
JEDI
}
type Starship {
name: String
}
查询GraphQL架构有关它支持的查询的信息通常很有用。 GraphQL允许我们使用内省系统来做到这一点!对于我们的星球大战例子来说,文件starWarsIntrospection-test.js包含许多查询内省系统的查询,并且是一个完整的按照规范实现的测试文件。
我们设计了类型系统,所以我们应该知道系统中可用的类型。但是如果不知道,我们还可以通过查询__schema字段来询问GraphQL。这个字段始终存在于根类型中。
Polymer提供一系列的自定义元素来简化一些共有的数据绑定逻辑:
dom-repeat 遍历显示数组array-selector 数组选择器dom-if 条件显示dom-bind 自动绑定2.0 tip. The data binding helper elements are bundled in to the backward-compatible,
polymer.htmlimport. If you aren't using the legacy import, you'll need to import the helper elements you're using.
为了向前兼容,polymer.html引入了所有的helper元素,而2.0的polymer.Element则要按照需要一个个手动引入。
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). The host element data can be a property or sub-property represented by a data path, or data generated based on one or more paths.
数据绑定能够将host元素和target元素的property或者attribute相互链接床单。这里数据指的是路径(Paths)
数据绑定有两种绑定方式:
<custom-element property-name=annotation-or-compound-binding ></custom-element>
<custom-element attribute-name$=annotation-or-compound-binding></custom-element>
=绑定property,$=绑定attribute(href,style,...)[](<#>)或者\{\{ \}\}"my name is {{ name }}"将target元素的name属性绑定到当前元素的my-Name属性。 注意驼峰式和dash式命名的转换规则(property name to attribute name mapping)
<target-element name="{{myName}}"></target-element>
相当于绑定到target元素的textContent属性上
<dom-module id="user-view">
<template>
<div>[[name]]</div>
</template>
<script>
class UserView extends Polymer.Element {
static get is() {return 'user-view'}
static get properties() {
return {
name: String
}
}
}
customElements.define(UserView.is, UserView);
</script>
</dom-module>
<!-- usage -->
<user-view name="Samuel"></user-view>
Binding to text content is always one-way, host-to-target.
注意,文字节点的绑定永远都是单向的(host to target)
attribute绑定相当于
element.setAttribute(attr,value)
element.property = 'value'
因此,一些attribute同样可以使用property形式绑定:
<template>
<!-- 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; -->
</template>
[](<#>))
There are a handful of common native element properties that Polymer can't data-bind to directly, because the binding causes issues on one or more browsers.
一些原生的property无法使用=绑定数据,需要使用attribute形式的$=才能成功绑定。
| Attribute | Property | Notes |
|---|---|---|
class |
classList, className |
Maps to two properties with different formats. |
style |
style |
By specification, style is considered a read-only reference to a CSSStyleDeclaration object. |
href |
href |
|
for |
htmlFor |
|
data-* |
dataset |
Custom data attributes (attribute names starting with data-) are stored on the dataset property. |
value |
value |
Only for <input type="number">. |
data binding to the
valueproperty doesn't work on IE for numeric input types. For this specific case, you can use one-way attribute binding to set thevalueof a numeric input. Or use another element such asiron-inputorpaper-inputthat handles two-way binding correctly.
<!-- class -->
<div class$="[[foo]]"></div>
<!-- style -->
<div style$="[[background]]"></div>
<!-- href -->
<a href$="[[url]]">
<!-- label for -->
<label for$="[[bar]]"></label>
<!-- dataset -->
<div data-bar$="[[baz]]"></div>
<!-- ARIA -->
<button aria-label$="[[buttonLabel]]"></button>
可以在data-binding表达式前面添加!号取反
<template>
<my-page show-login="[[!isLoggedIn]]"></my-page>
</template>
!不能!!computed binding类似于computed property。
<div>[[_formatName(first, last, title)]]</div>
An element can have multiple computed bindings in its template that refer to the same computing function. 一个元素里面可以有多个使用同样的computing function的computed binding
computed binding并不完全等同于computed property,差异有下面几点:
<dom-module id="x-custom">
<template>
My name is <span>[[_formatName(first, last)]]</span>
</template>
<script>
class XCustom extends Polymer.Element {
static get is() {return 'x-custom'}
static get properties() {
return {
first: String,
last: String
}
}
_formatName(first, last) {
return `${last}, ${first}`
}
}
customElements.define(XCustom.is, XCustom);
</script>
</dom-module>
Commas in literal strings: Any comma occurring in a string literal must be escaped using a backslash (
\).
如果参数是字符串,那么字符串里面所有的逗号都要被转义
<dom-module id="x-custom">
<template>
<span>{{translate('Hello\, nice to meet you', first, last)}}</span>
</template>
</dom-module>
Computed bindings are one-way. A computed binding is always one-way, host-to-target.
computed binding只能在单向绑定中使用
可以在字符串里面或者textContent里面使用绑定标记
<img src$="https://www.example.com/profiles/[[userId]].jpg">
<span>Name: [[lastname]], [[firstname]]</span>
\{\{ \}\}记号。To keep annotation parsing simple, Polymer doesn't provide a way to bind directly to an array item.
为了解析简单,Polymer无法直接绑定一个数组里面的元素
<!-- Don't do this! -->
<span>{{array[0]}}</span>
<!-- Or this! -->
<span>{{array.0}}</span>
有下面几种方法可以解决:
- dom-repeat里面已经为每个数组里面的元素创建了一个子scope,因此可以直接binding
- array-selector 同上,可以直接绑定一个元素或者被选择的元素集合
- 使用computed binding来间接绑定,见下面例子
<dom-module id="x-custom">
<template>
<div>[[arrayItem(myArray.*, 0, 'name')]]</div>
<div>[[arrayItem(myArray.*, 1, 'name')]]</div>
</template>
<script>
class XCustom extends Polymer.Element {
static get is() {return 'x-custom'}
static get properties() {
return {
myArray: {
type: Array,
value: [{ name: 'Bob' }, { name: 'Doug' }]
}
}
}
// 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: 'Susan' });
// change a subproperty
this.set('myArray.1.name', 'Rupert');
}
}
customElements.define(XCustom.is, XCustom);
</script>
</dom-module>
为了达到非Polymer元素上面的双向绑定,可以使用下面的标记:
target-prop="{{hostProp::target-change-event}}"
<!-- 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}}">
target-prop的变化通知函数是target-prop-changed则该定义可以省略。
<!-- Listens for `value-changed` event -->
<my-element value="{{hostValue::value-changed}}">
<!-- Listens for `value-changed` event using Polymer convention by default -->
<my-element value="{{hostValue}}">
有两种监听器:
每个监听器都有一个或多个 依赖 ,当依赖发生可监听的变化是,监听方法就会被调用。
Computed properties are virtual properties based on one or more pieces of the element's data. A computed property is generated by a computing function—essentially, a complex observer that returns a value.
计算属性顾名思义,是由一个返回某个值的计算函数算出来的属性。
只有当元素初始值加载完毕并且至少有一个依赖的属性被定义(undefined => somevalue),监听器才会被调用。
Polymer.Async库来将它放到异步任务队列里面去。简单监听器需要在properties中需要监听的property里面注册。监听函数里面不能依赖其他任何property
Simple observers are fired the first time the property becomes defined (!=
undefined), and on every change thereafter, even if the property becomes undefined.
简单监听器第一次触发在property被定义后(undefined => somevalue),在此之后,任何property变化(包括重新变回undefined)都会被调用。
Simple observers only fire when the property itself changes. They don't fire on subproperty changes, or array mutation.
简单监听器只会响应当前property的可观察变化(observable changes)
You specify an observer method by name. The host element must have a method with that name.
简单监听器的定义和实现(函数本身)必须一一对应,简单监听器的实现可以在当前class里面也可以继承自superclass也可以添加自mixin。
The observer method receives the new and old values of the property as arguments.
简单监听器接受两个参数: oldValue,newValue
class XCustom extends Polymer.Element {
static get is() {return 'x-custom'; }
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);
}
}
复杂监听器需要在this.observers数组里面注册,复杂监听器可以监听多条路径(依赖)
static get observers() {
return [
// Observer method name, followed by a list of dependencies, in parenthesis
'userListChanged(users.*, filter)'
]
}
一个简单的属性路径 (firstName).
一个简单的子属性路径 (address.street).
一个数组的变化结果路径 (users.splices).
一个包含通配符的路径 (users.*).
函数被调用时所得到的参数依据监听的路径种类不同而不同:
Note that any of the arguments can be
undefinedwhen the observer is called.
监听函数中每次调用,任何一个参数都有可能是undefined
复杂监听器的参数只有新值没有旧值
class XCustom extends Polymer.Element {
static get is() {return 'x-custom'; }
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
}
}
数组变化路径的参数change record是一个对象,包含一个indexSplices数组,数组中的每一项表示一处变更记录,包含下面信息:
index. 变更其实的地方removed. 被删除的数据addedCount. 插入的数据长度object: 一个指向新数据的数组(不是拷贝) //A reference to the array in question. 这句话不懂什么意思type: 一个值为'splice'的字符串注意: change record也许会为undefined
class XCustom extends Polymer.Element {
static get is() {return 'x-custom'; }
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(XCustom.is, XCustom);
通配符路径的参数是一个change record对象,包含:
path 监听器监听的路径value 新值base 通配符之前的路径所代表的对象如果通配符路径监听到的是数组的变化,那么
path则是该数组的路径(不包括.splices)value里面则会包含一个indexSplices项<dom-module id="x-custom">
<template>
<input value="{{user.name.first::input}}"
placeholder="First Name">
<input value="{{user.name.last::input}}"
placeholder="Last Name">
</template>
<script>
class XCustom extends Polymer.Element {
static get is() { return 'x-custom'; }
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(XCustom.is, XCustom);
</script>
</dom-module>
Observers shouldn't rely on any properties, sub-properties, or paths other than those listed as dependencies of the observer. This creates "hidden" dependencies
监听器不能依赖任何其他未被注册过的路径,否则: 1. 不能保证该路径是否已经初始化完成 2. 当该路径的属性发生变化时,无法触发当前监听器
For example:
static get properties() {
return {
firstName: {
type: String,
observer: 'nameChanged'
},
lastName: {
type: String
}
}
}
// WARNING: ANTI-PATTERN! DO NOT USE
nameChanged(newFirstName, oldFirstName) {
// Not invoked when this.lastName changes
var fullName = newFirstName + ' ' + this.lastName;
// ...
}
Note that Polymer doesn't guarantee that properties are initialized in any particular order.
Polymer不能保证属性之间的初始化顺序。
Computed properties are virtual properties computed on the basis of one or more paths. The computing function for a computed property follows the same rules as a complex observer, except that it returns a value, which is used as the value of the computed property.
计算属性定义跟复杂监听器类似,但是计算属性的计算函数需要返回一个值。
计算属性需要在properties里面的property配置对象中使用computed键注册,注册语法跟复杂监听器一致。
<dom-module id="x-custom">
<template>
My name is <span>{{fullName}}</span>
</template>
<script>
class XCustom extends Polymer.Element {
static get is() { return 'x-custom'; }
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(XCustom.is, XCustom);
</script>
</dom-module>
另外见 : Computed Bindings
If the observer method is declared in the
propertiesobject, the method is considered dynamic: the method itself may change during runtime. A dynamic method is considered an extra dependency of the observer, so the observer re-runs if the method itself changes. For example:
如果一个监听器或者计算属性的方法被定义在了properties里面,那么我们可以动态的对这个方法进行覆盖、重写。
当方法发生变化的时候,新的简单监听器或计算属性会被立即触发或者被更新。
class NameCard extends Polymer.Element {
static get is() {
return 'name-card'
}
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);
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
formattedName的方法formatter发生变化的时候,尽管依赖name没有变化,但是该属性还是触发更新了。
因为动态监听器方法出于properties里面,因此会被看作一个依赖,一旦这个方法被定义,监听器就在初始化的时候触发,尽管其他依赖都没有被定义。
In some cases, you may want to add an observer or computed property dynamically. A set of instance methods allow you to add a simple observer, complex observer, or computed property to the current element instance.
使用js API来动态添加计算属性或监听器
- _createPropertyObserver
- _createMethodObserver
- _createComputedProperty
this._observedPropertyChanged = (newVal) => { console.log('observedProperty changed to ' + newVal); };
this._createPropertyObserver('observedProperty', '_observedPropertyChanged', true);
_observedPropertyChanged)是否应该被看作一个依赖
this._createMethodObserver('_observeSeveralProperties(prop1,prop2,prop3)', true);
第三个参数代表这个方法(_observeSeveralProperties)是否应该被看作一个依赖
this._createComputedProperty('newProperty', '_computeNewProperty(prop1,prop2)', true);
_computeNewProperty)是否应该被看作一个依赖
Polymer提供观察函数、计算属性、数据绑定三大模型功能:
- Observers Callbacks invoked when data changes.
- Computed properties Virtual properties computed based on other properties, and recomputed when the input data changes.
- Data bindings Annotations that update the properties, attributes, or text content of a DOM node when data changes.
```html
```
The data system is based on paths, not objects, where a path represents a property or subproperty relative to the host element.
Polymer的数据系统是基于数据的 路径 之上的。并不是实际的对象。
可观察到的变化指的是Polymer里面存在一个相关的路径指向这个数据的变化。因此,直接改变一个object、array等引用对象不会被观察到。
this.address.street = 'Elm Street'
address.street并不能被address上的观察器捕捉到变化
使用Polymer提供的方法:
// 改变object
this.set('address.street','Half Moon Street')
// 改变array
this.set('users.3',{name:'Hawking'})
this.push('users',{name: 'Maturin'})
this.pop('users')
this.unshift('users',{name: 'Martin'})
this.shift('users')
this.splice('users',3,1,{name:'Hawking'})
// 批量更新
this.setProperties({item:'Orange', count:12},true) //setReadOnly:true 代表需要设置ready-only的属性
//延迟统一更新
this.notifyPath('address.street')
this.notifySplices('users') //only array
// 获得路径代表的属性值
var value = this.get('address.street')
//获得users[2]的值
var item = this.get(['users',2])
Polymer performs dirty checking for objects and arrays using object equality. It doesn't produce any property effects if the value at the specified path hasn't changed.
notifyPath方式需要提供具体属性的路径,而不应该是Object或Array,因为Polymer直接使用数据引用地址进行比较。如果一定需要观察它们,Polymer提供了三种解决方案:
Immutable之类的库,或者每次改变Object和Array里面的值的时候都先克隆出一个副本,在克隆的副本上修改然后把路径指向克隆副本。Polymer.MutableData可以禁止Polymer的对object和array的脏检查(dirty check)Polymer.OptionalMutableData可以在标签上添加一个bool属性mutable,代表是否对object或array开启脏检测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.
数据路径可以是一个路径字符串,也可以是一个包含路径字符串的数组,比如下面三行全部代表同一个路径:
"one.two.three"
["one", "two", "three"]
["one.two", "three"]

Polymer doesn't automatically know that these properties refer to the same object.
<address-card>makes a change to the object, no property effects are invoked on<user-profile>For data changes to flow from one element to another, the elements must be connected with a data binding.
Polymer是以路径来监听数据变化的。所以,就算两个路径实际上都指向同一个对象,他俩也不会联动。需要对这两个路径进行链接操作。
data binding is the only way to link paths on different elements
<dom-module id="user-profile">
<template>
…
<address-card
address="{{primaryAddress}}"></address-card>
</template>
…
</dom-module>
The
<user-profile>element has a propertyprimaryAddressthat refers to a JavaScript object. The<address-card>element has a propertyaddressthat refers to the same object. The data binding connects the path "primaryAddress" on<user-profile>to the path "address" on<address-card>
Paths are relative to the current data binding scope. The topmost scope for any element is the element's properties. Certain data binding helper elements (like template repeaters) introduce new, nested scopes. For observers and computed properties, the scope is always the element's properties.
template repeaters)可以创建新的子域。observers)和计算属性computed properties的域永远都是当前元素本身(this)*来表示当前路径下的所有子路径的任何变化。比如users指向一个数组,users.*代表该数组的所有变化splices- 可以用在数组路径后面,代表数组任何添加、删除的变化users.12注意:
splices路径时,事件参数中只提供当前数组发生变化的元素组成的子数组,所以在一般情况下通配符路径比splices路径要实用如果两条路径都指向同一个对象,如下图,需要使用linkPaths方法将它们关联起来。
注意:
unlinkPaths, 该函数只接受linkPaths调用时的第一条路径Polymer implements the mediator pattern, where a host element manages data flow between itself and its local DOM nodes. When two elements are connected with a data binding, data changes can flow downward, from host to target, upward, from target to host, or both ways. When two elements in the local DOM are bound to the same property data appears to flow from one element to the other, but this flow is mediated by the host. A change made by one element propagates up to the host, then the host propagates the change down to the second element.
Polymer的数据流是一个中间人模型。任何存在数据绑定的两个元素直接的数据流都不是表面上的直接传递的,而是先向上传递(upward)到host元素再向下传递(downward)到目标元素。
Data flow is synchronous. When your code makes an observable change, all of the data flow and property effects from that change occur before the next line of your JavaScript is executed, unless an element explicitly defers action (for example, by calling an asynchronous method).
Polymer的数据传递是同步的,除非调用一个外部异步函数
数据流的方向主要由两个地方控制:数据绑定方式和属性配置项。
数据绑定主要有两种方式:
- Automatic :双向绑定,包括向上(upward,target to host)和向下(downward.host to target),使用 \{\{ \}\}
<my-input value="{{name}}"></my-input>
[](<#>)
<name-tag name="[[name]]"></name-tag>
notify 允许数据向上(upward,target to host)传递,默认为falsereadOnly 禁止数据向下(downward.host to target)传递到当前组件,默认false属性配置实例:
properties: {
// default prop, read/write, non-notifying.
basicProp: {
},
// read/write, notifying
notifyingProp: {
notify: true
},
// read-only, notifying
fancyProp: {
readOnly: true,
notify: true
}
}
notify配置项无效。
2. 当单向绑定且readOnly:true时,将没有任何数据流
Property configuration only affects the property itself, not subproperties. In particular, binding a property that's an object or array creates shared data between the host and target element. There's no way to prevent either element from mutating a shared object or array.
属性配置不能继承
Since the host element manages data flow, it can directly interact with the target element. The host propagates data downward by setting the target element’s properties or invoking its methods.
host元素可以直接通过设置target元素属性或调用回调函数等方法将数据向下传递给target元素

Polymer elements use events to propagate data upward. The target element fires a non-bubbling event when an observable change occurs.
当数据是双向绑定的时候,target元素通过触发一个non-bubbling的change事件来将数据传递给监听这个事件的host元素,host元素监听到事件后对数据变化做出响应改变数据绑定模型(可能影响相邻元素)并触发另外一个change事件向上传播。
当数据是单向绑定的时候,host元素不会监听target元素的change事件,因此数据无法向上传递。

For object and array properties, data flow is a little more complicated. An object or array can be referenced by multiple elements, and there's no way to prevent one element from mutating a shared array or changing a subproperty of an object. As a result, Polymer treats the contents of arrays and objects as always being available for two- way binding. That is:
- Data updates always flow downwards, even if the target property is marked read-only.
- Change events for upward data flow are always fired, even if the target property is not marked as notifying.
Since one-way binding annotations don't create an event listener, they prevent these change notifications from being propagated to the host element.
对于objects和arrays绑定的数据流非常复杂,不得已Polymer将忽略关于它们所有的readyOnly和notify配置项,并将它们hardcode为true,单项绑定和双向绑定不受影响。
当一个元素某路径比如property发生变化,则会响应的触发一个property-changed的通知事件。
事件内容根据路径类型相关:
detail.value中detail.path中,新值将会储存在detail.value中。detail.path中(比如: myArray.splices),新值将会被储存在detail.value中。Don't stop propagation on change notification events. To avoid creating and discarding event objects, Polymer uses cached event objects for change notifications. Calling
stopPropagationon a change notification event prevents all future events for that property. Change notification events don't bubble, so there should be no reason to stop propagation.
注意: 不要在通知事件里面使用stopPropagation
一些Native元素比如<input>并不存在变化通知事件,因此也不能将数据向上传递。可以通过下面的方式手动自定义一个变化通知事件:
<input value="{{firstName::change}}">
In this example, the
firstNameproperty is bound to the input'svalueproperty. Whenever the input element fires itschangeevent, Polymer updates thefirstNameproperty to match the input value, and invokes any associated property effects. The contents of the event aren't important.This technique is especially useful for native input elements, but can be used to provide two-way binding for any non-Polymer component that exposes a property and fires an event when the property changes.
当属性变化的时候,下面的任务会依次执行:
Polymer elements can use the standard DOM APIs for creating, dispatching, and listening for events. Polymer also provides annotated event listeners, which allow you to specify event listeners declaratively as part of the element's DOM template.
这个其实就是在标签上使用on-event属性。
<dom-module id="x-custom">
<template>
<button on-click="handleClick">Kick Me</button>
</template>
<script>
class XCustom extends Polymer.Element {
static get is() {return 'x-custom'}
handleClick() {
console.log('Ow!');
}
}
customElements.define(XCustom.is, XCustom);
</script>
</dom-module>
on-tap事件代替on-click事件,提供更好的移动浏览器支持。 直接使用原生的addEventListener和removeEventListener
直接使用原生的CustomEvent和dispatchEvent
event.composedPath() : 包含事件传递过程中所经过的nodes路径列表。 自定义的事件默认无法穿透ShadowDOM边界。可以如下设置:
var event = new CustomEvent('my-event', {bubbles: true, composed: true});
手势事件的支持是基于Legacy Element的,如果使用Polymer2的class风格定义需要使用mixin:Polymer.GestureEventListeners
<link rel="import" href="polymer/lib/mixins/gesture-event-listeners.html">
<script>
class TestEvent extends Polymer.GestureEventListeners(Polymer.Element) {
...
</script>
Polymer.Gestures.addListener(this, 'tap', e => this.tapHandler(e));
包含以下几种手势事件,包含在e.detail中。
x—clientX coordinate for event y—clientY coordinate for event sourceEvent—the original DOM event that caused the down action x—clientX coordinate for event y—clientY coordinate for event sourceEvent—the original DOM event that caused the up action x—clientX coordinate for event y—clientY coordinate for event sourceEvent—the original DOM event that caused the tap action state—a string indicating the tracking state: start—fired when tracking is first detected (finger/button down and moved past a pre-set distance threshold) track—fired while tracking end—fired when tracking ends x—clientX coordinate for event y—clientY coordinate for event dx—change in pixels horizontally since the first track event dy—change in pixels vertically since the first track event ddx—change in pixels horizontally since last track event ddy—change in pixels vertically since last track event hover()-a function that may be called to determine the element currently being hovered