Polymer
Elements can serve as a generic package for reusable functionality. Custom Elements pave a path to Polymer’s “Everything is an element” philosophy. Elements can be classified into UI Element and Non-UI Element.
What is Polymer
The future to create Web Components. Polymer 利用 HTML 为基础,以组件的形式集成针对该组件的:Model, View, Controller。
它的设计思想类似于:聚合物,从最小的组件聚合到最大的组件从而构成一个 App. Polymer 在层次体系中实现层次通信,一般是隔层的通信,因而感觉写法和结构是比较固定的。
- Web Components Standard
- Polymer Library to define custom elements
- Elements can be a comprehensive set of UI and non-UI elements
Shadow DOM
Shadow DOM separates content from presentation thereby eliminating naming conflicts and improving code expression.
Light DOM and Shadow DOM
This is a light DOM
<my-custom-element>
<q>Hello World</q> <!-- part of my-custom-element's light DOM -->
</my-custom-element>
This is a shadow DOM
<polymer-element name="my-custom-element" noscript>
<template>
<span>People say: </span>
<content select="q"></content>
<footer>sometimes</footer>
</template>
</polymer-element>
The span, content, and footer
elements are encapsulated within the custom element and hidden from the rest of the page.
The composed DOM is what the browser actually renders. For rendering, the light DOM is distributed into the shadow DOM to produce the composed DOM.
This is a Composed DOM
<my-custom-element>
<span>People say: <q>Hello World</q></span>
<footer>sometimes</footer>
</my-custom-element>
Web components polyfills
Web Components:
- Shadow DOM. Encapsulate DOM subtrees and the associated CSS.
- HTML Imports. Load element definitions and other resources declaratively.
- Custom Elements. Define new elements in HTML.
Dependencies required by the Web Components polyfills:
- WeakMap. Shim for ES6 WeakMap type.
- Mutation Observers. Efficiently watch for changes in the DOM.
Why use Polymer
个人觉得 Polymer 比较适合内容发布式的 Web 应用,适合重复组件比较多的,或者结构清晰,顺序分明的 App 结构。同时也适合多终端结构的 Web App 开发。
Brief API
Reference of Polymer
Element
<link rel="import"
href="../bower_components/polymer/polymer.html">
<polymer-element
name="tag-name"
constructor="TagName"
noscript
extends="TagName"
attributes="foo bar baz"
>
<template>
<!-- A shadow element renders the extended
element's shadow DOM here. -->
<shadow></shadow> <!-- "You are cool Matt" -->
<!-- shadow DOM here -->
<style>input {box-sizing:border-box;width:100%;}</style>
<input type="number" value="{{num}}"><br>
<em>{{num}}^2 = {{square}}</em>
<input id="nameInput" placeholder="Name">
<button on-click="">Say hello</button>
</template>
<script>
(function() {
// Run once. Private and static to the element.
var foo_ = new Foo();
// these variables are shared by all instances of this element
var values = {
firstName: 'John',
lastName: 'Smith'
}
// created() is run for every instance of the element that is created.
Polymer({
get foo() { return foo_; }
created: function() {
// Overwrite the supper method
this.super([arg1, arg2]);
// Observing changes to light DOM children
this.onMutation(domElement, someCallback);
// async(inMethod, inArgs, inTimeout)
this.async(function() {
this.foo = 3;
// Dynamic import
Polymer.import(['dynamic-element.html'], function() {
document.querySelector('dynamic-element')
.description = 'a dynamic import';
});
// Mixin methods
Polymer(Polymer.mixin({
// my-element prototype
}, myMixin));
}
publish: { // publish attributes
foo: 'bar'
}
// Set a Reflect Property
// If the property value is an object, array, or function,
// the value is never reflected, whether or not reflect is true.
propertyName: {
value: defaultValue,
reflect: true
},
// In the case of property changes that result
// in DOM modifications
propChanged: function() {
// If "prop" changing results in our DOM changing,
// schedule an update after the new micro task.
this.async(this.updateValues);
},
responseChanged: function() { ... },
// UI Events Binding
keypressHandler: function(event, detail, sender) { ...},
buttonClick: function(event, detail, sender) {
// fire(type, detail, targetNode, bubbles?, cancelable?)
this.fire('ouch', {msg: 'That hurt!'});
},
// Automatic node finding
sayHello: function() {
alert("Hello " + this.$.nameInput.value);
this.$.container.querySelector('#inner');
},
// Watcher
bar: 'bar',
observe: {
bar: 'validate'
},
barChanged: function(oldValue, newValue) {
console.log("I'm not called");
},
validate: function(oldValue, newValue) {
console.log("I'm called instead");
}
});
})();
// Element life-cycle methods
Polymer('tag-name', {
created: function() { ... },
ready: function() { ... },
attached: function () { ... },
domReady: function() { ... },
detached: function() {
// Keep bindings active when this element is removed
this.cancelUnbindAll();
},
attributeChanged: function(attrName, oldVal, newVal) {
//var newVal = this.getAttribute(attrName);
console.log(attrName, 'old: ' + oldVal, 'new:', newVal);
},
});
</script>
<script src="path/to/tagname.js"></script>
</polymer-element>
Polymer elements prepare themselves automatically in the following cases:
- when they’re created in a document that has a defaultView (the main document)
- when they receive the attached callback
- when they’re created in the shadowRoot of another element that is preparing itself
- In addition, if the
.alwaysPrepare
property is set to true, Polymer elements prepare themselves even when they do not satisfy the above rules. - Registration an element:
Polymer([ tag-name, ] [prototype]);
Data Binding
Polymer adds declarative, two-way data binding to templates. Data binding lets you assign, or bind, a JavaScript object as the template’s data model.
Polymer data binding makes the smallest changes to the DOM necessary.
// if
<template>
<template bind="{{myList as list}}">
<template repeat="{{item in list.items}}" if="{{list.showItems}}">
<li>{{item.name}}</li>
</template>
</template>
</template>
// Expression scope
<template>
<!-- outermost template -- element's properties available -->
<template bind="{{organization as organization}}">
<!-- organization.* available -->
<template bind="{{organization.contact as contact}}">
<!-- organization.* & contact.* available -->
<template bind="{{contact.address}}">
<!-- only properties of address are available -->
<template bind="{{streetAddress as streetAddress}}">
<!-- streetAddress.* and properties of address are available.
NOT organization.* or contact.* -->
</template>
</template>
</template>
</template>
</template>
// Filter
{{myNumber | toFixed(2)}}
{{myNumber | toHex | toUpperCase}}
PolymerExpressions.prototype.uppercase = function(input) {
if (input) {
return input.toUpperCase();
}
};
Events
Polymer Ready Events
window.addEventListener('polymer-ready', function(e) {
var xFoo = document.querySelector('x-foo');
xFoo.barProperty = 'baz';
});
Layout Attributes
version 1.0 via YQN 2015-03-20