-
Notifications
You must be signed in to change notification settings - Fork 28
Web Component Developing
When we talk about "web component", basically we are talking about a group of newest web api specs:
- custom elements
- html imports
- templates
- shadow dom
Nowadays it is not all the browsers that support the above specs, the best choise of web component development should be chrome which has been supporting all the above specs. Mozilla firefox has been supporting some of them in experimental status, and the MS IE(edge) says they are considering them.
Fortunately we have polyfills called "webcomponents.js" which enable web components in most browsers that lack native support. The details about web component specs and webcomponents.js polifills can be found at its official site: webcomponents.org
Facebook's react.js and the Google's polymer bring us the trend of component based development, of course, Asta4js allow you do all the web component related tasks by Asta4js too.
When we introduce the web component into our development, there are two basic tasks:
There are some pre-implemented components shipped with Google's polymer project, which can not only be used within polymer itself, but also can be used as standard web components. Details can be found at Polymer Catalog .
Mozilla aslo has their own web component collection which is called brick: mozillabrick.
All of these components can be used as standard web component spec compatible components, thus Asta4js can interact with them by the standard way.
We use the polymer's paper-slider to show the brief of how to make use of web component by Asta4js.
Assume we have a html snippet as following:
<div>manual value: <input name="slider"></div>
<div><paper-slider id="pSlider" style="width:50%"></paper-slider></div>
Our purpose is to synchronize the value between the traditional input element and the modern custom component "paper-slider". Since the Asta4js has no idea about how to cope with the custom "paper-slider" component, we need to write our own binding meta rather than use the default built-in Aj.form() convenience.
According to the document of "paper-slider", we wrap our own binding meta as following:
var PaperSlider=function(selector){
return {
_selector: selector,
_render:function(target, newValue, oldValue){
if(target.prop("immediateValue") !== newValue){
target.attr("value", newValue);
}
},//_render
_register_dom_change : function(target, changeHandler, bindContext){
target.bind("immediate-value-change", function(e){
changeHandler(target.prop("immediateValue"), bindContext);
});
return function(){
changeHandler(target.prop("immediateValue"), bindContext);
}
}//_register_dom_change
}//return
}
Then, the real binding logic can be simply as following:
$scope.snippet("#slider-snippet").bind($scope.data,{
slider: [
Aj.form({},"keyup"),
PaperSlider("#pSlider")
]
});
That's all, everything has been done.
The completely runnable example can be viewed at example of using polymer's paper-slider.
There is another example of using mozilla brick calendar.
The details of how to implement a custom web component can be found at http://webcomponents.org/resources/.
We will show the brief of how to use Asta4js to cope with the data binding inside the custom component.
Assume we have a custom element called "book-card", then we declare it in our html as following:
<book-card id="edit-card"></book-card>
Of course, as introduced above, we use Asta4js to bind our model to the cutom element:
$scope.snippet(".x-book-info-snippet").bind($scope.data,{
currentBook: {
_selector: "#edit-card",
_render: function(target, newValue){
target.prop("book", newValue);
}
}
});
Then turn to the component side, the following shows the template of our custom "book-card" element:
<ul>
<li><label>title:</label><input name="title">
<li><label>year:</label><input name="year">
</ul>
Then, let's implement the necessary skeleton of a web component, which is unrelated to Asta4js:
(function(){
var currentScript = document._currentScript || document.currentScript;
var currentDocument = currentScript.ownerDocument;
var BookCard = Object.create(HTMLElement.prototype, {
createdCallback: {
value: function(){
var t = currentDocument.querySelector("#bookCardTemplate");
var clone = currentDocument.importNode(t.content, true);
var sRoot = this.createShadowRoot();
sRoot.appendChild(clone);
this.data = {};
this.shadowRoot = sRoot;
}
},
attachedCallback : {
value : function(){
...
}
}//attachedCallback
});
Object.defineProperties(BookCard, {
'book': {
set: function(v){
if(v && typeof v === "string"){
this.data.book = JSON.parse(v);
}else{
this.data.book = v;
}
},
get: function(v){
return this.data.book;
}
}
});
document.registerElement('book-card', {prototype: BookCard});
})();
The "attachedCallback" left empty in above source snippet, then we will add the binding logic to the attachedCallback:
attachedCallback : {
value : function(){
this.data.book = this.getAttribute("book");
var self = this;
Aj.init(function($scope){
$scope.data = self.data;
$scope.snippet($(self.shadowRoot).find("#card")).bind($scope.data, {
book: {
title: Aj.form({},null, ["keyup"]),
year: Aj.form({},null, ["keyup"]),
}
});
});
}
}//attachedCallback
That's all. Notice that we do not fire any event in our "book-card" component but we bind the passed instance directly to our input elements, which cause any input inside the component will be synchronized to outside immediately.
A complete runnable example can be viewed at example of custom component which also contains how to synchronize data by event notify from the custom component.