Criando aplicações ambiciosas com Ember Js
Quem sou eu
- Graduando no curso de TSI
- Membro do GURU/PR
- Quase 4 anos de experiência com desenvolvimento web
- Atuo como desenvolvedor pleno na empresa Letsgrow Sistemas
ATENÇÃO!
não seja um profeta do apocalipse
Profetas
- Quando surge uma nova tecnologia ou tendência eles atacam
- Fazendo falsas previsões
- Sem o menor conhecimento ou dominio do assunto
- Exemplo: surgimento do MongDB, previram a extinsão do SQL
Com a consciência limpa
Podemos seguir em frente =D
A forma como construímos nossas aplicações está mudando
Pré-processadores





Uma nova versão do HTTP

Gerenciar tarefas e dependêcias



Controle e Monitoramento



Testes e mais testes




Em meio a tudo isso
- Inumeras opções para resolver casa problema fica o questionamento
Como devemos nos posicionar, em meio a tantas novidades ?
Analisar caso a caso
- As tecnologias são criadas para atender a fins
- Sendo assim, devemos discutir problemas e como ataca-los
- Ao invés de brigar por qual linguagem é mais legal
Em meio a todas essas novidades
Resurgiu um paradigma no desenvolvimento web
Single Page Application
Single Page Application
- Também chamadas de aplicações de página única
- Diferente das aplicações convencionais, buscam enviar o HTML, CSS e Javascript apenas na primeira requisição
- A partir desse ponto, fica a cargo do framework do client-side, renderizar templates e se comunicar com o servidor por meio de uma API utilizando AJAX
Web tradicional

Como SPA Funciona

E quanto a organização ...
- No server-side, fica um framework qualquer, trabalhando como uma API RESTfull
- O que torna o SPA completamente agnóstico quanto a framework (ex: Laravel, Rails, Django)
- E no client-side, fica um framework que segue o paradigma SPA
Eis que surgem




Ember Js

Ember Js
- Criado a partir do SproutCore, por Tom Dale e Yehuda Katz
- Foi desenvolvido para aplicações ambiciosas e complexas
- Utiliza o conceito MVC e convenção sobre configuração
MVC

Hello Ember !
# Criando um novo projeto com o Ember cli
$ ember new workshop
# Entrando no diretório
$ cd workshop/
# Utilize dois terminais
# Em um deles utilize o watch
$ ember build --watch
# No outro o servidor
$ ember server
http://localhost:4200

Página sobre
# Criando uma nova página
$ ember generate route about
# Verificar os arquivos alterados e criados
# app/router.js
# app/routes/about.js
# app/templates/about.hbs
O que fizemos ?
- O Ember possui versão em Js puro e outra em node, chamada ember-cli, o qual estamos usando
- Usamos os generators para criar nossos arquivos
- No ember-cli usamos ES6, por essa razão o comando build --watch, deixa o ember "ouvindo" toda vez que salvamos um arquivo, para que o Babel os compile para a versão atual do JS
Como o Ember é divido ?
- Ember, pacote que possui as classes base do framework, por exemplo, Object
- Ember-data, responsável pela comunicação com API's externas
- Handlebars, biblioteca externa responsável por manipular os templates (if, for, etc.)
Exercício
<div>
<label>Nome:</label>
{{input type="text" value=name}}
</div>
<div>
<h3>Seu nome é {{name}}</h3>
</div>
app/templates/about.hbs
Two Way Binding
- As mudanças são propagadas em ambas das direções
- Tanto na variável criada, quanto na UI
- Conceito chave para entender outros frameworks, como Angular e React
jQuery
- É comum ouvir profetas falando "Angular vai acabar com jQuery", "Largue mão disso, vai usar Angular que é o futuro"
- jQuery não pode ser comparado a frameworks SPA
- Porque é uma biblioteca
Adicionando Bootstrap
# Adiciona bootstrap ao seu projeto
$ ember install ember-bootstrap
Navbar
<div class="container">
<div class="row">
<div class="col-md-12">
<nav class="navbar navbar-inverse">
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a href="/about">About</a></li>
</ul>
</div>
</nav>
</div>
</div>
{{content-for "body"}}
</div>
app/index.html
Debug
- Utilizar um debug é uma ótima forma para entender melhor como nossa ferramenta funciona
- O Ember possui um pluggin para debug chamado ember inspect, funciona tanto para o Google Chrome quanto para o Mozilla Firefox
Logs
- Outra forma de enteder o funcionamento interno de nossa aplicação é usar logs
- O Ember possui logs muito detalhados para ajudar em seu desenvolvimento
Logs
App = Ember.Application.extend({
// Adicionar as 3 linhas abaixo
LOG_TRANSITIONS: true,
LOG_TRANSITIONS_INTERNAL: true,
LOG_ACTIVE_GENERATION: true,
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix,
Resolver
});
app/app.js
Workflow

Workflow - Exemplo
- Para entender melhor a divisão de responsabilidades e a ordem como as coisas são carregadas, vamos fazer um exemplo de um formulário de contato
Contato - Rota
Router.map(function() {
this.route('about');
// Adicionar as linhas abaixo
this.resource('contact', function() {
this.route('new');
});
});
// Após adiciona-las
// confira o ember inspector
app/router.js
Contato - Rota
import Ember from 'ember';
export default Ember.Route.extend({
init: function() {
Ember.Logger.debug("IndexRoute");
this._super();
}
});
app/routes/contact/index.js
Contato - Controller
import Ember from 'ember';
export default Ember.Controller.extend({
init: function() {
Ember.Logger.debug("IndexController");
this._super();
}
});
app/controllers/contact/index.js
Contato - Template
ContactIndexTemplate
app/templates/contact/index.hbs
Contato - Rota
- Observe o console do navegador
- Há uma mensagem, dizendo que o resources será removido nas próximas versões
- As mensagens do Ember são muito intuítivas
Contato - Rota
Router.map(function() {
this.route('about');
// Adicionar as linhas abaixo
this.route('contact', { resetNamespace: true }, function() {
this.route('new');
});
});
// Após alterar
// confira os logs novamente
app/router.js
CRUD simples
- Vamos fazer um pequeno CRUD de livros
- Cada livro terá um título e um campo para conteúdo
CREATE
Rota
Router.map(function() {
this.route('about');
this.route('contact', { resetNamespace: true }, function() {
this.route('new');
});
// Adicionar as linhas abaixo
this.route('book', { resetNamespace: true } ,function() {
this.route('new');
});
});
// Após alterar
// confira os logs novamente
app/router.js
Formulário
<h1>Novo Livro</h1>
<form>
<div class="form-group">
<label for="title" class="col-sm-2 control-label">Título</label>
<div class="col-sm-10">
{{input type="text" name="title" class="form-control" value=title}}
</div>
</div>
<div class="form-group">
<label for="content" class="col-sm-2 control-label">Conteúdo</label>
<div class="col-sm-10">
{{input type="text" name="content" class="form-control" value=content}}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button class="btn btn-primary" {{ action 'create' }}>Salvar</button>
</div>
</div>
</form>
app/templates/book/new.hbs
Controller
import Ember from 'ember';
export default Ember.Controller.extend({
title: '',
content: '',
actions: {
create: function(){
var title = this.get('title');
var content = this.get('content');
var _self = this;
Ember.$.ajax({
url: 'https://ember-api-workshop.herokuapp.com/books',
type: 'POST',
data: { book: { title: title, content: content, author_id: 1 } },
success: function() {
_self.transitionToRoute('book.index');
}
});
}
}
});
app/controllers/book/new.js
EDIT
Rota
Router.map(function() {
this.route('about');
// Adicionar as linhas abaixo
this.route('contact', { resetNamespace: true }, function() {
this.route('new');
});
this.route('book', { resetNamespace: true } ,function() {
this.route('new');
this.route('edit', { path: 'edit/:id' });
});
});
// Após alterar
// confira os logs novamente
app/router.js
Formulário
<h1>Editar Livro</h1>
<form>
<div class="form-group">
<label for="title" class="col-sm-2 control-label">Título</label>
<div class="col-sm-10">
{{input type="text" name="title" class="form-control" value=title}}
</div>
</div>
<div class="form-group">
<label for="content" class="col-sm-2 control-label">Conteúdo</label>
<div class="col-sm-10">
{{input type="text" name="content" class="form-control" value=content}}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button class="btn btn-primary" {{ action 'update' }}>Atualizar</button>
</div>
</div>
</form>
app/templates/book/edit.hbs
Route
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params, controller) {
var url = "http://ember-api-workshop.herokuapp.com/books/" + params.id;
var model;
Ember.$.ajax({
url: url,
async: false,
success: function(data) {
model = data.books;
}
});
return model;
},
setupController: function(controller, model) {
this._super(controller, model);
controller.set('title', model.title);
controller.set('content', model.content);
controller.set('id', model.id);
}
});
app/routes/book/edit.js
Controller
import Ember from 'ember';
export default Ember.Controller.extend({
title: '',
content: '',
actions: {
update: function(){
var title = this.get('title');
var content = this.get('content');
var _self = this;
Ember.$.ajax({
url: 'https://ember-api-workshop.herokuapp.com/books/' + _self.id,
type: 'POST',
data: { book: { title: title,
content: content,
author_id: 1 }, _method: 'PUT' },
success: function() {
_self.transitionToRoute('book.index');
}
});
}
}
});
app/controllers/book/edit.js
Exibir todos
Route
import Ember from 'ember';
export default Ember.Route.extend({
setupController: function(controller, model) {
var url = "http://ember-api-workshop.herokuapp.com/books/";
var book;
Ember.$.ajax({
url: url,
async: false,
success: function(data) {
book = data.books;
}
});
Ember.Logger.debug(book);
this._super(controller, model);
controller.set('book', book);
}
});
app/routes/book/index.js
Listagem
<table class="table">
<tr>
<th>Título</th>
<th>Conteúdo</th>
<th colspan="2"></th>
</tr>
<tr>
<td>{{ book.title }}</td>
<td>{{ book.content }}</td>
<td>{{#link-to 'book.edit' book.id }}Editar{{/link-to}}
</td>
</tr>
</table>
app/templates/book/index.hbs
Delete
Listagem
<table class="table">
<tr>
<th>Título</th>
<th>Conteúdo</th>
<th colspan="2"></th>
</tr>
<tr>
<td>{{ book.title }}</td>
<td>{{ book.content }}</td>
<td>{{#link-to 'book.edit' book.id }}Editar{{/link-to}}
<td>{{#link-to 'book.delete' book.id }}Deletar{{/link-to}}</td>
</tr>
</table>
app/templates/book/index.hbs
Rota
Router.map(function() {
this.route('about');
// Adicionar as linhas abaixo
this.route('contact', { resetNamespace: true }, function() {
this.route('new');
});
this.route('book', { resetNamespace: true } ,function() {
this.route('new');
this.route('edit', { path: 'edit/:id' });
this.route('delete', { path: 'delete/:id' });
});
});
app/router.js
Route
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params, controller) {
var url = "http://ember-api-workshop.herokuapp.com/books/" +
params.id;
var model;
Ember.$.ajax({
url: url,
async: false,
type: 'POST',
data: { _method: 'DELETE' }
});
this.transitionTo('book.index');
return model;
}
});
app/routes/book/delete.js
Exercício
- Seguindo a mesma lógico do CRUD de livros, faça o CRUD autores =D
Components
- Reaproveitamento de código é muito importante, lembre-se do DRY
- Os componentes são uma forma de reaproveitar nosso código
- Além de oferecer tratamento de eventos
Gerando um component
$ ember g component double-click
Component
import Ember from 'ember';
export default Ember.Component.extend({
doubleClick: function(){
alert("DoubleClick :) ");
}
});
app/components/double-click.js
Component template
{{yield}}
app/templates/components/double-click.hbs
Home do Site
{{#double-click}}
Clique aqui duas vezes
{{/double-click}}
app/templates/index.hbs
Components Hooks
- init
- didReceiveAttrs
- willRender
- didInsertElement
- didRender
- Entre outros
https://guides.emberjs.com/v2.7.0/components/the-component-lifecycle/#toc_formatting-component-attributes-with-code-didreceiveattrs-code
Obrigado!
Criando aplicações ambiciosas com Ember Js
By Vinícius Alonso
Criando aplicações ambiciosas com Ember Js
- 1,119