Introducción a WebComponents y VisualStudio
VisualStudio
WebComponents
Agenda Introducción Qué son los
WebComponents? Mi primer WebComponent Construcción de una
aplicación
3
Software Engineer at [email protected]@pekewakehttps://github.com/ruchavarri
Rubén Chavarri
Who are we:
Software Architect at Ciber Españ[email protected]@dvdchavarrihttps://github.com/dvdchavarri
David Chavarri
#DISCLAIMERconst talk = best ? fav_framework :
webcomponents;
@pekewake
Introducción al desarrollo web orientado a webcomponents
@dvdchavarri
El problema de elegir un framework de desarrollo front-end
Front-end Choice Paralisis
@dvdchavarri
Por qué Web Components
Reutilización Encapsulación
Homogeneidad
Composición
@dvdchavarri
“ Componentes Web es la tecnología que permiten definir tus propias etiquetas HTML personalizadas que incluyen una semántica, un comportamiento funcional y una lógica de presentación propia. ”
Recogido en el estándar W3C, 2011
Qué son los WebComponents?
@dvdchavarri
Custom element
Modelo de extensibilidad que permite definir nuevas etiquetas o redefinir las etiquetas estándar HTML
W3C Web Component Especification
ImportPosibilidad de cargar ficheros de código HTML dentro de otros ficheros permitiendo modularizar elementos y empaquetarlos.
Shadow DomModelo de encapsulación que permite aislar el contenido interno del componente.
TemplateModelo de renderización basado en plantillas de código HTML que sólo son activadas cuando se renderiza el componente
@dvdchavarri Link plunker
Web Components:
VanillaJS Polymer X-tag (micro)
• Diferentes Implementaciones:
@dvdchavarri
Soporte en los Navegadores
@dvdchavarri
Soporte en los Navegadores con Polyfills
@dvdchavarri
EjemploMi primer Web Component con Polymer
@dvdchavarri
<link rel="import" href="../polymer/polymer.html"><link rel="import" href="../iron-icon/iron-icon.html"><dom-module id="icon-toggle"> <template> <style>
stroke: var(--icon-toggle-outline-color, currentcolor); } :host([pressed]) iron-icon { fill: var(--icon-toggle-pressed-color, currentcolor); }
</style> <iron-icon icon="polymer"> </iron-icon> </template> <script> Polymer({ /* this is the element's prototype */ is: 'icon-toggle', properties: {
toggleIcon: String, pressed: { type: Boolean, value: false, notify: true }
}, listeners: {
'tap': 'toggle' }, toggle: function() {
this.pressed = !this.pressed; }, }); </script></dom-module>
Imports Mi primer WebComponent
Link plunker
Template
<link rel="import" href="../polymer/polymer.html"><link rel="import" href="../iron-icon/iron-icon.html"><dom-module id="icon-toggle"> <template> <style>
stroke: var(--icon-toggle-outline-color, currentcolor); } :host([pressed]) iron-icon { fill: var(--icon-toggle-pressed-color, currentcolor); }
</style> <iron-icon icon="polymer"></iron-icon> </template> <script> Polymer({ /* this is the element's prototype */ is: 'icon-toggle', properties: {
toggleIcon: String, pressed: { type: Boolean, value: false, notify: true }
}, listeners: {
'tap': 'toggle' }, toggle: function() {
this.pressed = !this.pressed; }, }); </script></dom-module>
Mi primer WebComponent
Link plunker
Link plunker
<link rel="import" href="../polymer/polymer.html"><link rel="import" href="../iron-icon/iron-icon.html"><dom-module id="icon-toggle"> <template> <style>
stroke: var(--icon-toggle-outline-color, currentcolor); } :host([pressed]) iron-icon { fill: var(--icon-toggle-pressed-color, currentcolor); }
</style> <iron-icon icon="polymer"> </iron-icon> </template> <script> Polymer({ /* this is the element's prototype */ is: 'icon-toggle', properties: {
toggleIcon: String, pressed: { type: Boolean, value: false, notify: true }
}, listeners: {
'tap': 'toggle' }, toggle: function() {
this.pressed = !this.pressed; }, }); </script></dom-module>
CustomElement
Mi primer WebComponent
EjemploComposición de WebComponentscon Polymer
@dvdchavarri
Ejemplo composición de WebComponents
Link plunker
<body unresolved> <template is="dom-bind"> <google-map map="{{map}}“ latitude="40.4185115“ longitude="-3.7983468" zoom="{{zoom}}"> <google-map-marker latitude="40.4185115" longitude="-3.7983468" title="Go WebComponents!"> </google-map-marker> </google-map> <google-map-directions map="{{map}}" start-address="{{start}}“ end-address="{{end}}"> </google-map-directions> <paper-card elevation="2"> <paper-icon-item> <iron-icon icon="search" item-icon></iron-icon> <paper-input label="Start address" value="{{start}}"></paper-input> </paper-icon-item> <paper-icon-item> <iron-icon icon="search" item-icon></iron-icon> <paper-input label="End address" value="{{end}}"></paper-input> </paper-icon-item> <paper-icon-item> <iron-icon icon="icons:aspect-ratio" item-icon></iron-icon> <paper-slider min="10" max="20" value="{{zoom}}"></paper-slider> </paper-icon-item> </paper-card> </template> </body>
Polymer - Catálogo de componentes.
@dvdchavarri
Desarrollo de una Aplicacióncon WebComponentsen ASP.NET
@pekewake
Terror
Descomposición de la aplicación
<TODO-APP>@pekewake
<TODOS><DONE>@pekewake
<TODO-ELEMENT>@pekewake
<TODO-API>@pekewake
<todo-api>
<todo-app> <todo-list>
<todo-element>
Descomposición de la aplicación en
WebComponents
@pekewake
<todo-api>
<todo-app> <todo-list>
<todo-element>
@pekewake
<dom-module id="todo-element" attributes="task user rid"><style> …</style> <template>
<paper-material class="todo" elevation="1"> <paper-checkbox checked="{{completed}}"></paper-checkbox> <paper-fab hidden={{editing}} icon="icons:create" on-tap="doEdit" class="edit"
mini></paper-fab> <paper-fab hidden={{!editing}} icon="icons:done" on-tap="doEdit" class="done"
mini></paper-fab> <paper-item hidden="{{editing}}" id="task">{{task}}</paper-item> <paper-input id="edit" hidden="{{!editing}}" value="{{task}}"></paper-input> <paper-item class="info">Created by: <span>{{ user }}</span></paper-item> <paper-item class="info">{{ time }}</paper-item>
</paper-material> </template>
</dom-module><script>Polymer({ is: "todo-element", properties: {
user: {type: String}, task: {type: String}, rid: {type: Number}, completed: { observer:’compChaged’ } editing: { value: false } } compChanged: function(e){
if(e){ this.fire('complete',this); } }, doEdit: function(e){ this.editing=!this.editing; if (!this.editing) { this.fire('edit', {rid:this.rid, task:this.task}); } }, }});</script>
Properties
Events
Template
<todo-element>
<todo-api>
<todo-app> <todo-list>
<todo-element>
@pekewake
<dom-module id="todo-app"><style>…</style> <template>
<template is="dom-repeat" items="{{done}}"><paper-item>{{item.task}}<i >{{item.user}}</i></paper-item>
</template><paper-material id="todoEntry" elevation="2">
<paper-input id="tTask" label="Task"></paper-input><paper-input id="tUser" char-counter label="Username" ></paper-input> <paper-fab icon="icons:add" on-tap="postTask"></paper-fab>
</paper-material> <template is="dom-repeat" items="{{todo}}">
<todo-element user="{{item.user}}" task="{{item.task}}" rid="{{item.rid}}"></todo-element> </template>
<todo-storage local-list="{{done}}" local-storage="localDone"></todo-storage><todo-api local-list="{{todo}}" ></todo-api>
</template></dom-module><script>…</script>
{{done}}
{{todo}}
<todo-api>
<todo-app> <todo-list>
<todo-element>
@pekewake
<dom-module id="todo-app"><style>… </style> <template> …
<paper-material id="todoEntry" elevation="2"><paper-input id="tTask" label="Task"></paper-input><paper-input id="tUser" char-counter label="Username" ></paper-input> <paper-fab icon="icons:add" on-tap="postTask"></paper-fab>
</paper-material> …</template></dom-module><script>Polymer({ is: "todo-app", properties: {todo: { type: Array}, done: { type: Array} },
postTask: function(e) { this.push("todo", { user: usr, task: tsk, rid: this.todo.length });},handleComplete: function(e){
var idx = findWithAttr(this.todo, 'rid',e.target.rid); var itm = this.splice('todo', idx, 1)[0];
},handleEdit: function(e){ this.set('todo.' + e.target.rid, { task: e.target.task, user: e.target.user, rid: e.target.rid });},ready: function(e){
this.addEventListener('complete', this.handleComplete);this.addEventListener('edit', this.handleEdit);
}…
</script>
Properties
Events
Template
<todo-app>
<todo-api>
<todo-app> <todo-list>
<todo-element>
@pekewake
<todo-api>
<dom-module id="todo-app"><style>…</style> <template>
<template is="dom-repeat" items="{{done}}"><paper-item>{{item.task}}<i style="margin-left: 5px;">{{item.user}}</i></paper-
item></template><paper-material id="todoEntry" elevation="2">
<paper-input id="tTask" label="Task"></paper-input><paper-input id="tUser" char-counter label="Username" ></paper-input> <paper-fab icon="icons:add" on-tap="postTask"></paper-fab>
</paper-material> <template is="dom-repeat" items="{{todo}}">
<todo-element user="{{item.user}}" task="{{item.task}}" rid="{{item.rid}}"></todo-element> </template>
<todo-storage local-list="{{done}}" local-storage="localDone"></todo-storage><todo-api local-list="{{todo}}" ></todo-api>
</template></dom-module><script>…</script>
<todo-storage>
¿Cómo nos comunicamos con Backend?
ASP.NET
@pekewake
<todo-api>
<todo-app> <todo-list>
<todo-element>
Cómo nos comunicamos con Back-end?
Web API
<todo-api><dom-module id="todo-api"><template>
<iron-ajax id="ajax“ handle-as="json“ content-type="application/json"></iron-ajax></template></dom-module><script>Polymer({ is: 'todo-api', properties: { localList: { value: [] } }, observers: [ 'listChange( localList.* )' ], listChange: function (changeRecord) { var _this = this; if (changeRecord.path == 'localList.splices') {
changeRecord.value.indexSplices.forEach(function (s) { s.removed.forEach(function (item) {
_this.deleteTask(item); }); if (s.addedCount > 0) {
_this.addTask(s); } }, this);
} else {if (changeRecord.value.rid != null) {
_this.updateTask(changeRecord.value); } } },
</script>
addTask: function (task) { this.$.ajax.url = [baseUrl, "api/todo"].join('/'); this.$.ajax.body = JSON.stringify({ rid: task.object[task.index].rid,
user: task.object[task.index].user, task: task.object[task.index].task });
this.$.ajax.method = "POST"; this.$.ajax.generateRequest(); }, deleteTask: function (task) { this.$.ajax.url = [baseUrl, "api/todo", task.rid].join('/'); this.$.ajax.method = "DELETE"; this.$.ajax.generateRequest(); }, updateTask: function (task) { this.$.ajax.url = [baseUrl, "api/todo", task.rid].join('/'); this.$.ajax.body = JSON.stringify({ rid: task.rid, user: task.user, task: task.task }); this.$.ajax.method = "PUT"; this.$.ajax.generateRequest();}, ready: function (e) { var _this = this; this.$.ajax.url = [baseUrl, "api/todo"].join('/'); this.$.ajax.method = “GET"; this.$.ajax.generateRequest().completes.then(function (request) { _this.localList = request.response; });
Web API
TodoController.cs
namespace PolymerApi.Controllers{ [Route("api/[controller]")] public class TodoController : Controller { static private List<Todo> todolist = new List<Todo>(); public TodoController() { if (todolist.Count == 0) { todolist.Add(new Todo { rid = 0, task = "Happy Polymer Coding", user = "Ruben Chavarri", time = "" }); } } // GET: api/values [HttpGet] public IEnumerable<Todo> Get() { return todolist; } // GET api/values/5 [HttpGet("{id:int}")] public IActionResult Get ( int id) { return new ObjectResult(todolist[id]); } // PUT api/values/5 [HttpPut("{id:int}")] public IActionResult Put ( int id, [FromBody] Todo todo) { var index = todolist.FindIndex(row => row.rid == id); todolist[index] = todo; return new ObjectResult(todo); }
// POST api/values [HttpPost] public IActionResult Post( [FromBody] Todo todo) { todolist.Add(todo); return new ObjectResult(todo); }
// DELETE api/values/5 [HttpDelete("{id:int}")] public IActionResult Delete (int id) { var index = todolist.FindIndex(row => row.rid == id); todolist.RemoveAt(index); return new HttpStatusCodeResult(200); } }}
DemoToDo List
@pekewake
WebComponentsNuevo “Ciclo de desarrollo”
@pekewake
Implementación de WebComponents
Generadores y PlantillasEs una buena práctica partir de una plantilla (a partir de Yeoman o github seed-element)
Desarrollo Diseño del webcomponente orientando a la reutilización
DespliegueTras el desarrollo del webcomponentes se compacta con gulp/grunt (minify, resize img, compile less/sass…).
PublicaciónSe empaqueta y publica en el catalogo de webcomponentes (public/privado) para la posterior reutilización
@pekewake
Uso de WebComponents
Selección de Webcomponents, procedente de repositorios públicos/privados.
Instalación/DescargaDescarga de components mediante bower (gestor de paquetes)
Uso de componentesSe incluye import en la aplicación y se utiliza como otra etiqueta html standar
@pekewake
Thanks!@pekewake @dvdchavarri
45
Software Engineer at [email protected]@pekewakehttps://github.com/ruchavarri
Rubén Chavarri
Who are we:
Software Architect at Ciber Españ[email protected]@dvdchavarrihttps://github.com/dvdchavarri
David Chavarri
SourcesEjemplos y fuentes:http://plnkr.co/edit/fmWG1YIBS6OaItdVe68v?p=preview
http://plnkr.co/edit/BoW1xTNDEo3MybMMSgfK?p=previewhttp://plnkr.co/edit/SO7fFJVgBwNqt7mHkhNw?p=previewhttps://github.com/Twiinlab/PolymerApiVSReferencias y menciones utilizadas en las slides:https://speakerdeck.com/robdodson/end-to-end-with-polymer
http://es.slideshare.net/jvelez77/orientando-a-componentes-la-web-55613507https://scotch.io/tutorials/build-a-real-time-polymer-to-do-appOtros links de interes:https://css-tricks.com/modular-future-web-componentshttp://webcomponents.org/articles/why-web-componentshttp://www.html5rocks.com/en/tutorials/webcomponents/customelements/
https://www.polymer-project.org/1.0/https://elements.polymer-project.org/