Componentes com AngularJS
Mais poder ao HTML com AngularJS
guiseek@gmail.com
HTML
HyperText Markup Language
Tags
- Palavras entre parênteses angulares
- Marcação semântica
- Hierarquia de elementos
- Atributos e seus valores
Exemplo
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<header></header>
<nav></nav>
<main>
<section>
<article></article>
</section>
<aside>
</aside>
</main>
</body>
</html>
Exemplo sem semântica
<div>
<div>26/09/2016</div>
<div>III Semana de Informática</div>
<div>
<span>UTFPR</span>
<div>
<span>Via Rosalina Maria dos Santos, 1233</span>
<span>Campo Mourão</span>
<span>Paraná</span>
</div>
</div>
</div>
Exemplo com semântica
<div itemscope itemtype="http://schema.org/Event">
<div itemprop="startDate" content="2016-09-26T08:00">26/09/2015 08:00</div>
<div itemprop="name">III Semana de Informática</div>
<div itemprop="location" itemscope itemtype="http://schema.org/Place">
<span itemprop="name">UTFPR</span>
<div itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">
<span itemprop="streetAddress">Via Rosalina Maria dos Santos, 1233</span>
<span itemprop="addressLocality">Campo Mourão</span>
<span itemprop="addressRegion">Paraná</span>
</div>
</div>
</div>
Web Components
WTF?!
Web Components é composto de várias tecnologias distintas. Você pode pensar em Web Components como widgets de interface de usuário reutilizáveis que são criados utilizando tecnologias Web abertas. Eles fazem parte do navegador, e assim eles não precisam de bibliotecas externas, como jQuery ou Dojo. Um componente da Web existente pode ser usado sem escrever código, simplesmente adicionando uma instrução de importação para uma página HTML. Componentes Web usam novos ou ainda recursos em desenvolvimento do navegador.
- Custom Elements
- HTML Templates
- Shadow DOM
- HTML Imports
Web Components consiste nestas quatro tecnologias (embora cada uma possa ser usada separadamente):
Custom Elements
Nomes e scripts criados pelo desenvolvedor.
HTML Templates
Designers e Front-enders na view (HTML, CSS e/ou JS) e engenheiros na lógica e integração com uma API server-side.
Shadow DOM
Parte do código encapsulada e escondida pelo browser.
HTML Imports
Custom elements empacotados e lidos como um recurso.
Exemplo
<template>
<p>Hello <strong></strong> :)</p>
</template>
<script>
(function(window, document, undefined) {
var thatDoc = document;
var thisDoc = (thatDoc._currentScript || thatDoc.currentScript).ownerDocument;
var template = thisDoc.querySelector('template').content;
var MyElementProto = Object.create(HTMLElement.prototype);
MyElementProto.who = 'World';
MyElementProto.createdCallback = function() {
var shadowRoot = this.createShadowRoot();
var clone = thatDoc.importNode(template, true);
shadowRoot.appendChild(clone);
this.strong = shadowRoot.querySelector('strong');
if (this.hasAttribute('who')) {
var who = this.getAttribute('who');
this.setWho(who);
}
else {
this.setWho(this.who);
}
};
MyElementProto.attributeChangedCallback = function(attr, oldVal, newVal) {
if (attr === 'who') {
this.setWho(newVal);
}
};
MyElementProto.setWho = function(val) {
this.who = val;
this.strong.textContent = this.who;
};
window.MyElement = thatDoc.registerElement('hello-world', {
prototype: MyElementProto
});
})(window, document);
</script>
Exemplo de uso
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title><hello-world></title>
<script src="webcomponents.min.js"></script>
<link rel="import" href="hello-world.html">
</head>
<body>
<hello-world who="Unicorn"></hello-world>
</body>
</html>
Como posso me aventurar?
Polymer

Lib polyfill
Web Components
Grupo de discussão
Ooou...

AngularJS
HTML é ótimo para declarar documentos estáticos, mas vacila quando tentamos usá-lo para declarar visualizações dinâmicas em aplicações web.
AngularJS permite estender elementos HTML para a sua aplicação.
O resultado é expressivo, legível e rápido para se desenvolver.
JavaScript The Right Way
Criado pelo Google, Angular.js é como um polyfill para o futuro do HTML.

Não sou eu quem está dizendo...
Camadas
- Controllers
- Services
- Factories
- Directives
- Providers
- Components
MVC ou MVWhatever
Dependency Injection
Data binding
Módulos e Componentes
A criação de módulos e componentes ficou fácil, por consequência disto, atualmente uma busca no NPM por angular retorna 9.373 packages e crescendo a cada dia.
Angular Component
Com AngularJS, um componente é um tipo especial de diretiva que usa uma configuração mais simples que é adequada para uma estrutura de aplicativo baseado em componentes.
Isto torna mais fácil escrever um aplicativo de forma semelhante ao uso de web components ou utilizando o estilo de arquitetura com aplicação Angular 2.
Vantagens
- Reutilização de código no seu web app ou em futuros
- Seguir padrões de novas tendências do mercado
Exemplo de Web App simples
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
</head>
<body>
<div>
<label>Nome:</label>
<input type="text" ng-model="seuNome" placeholder="Seu nome">
<hr>
<h1>Olá {{seuNome}}!</h1>
</div>
</body>
</html>
Mais poder com diretivas do AngularJS
<div ng-controller="UsuarioCtrl as ctrl">
<form ng-submit="ctrl.submit()" name="usuarioForm" novalidate>
<div>
<label>Usuário<label>
<input type="text" name="usuario" ng-model="ctrl.usuario" ng-minlength="5" ng-maxlength="10" required>
<div ng-show="usuarioForm.$submitted || usuarioForm.usuario.$touched">
<div ng-show="usuarioForm.usuario.$error.required">Preencha seu nome.</div>
<div ng-show="usuarioForm.usuario.$error.minlength">Deve ter mais de 5 caracteres.</div>
<div ng-show="usuarioForm.usuario.$error.maxlength">Deve ter menos de 10 caracteres.</div>
</div>
<div>
<button type="submit" ng-disabled="usuarioForm.$invalid">Salvar</button>
</div>
</div>
</form>
</div>
Componentes
Como o AngularJS lida com manipulação do DOM e renderiza UI/Componentes reutilizáveis.
Desde trechos HTML simples até componentes complexos com comportamentos específicos definidos pelo desenvolvedor.
Angular Component
Vamos ver como funciona um componente
Entradas e Saídas
Isolamento escopo só vai tão longe, porque angular usa two-way bindings. Então, se você passar um objeto para um componente desta forma:
bindings: {item: '='}
E modificar uma de suas propriedades, a alteração será refletida no componente pai. No entanto, apenas o componente que possui os dados deve modificá-lo, para tornar mais fácil o raciocínio sobre quais e quando os dados forem alterados.
Entradas / Saídas
Entradas devem estar usando bindings < e @.
O símbolo < define one-way bindings.
A diferença para o = é que as propriedades ligadas no componente não são observadas, portanto, se você atribuir um novo valor no escopo do componente, ele não será refletido no escopo pai.
O símbolo @ pode ser usado quando a entrada é uma string, principalmente quando o valor não muda.
Exemplo
bindings: {
title: '@',
options: '=',
musics: '<'
}
Entradas / Saídas
O símbolo & define chamadas de retorno para eventos de componentes.
O componente chama um evento de saída com os dados alterados. Isso significa que o componente não é responsável pela tarefa, mas envia de volta para o responsável.
Exemplo
bindings: {
onSetMusic: '&'
},
Lifecycle
Componentes devem controlar apenas seu escopo e nunca elementos DOM ou quaisquer dados de fora.
Com angular é possível modificar dados em qualquer lugar da aplicação usando herança de escopo e watches. Isto é prático, mas também pode levar a problemas quando não é claro qual a parte da aplicação é responsável por modificar os dados. Por isso componentes angular usam escopo isolado, restringindo a manipulação de escopo.
$onInit()
Chamado em cada controlador depois de todos os controladores em um elemento forem construídos e tiveram seus bindings inicializados.
Este é um bom lugar para colocar código de inicialização para seu controlador.
$onChanges(obj)
Chamado sempre que os one-way bindings são actualizados.
O obj é um hash cujas keys são os nomes das propriedades vinculadas que foram alterados, e os valores são um objeto {currentValue, previousValue, isFirstChange()}.
Utilize este gancho para acionar atualizações dentro de um componente, tais como a clonagem do valor para evitar mutação acidental do valor externo.
$doCheck()
Chamado em cada retorno do ciclo de digest. Oferece uma oportunidade para detectar e agir sobre as mudanças.
Este não tem efeito sobre quando o $onChanges é chamado.
$onDestroy()
Chamado em um controlador quando o seu escopo é destruído.
Utilize este gatilho para liberar recursos externos, watches e manipuladores de eventos.
$postLink()
Chamado após o elemento deste controlador e seus filhos terem sido associados. Este gatilho pode ser usado para configurar manipuladores de eventos DOM e fazer manipulação do DOM diretamente.
Módulo
import AudioPlayerComponent from './component/audio.js'
import AudioListComponent from './component/audio.list.js'
angular
.module('audio.player', [])
.component('audioPlayer', AudioPlayerComponent)
.component('audioList', AudioListComponent)
Component Audio Player
import template from './audio.template.js'
let AudioPlayerComponent = {
restrict: 'E',
bindings: {
title: '@',
options: '=',
musics: '<',
onSetMusic: '&'
},
template: template,
controller: function($scope, $element, $attrs, $interval) {
let ctrl = this
let createAudio = () => {
ctrl.audio = new Audio()
ctrl.audio.loop = ctrl.options.loop
ctrl.audio.autoplay = ctrl.options.autoplay
ctrl.audio.crossOrigin = ctrl.options.origin || 'anonymous'
ctrl.audio.addEventListener('ended', ctrl.next)
}
let getRandom = () => {
return Math.floor(Math.random() * ctrl.musics.length)
}
ctrl.setCurrent = current => {
ctrl.current = (current >= ctrl.musics.length) ? 0 : current
}
let song
ctrl.setSong = () => {
song = ctrl.musics[ctrl.current]
ctrl.song = `${song.artist} - ${song.title}`
ctrl.audio.src = song.value
}
ctrl.setMusic = index => {
ctrl.setCurrent(index)
ctrl.setSong()
ctrl.play()
}
ctrl.changeVolume = () => {
ctrl.audio.volume = ctrl.volume
}
ctrl.play = () => {
ctrl.audio.play()
}
ctrl.pause = () => {
ctrl.audio.pause()
}
ctrl.next = () => {
if (ctrl.options.random) {
ctrl.setMusic(getRandom())
return
}
ctrl.setMusic(ctrl.current + 1)
}
ctrl.prev = () => {
if (ctrl.options.random) {
ctrl.setMusic(getRandom())
return
}
ctrl.setMusic(ctrl.current - 1)
}
let getSvgFill = bool => {
return bool ? '#000000' : '#999999'
}
let setLoopFill = () => {
loop.style.fill = getSvgFill(ctrl.options.loop)
}
ctrl.changeLoop = () => {
ctrl.audio.loop = ctrl.options.loop = !ctrl.options.loop
setLoopFill()
}
let setRandomFill = () => {
random.style.fill = getSvgFill(ctrl.options.random)
}
ctrl.changeRandom = () => {
ctrl.options.random = !ctrl.options.random
setRandomFill()
}
let h, m, s
let secToTime = (seconds) => {
h = Math.floor((seconds % 86400) / 3600)
m = Math.floor(((seconds % 86400) % 3600) / 60)
s = ((seconds % 86400) % 3600) % 60
return `${('00'+h).slice(-2)}:${('00'+m).slice(-2)}:${('00'+s).slice(-2)}`
}
let duration, currentTime
$interval(() => {
duration = Math.floor(ctrl.audio.duration)
currentTime = Math.floor(ctrl.audio.currentTime)
ctrl.time = secToTime(currentTime)
ctrl.timeLeft = secToTime(duration - currentTime)
ctrl.percentage = Math.floor((100 / ctrl.audio.duration) * ctrl.audio.currentTime)
}, 100)
let audioContext, analyser, source, fbc_array
let createAudioContext = () => {
audioContext = new (window.AudioContext || window.webkitAudioContext)
source = audioContext.createMediaElementSource(ctrl.audio)
analyser = audioContext.createAnalyser()
fbc_array = new Uint8Array(analyser.frequencyBinCount)
source.connect(analyser)
analyser.connect(audioContext.destination)
}
let canvas, canvasCtx
let createCanvas = () => {
canvas = document.getElementById('analyser')
canvasCtx = canvas.getContext('2d')
}
let defineSizesCanvas = () => {
canvas.width = 200
canvas.height = 300
}
let bars, bar_x, bar_width, bar_height
let frameLooper = () => {
window.requestAnimationFrame(frameLooper)
analyser.getByteFrequencyData(fbc_array)
render()
}
let render = () => {
canvasCtx.clearRect(0, 0, canvas.width, canvas.height)
canvasCtx.fillStyle = 'orange'
bars = 200
for (let i = 0; i < bars; i++) {
bar_width = canvas.width / bars
bar_x = i * (bar_width + 2)
bar_height = -(fbc_array[i])
canvasCtx.fillRect(bar_x, canvas.height, bar_width, bar_height)
}
}
ctrl.$onInit = () => {
ctrl.current = 0
ctrl.volume = 1
ctrl.time = 0
createAudio()
createAudioContext()
createCanvas()
defineSizesCanvas()
frameLooper()
ctrl.setSong()
}
let loop, random
ctrl.$postLink = () => {
loop = $element[0].querySelector('#loop')
setLoopFill()
random = $element[0].querySelector('#random')
setRandomFill()
}
}
}
export default AudioPlayerComponent
Template Audio Player
import style from './audio.style.js'
export default `
<style>${style}</style>
<fieldset>
<legend>{{$ctrl.title}}</legend>
<canvas id="analyser"></canvas>
<div id="controls">
<button type="button" ng-click="$ctrl.play()">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M8,5.14V19.14L19,12.14L8,5.14Z" /></svg>
</button>
<button type="button" ng-click="$ctrl.pause()">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M14,19H18V5H14M6,19H10V5H6V19Z" /></svg>
</button>
<button type="button" ng-click="$ctrl.prev()">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M6,18V6H8V18H6M9.5,12L18,6V18L9.5,12Z" /></svg>
</button>
<button type="button" ng-click="$ctrl.next()">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M16,18H18V6H16M6,18L14.5,12L6,6V18Z" /></svg>
</button>
<progress value="{{$ctrl.percentage}}" max="100">{{$ctrl.percentage}} %</progress>
<div id="time">
<span ng-bind="$ctrl.time"></span>
<span ng-bind="$ctrl.timeLeft"></span>
</div>
<div id="options">
<label data-ng-click="$ctrl.changeLoop()">
<svg id="loop" fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>
</label>
<label data-ng-click="$ctrl.changeRandom()">
<svg id="random" fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M10.59 9.17L5.41 4 4 5.41l5.17 5.17 1.42-1.41zM14.5 4l2.04 2.04L4 18.59 5.41 20 17.96 7.46 20 9.5V4h-5.5zm.33 9.41l-1.41 1.41 3.13 3.13L14.5 20H20v-5.5l-2.04 2.04-3.13-3.13z"/>
</svg>
</label>
</div>
<input type="range" ng-change="$ctrl.changeVolume()" ng-model="$ctrl.volume" min="0.0" max="1" step="0.1" ng-value="$ctrl.volume">
<span id="volume">{{$ctrl.volume}}</span>
</div>
<div id="current">
<marquee scrolldelay="200">{{$ctrl.song}}</marquee>
</div>
<audio-list musics="$ctrl.musics" on-set-music="$ctrl.setMusic(index)"></audio-list>
</fieldset>`
Style Audio Player
export default `
audio-player {
font-family: Arial, Helvetica, sans-serif;
font-size: 13px;
}
audio-player fieldset {
width: 205px;
padding: 4px 9px 12px 10px;
background-color: #f5f5f5;
border: 2px solid #ccc;
}
audio-player legend {
padding: 0 6px;
}
audio-player canvas {
width: 200px;
height: 100px;
background-color: #eaeaea;
border: 2px solid #ccc;
}
audio-player #controls {
margin-top: 6px;
}
audio-player #controls button {
margin: 0 0 4px;
padding: 4px 0;
width: 48px;
font-size: 12px;
background-color: #fff;
border: 2px solid #ccc;
}
audio-player #controls #time,
audio-player #controls #options label {
cursor: pointer;
}
audio-player #controls #options {
width: 202px;
}
audio-player #controls #time span:last-child,
audio-player #controls #options label:last-child {
text-align: right;
float: right;
}
audio-player #controls progress {
width: 202px;
margin: 5px 0;
}
audio-player #controls #volume {
margin: 4px 0;
width: 20px;
float: right;
text-align: right;
}
audio-player #controls input[type="range"] {
margin: 2px 0;
width: 180px;
}
audio-player #current {
margin: 0 0 2px 0;
padding: 4px 0;
border-bottom: 2px solid #ccc;
}
audio-player #current input[type="text"] {
width: 202px;
background-color: transparent;
border: 0;
}`
Component Audio List
import template from './audio.list.template.js'
let AudioListComponent = {
restrict: 'E',
bindings: {
musics: '<',
onSetMusic: '&'
},
template: template,
controller: function($scope, $element, $attrs) {
let ctrl = this
ctrl.setMusic = index => {
ctrl.onSetMusic({index: index})
}
ctrl.toggle = () => {
ctrl.show = !ctrl.show
}
ctrl.$onInit = () => {
ctrl.show = true
}
}
}
export default AudioListComponent
Template Audio List
import style from './audio.list.style.js'
export default `
<style>${style}</style>
<button type="button" data-ng-click="$ctrl.toggle()">
<svg data-ng-show="!$ctrl.show" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M10,4H14V13L17.5,9.5L19.92,11.92L12,19.84L4.08,11.92L6.5,9.5L10,13V4Z" /></svg>
<svg data-ng-show="$ctrl.show" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M14,20H10V11L6.5,14.5L4.08,12.08L12,4.16L19.92,12.08L17.5,14.5L14,11V20Z" /></svg>
</button>
<ul id="list" data-ng-class="{hide: !$ctrl.show}">
<li ng-repeat="music in $ctrl.musics" ng-click="$ctrl.setMusic($index)">
<a>{{music.artist}} - {{music.title}}</a>
</li>
</ul>`
Style Audio List
export default `
audio-player audio-list {
display: block;
position: relative;
}
audio-player audio-list button {
position: absolute;
left: 82px;
bottom: -38px;
cursor: pointer;
background-color: transparent;
border: 0;
}
audio-player audio-list #list {
padding: 0;
width: 202px;
max-height: 120px;
overflow-y: scroll;
list-style: none;
-webkit-transition: all 1s ease;
-moz-transition: all 1s ease;
-ms-transition: all 1s ease;
-o-transition: all 1s ease;
transition: all 1s ease;
}
audio-player audio-list #list.hide {
max-height: 0;
}
audio-player audio-list #list li {
margin: 3px 0;
padding: 3px 0;
cursor: pointer;
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;
border-bottom: 1px solid #ccc;
}
audio-player audio-list #list li:last-child {
border-bottom: 0;
}`
Exemplo de uso
Módulo do Web App e Controller
angular
.module('app', ['audio.player'])
.controller('AppController', ['$scope', function ($scope) {
var ctrl = this;
ctrl.options = {
autoplay: true,
loop: false,
random: true,
origin: 'anonymous' // Optional, anonymous is default
}
ctrl.list = [
{
artist: 'Chasing Ghosts',
title: 'Fallen From Grace',
album: 'Chasing Ghosts',
value: 'musics/Chasing_Ghosts-Fallen_From_Grace.mp3'
},
{
artist: 'Sepultura',
title: 'Refuse/Resist',
album: 'Chaos A.D.',
value: 'musics/Sepultura-Refuse_Resist.mp3'
},
{
artist: 'Raimundos',
title: 'Baily Funk',
album: 'Lapadas do povo',
value: 'musics/Raimundos-Baily_Funk.mp3'
}
]
}])
Index
<!doctype html>
<html data-ng-app="app">
<head>
<meta charset="utf-8">
<title>Angular Audio Player Component - HTML5/ES6</title>
<link rel="stylesheet" href="src/component/style.css">
</head>
<body data-ng-controller="AppController as ctrl">
<!-- Component Audio Player -->
<audio-player title="Audio Player" options="ctrl.options" musics="ctrl.list"></audio-player>
<!-- /Component Audio Player -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<script src="dist/bundle.min.js"></script>
<script src="app.js"></script>
</body>
</html>
Tchanammm...
ES6?
Já posso usar no front-end?
Sebastian McKenzie
Criador do BabelJS
Babel is a JavaScript compiler.
Use next generation JavaScript, today.
Como?
npm install babel-cli --save-dev
npm install babel-preset-es2015 --save-dev
npm install babelify --save-dev
Vamos mais fundo
{
"name": "angular-audio-player-html5-es6",
"version": "2.1.0",
"description": "Angular Audio Player Component - HTML5/ES6",
"main": "./src/index.js",
"scripts": {
"dev": "npm-run-all -s -c build:concat",
"build": "npm-run-all -p build:sequential",
"build:sequential": "npm-run-all -s -c build:concat build:minify",
"build:concat": "browserify ./src/index.js -o ./dist/bundle.js -t [ babelify ]",
"build:minify": "uglifyjs ./dist/bundle.js -o ./dist/bundle.min.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"angular": "^1.5.8"
},
"babel": {
"presets": [
"es2015"
]
},
"devDependencies": {
"babel-cli": "^6.11.4",
"babel-preset-es2015": "^6.13.2",
"babelify": "^7.3.0",
"browserify": "^13.1.0",
"npm-run-all": "^2.3.0",
"uglify-js": "^2.7.0",
"watchify": "^3.7.0"
},
"repository": {
"type": "git",
"url": "https://github.com/guiseek/angular-audio-player-html5-es6.git"
},
"keywords": [
"angular",
"audio",
"player",
"component",
"HTML5",
"ES6"
],
"author": "Gui Seek <guiseek@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/guiseek/angular-audio-player-html5-es6/issues"
},
"homepage": "https://github.com/guiseek/angular-audio-player-html5-es6"
}
Conclusão
O HTML (em parceria com JavaScript) está ficando poderoso para criação de aplicações fazendo uso de componentes reutilizáveis.
Usem e escrevam seus custom elements! : )
Muito obrigado!
Meu Github
Sobre meu trabalho
DevParaná
Components com AngularJS
By Guilherme Siquinelli
Components com AngularJS
- 59