#Resumo Emberjs
- Site emberjs
- Ember cli
- Desinstalando um add-on
- Ember cli quickstart
- ES6 - Babel
- Ember-cli-deploy
- PhantomJs - Testes sem browser
- Esquema de funcionamento do ember
- Content security type
- Json api
- Ember fit any backend
- Customizando serializers
- Erros comuns com Emberjs
- Relacionamento com emberjs
- Curso de ember no Microsoft Virtual Academy
- Ember-table tabela
- Ember-table exemplos
- Adepar ember table
- The 8 most common ember js dev mistakes
Não vistos ainda:
- Airpair - Emberjs using ember cli
- Vídeo Emberjs NYC 2016
- Microsoft jun 2016
- Configurar Ember com arcgis
- Organizando código em pod
- Campos obrigatórios no formulário
- Mensagens flash ember-cli-flash
- Git flash ember-cli-flash
- Handlebars helpers addon
- yoember - Tutorial recente CRUD
- Conceitos Ember
- Passando parametro pelo transitionTo
- Should we use controllers ember 2.0
- Modern Javascript Frameworks - Introduction to Ember.JS and Ember-CLI
- Modern Javascript Frameworks - Introduction to Ember.JS and Ember-CLI
- Resetar scroll da página após transição. Voltar para o topo
- Modern Javascript Frameworks - Introduction to Ember.JS and Ember-CLI
- The ember way
- things-i-wish-someone-had-told-me-when-i-was-learning-ember
- Ember screencasts
- Animações e transições com LiquidFire
- Partial ember cli
- Integração de emberjs com spring
- Documentação ember serialização com JSONSerializer
- Datatable - tabelas
- Tabelas Emberjs
- Unslider - Ember slider de imagens
- ember-validations - Validação de formulário
- Ember-forms - criação de formulários (Depreciado)
- Ember-cp-validations - validação de formulários (muito prolixo)
- Ember-cp-validations - Documentação
- Validação de forms com pacote Validator do npm e bootstrap
- Mixins x herança
- ember-model-validator - validação parecida com rails
- Ember-cli scaffold parecido com rails
- Ember js cors credentials
npm install -g ember-cli
ember --help
ember help <command-name>
ember new nomeprojeto
|--app
|--bower_components
|--config
|--dist
|--node_modules
|--public
|--tests
|--tmp
|--vendor
bower.json
ember-cli-build.js
package.json
README.md
testem.js
ember generate template application
ou
ember g template application
Criará app/templates/application.hbs.
No application controller inclua:
import Ember from 'ember';
export default Ember.Controller.extend({
currentRouteName() {
return this.controllerFor('application').get('currentRouteName');
}
});
ember generate route lotes
ou
ember g route lotes
output:
installing route
create app/routes/scientists.js
create app/templates/scientists.hbs
updating router
add route scientists
installing route-test
create tests/unit/routes/scientists-test.js
Criará rota para http://localhost:4200/lotes.
Para criar rotas e templates no mesmo diretório use a opção --pod com abaixo. Veja o vídeo da ms virtual academy
server g route person --pod
ember g route index
server destroy route person
ou
server d route person
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return ['Marie Curie', 'Mae Jemison', 'Albert Hofmann'];
}
});
If you need to fetch data asynchronously, the model() method supports any library that uses JavaScript Promises.
Para retornar múltiplas promises use:
import Ember from 'ember';
const{
Route,
RSVP : { hash }, // mesmo que var RSVP = Ember.RSVP; var hash = RSVP.hash;
} = Ember;
export default Ember.Route.extend({
model() {
// GET to /zonas
let zonas = this.store.findAll('zona');
//É possível usar get do JQuery usando o nome da rota como abaixo:
//let zonas = $.get('zonas');
// GET to /eixos
let eixos = this.store.findAll('eixo');
return Ember.RSVP.hash({
zonas : zonas,
eixos : eixos
});
}
});
Um controlador para esta rota acima poderia utilizar alias para deixar o template mais descritivo:
import Ember from 'ember';
//destrutor
const {
Controller,
computed,
computed: { alias, union, mapBy, uniq, reduce },
get
} = Ember;
export default Controller.extend({
//Isso permite acessar os dados no template usando as variáveis users e employess.
users: computed.alias('model.users'),
employees: computed.alias('model.employess'),
allPeople: union('users', 'employees'),
// Ember.computed.mapBy(colecao, atributo)
userNames: mapBy('users', 'fullName'),
employeeNames:mapBy('employees', 'fullName'),
allUniqueNames: uniq('userNames', 'employeeNames'),
//Para cada user da coleção users, observe qualquer alteração na propriedade income e atualize <sumOfIncomes class=""></sumOfIncomes>
sumOfIncomes: computed('[email protected]', {
get(){
let users = get(this, 'users');
let incomes = users.mapBy('income');
return incomes.reduce((previous, current) => {
return parseFloat(previous) + parseFloat(current);
}, 0 ); // 0 é o valor default.
}
});
// se user fosse array simples como userTeste = ['user1','user2', 'user3'] usaria
// userNames: computed('userTest.[]',{
...
}),
employeesSubordinados: computed.filterBy('employees','tipo','subordinado');
});
app/templates/scientists.hbs
<h2>List of Scientists</h2>
<ul>
{{##each model as |scientist|}}
<li>{{scientist}}</li>
{{/each}}
</ul>
ember generate component people-list
Resultado:
PS D:\arquivos\repositorio\sisgeo> ember generate component people-list
installing component
create app\components\people-list.js
create app\templates\components\people-list.hbs
installing component-test
create tests\integration\components\people-list-test.js
Em app/templates/components/people-list.hbs:
<h2>{{title}}</h2>
<ul>
{{##each people as |person|}}
<li>{{person}}</li>
{{/each}}
</ul>
Componentes são como tags html, mas ao invés de usar usa-se {{componente}}, passando os atributos que ele utiliza. Exemplo:
Em app/templates/scientists.hbs
{{people-list title="List of Scientists" people=model}}
Observação:
{{component 'blog-post'}} é o mesmo que usar {{blog-post}}
Este comando agrupará todos seus arquivos e irá gerar uma versão para deploy em /dist.
ember build --env production
ember install ember-cli-deploy
If you deploy your application to an Apache web server, first create a new virtual host for the application. To make sure all routes are handled by index.html, add the following directive to the application's virtual host configuration FallbackResource index.html
ember server
Acesse o projeto em http://localhost:4200
Para rodar em modo desenvolvimento:
ember server -dev
Para rodar em modo produção:
ember server -prod
Configurado rotas para usar hashbang referência
Acesse projeto>config>environment.js e configure a variável locationType para:
var ENV = {
modulePrefix: 'sisgeo',
environment: environment,
rootURL: '/',
locationType: 'auto',
...
ember g template application
app/templates/application.hbs
Crie um template para página de erro 404:
ember g template 404
Inclua o conteúdo da página projeto/app/templates/404.hbs Depois, em projeto>app>router.js inclua: javascript
Router.map(function() {
this.route('restricoes', function() {
this.route('novo'); // restricao/novo
this.route('editar', {path: 'editar/:id'} ); // :id é a variavel passada pela url
this.route('detalhes', {path: 'id'}); // restricao/123
});
//outras rotas ...
this.route('404', { path: '/*wildcard' });
});
// projeto\app\restricao\detalhes\route.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params){
var id = params.id
return this.store.findRecord('restricao', id);
}
})
javascript
Para criar link para a raiz da aplicação inclua em uma view:
{{link-to 'Sisgeo' 'index' }}
{{ ##each model as |bug|}}
{{##link-to 'bugs.details' bug class="list-group-item" }}
{{bug.name}}
{{/link-to}}
# criando URL com parâmetros: http://example.com/articles?sort=asc
{{#link-to "posts" (query-params direction="asc")}}Sort{{/link-to}}
// Binding is also supported
# criando URL com parâmetros: http://example.com/articles?direction=otherDirection
{{#link-to "posts" (query-params direction=otherDirection)}}Sort{{/link-to}}
// app/controllers/articles.js
import Ember from 'ember';
const {
get,
computed
} = Ember
export default Ember.Controller.extend({
queryParams: ['category'],
category: null,
filteredArticles: computed('category', 'model', function() {
var category = get(this, 'category');
var articles = get(this, 'model');
if (category) {
return articles.filterBy('category', category);
} else {
return articles;
}
})
});
app/routes/some-route.js
this.transitionTo('post', object, { queryParams: { showDetails: true }});
this.transitionTo('posts', { queryParams: { sort: 'title' }});
// if you want to transition the query parameters without changing the route
this.transitionTo({ queryParams: { direction: 'asc' }});
//You can also add query params to URL transitions:
this.transitionTo('/posts/1?sort=date&showDetails=true');
Para realizar a transição completa com busca no servidor use refreshModel:true como abaixo no arquivo de rota:
app/routes/articles.js
import Ember from 'ember';
export default Ember.Route.extend({
queryParams: {
category: {
refreshModel: true
}
},
model(params) {
// This gets called upon entering 'articles' route
// for the first time, and we opt into refiring it upon
// query param changes by setting `refreshModel:true` above.
// params has format of { category: "someValueOrJustNull" },
// which we can forward to the server.
return this.get('store').query('article', params);
}
});
Para usar os parametros da url na rota faça como abaixo:
// app/router.js
...
this.route('item', { path: '/items/:item_id' });
...
// app/routes/item.js
export default Ember.Route.extend({
model(params) {
return {
id: params.item_id,
text: "This is item " + params.item_id
}
}
});
É possível também obter objetos json da rede usando Ember.$.getJSON:
export default Ember.Route.extend({
model() {
// obtem objetos de http://localhost:8080/zonas se o servidor ember
//for iniciado com --proxy http://localhost:8080. Retorna promise e é assíncrono.
return Ember.$.getJSON('/zonas');
}
});
ou ainda
export default Ember.Route.extend({
model(params) {
return this.store.findRecord('item', params.item_id);
}
});
Uma rota, como acima, carrega o 'model' e depois chamad o método setupController do controlador, que por padrão é:
export default Ember.Route.extend({
model() {
// returns a model
},
setupController(controller, model) {
controller.set('model', model);
}
})
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Sisgeo</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for "head"}}
<link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
<link rel="stylesheet" href="{{rootURL}}assets/sisgeo.css">
{{javascriptcontent-for "head-footer"}}
</head>
<body>
{{content-for "body"}}
<script src="{{rootURL}}assets/vendor.js"></script>
<script src="{{rootURL}}assets/sisgeo.js"></script>
{{content-for "body-footer"}}
</body>
</html>
Criando modelos referência
ember generate model person
Exemplo 1:
import DS from 'ember-data';
export default DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
fullName: Ember.computed('firstName', 'lastName', function() {
return `${this.get('firstName')} ${this.get('lastName')}`;
})
});
ember g adapter application
Inclua o seguinte conteúdo em projeto>app>adapters>application.js:
import DS from 'ember-data';
import ENV from '../config/environment';
var ApplicationAdapter = DS.RESTAdapter.extend({
host: ENV.APP.HOST_BACKEND
//, namespace: 'NOME_CONTEXTO_BACKEND'
});
export default ApplicationAdapter;
Configure em projeto>config>environment.js a variável HOST_BACKEND:
...
if (environment === 'development') {
ENV.APP.HOST_BACKEND = 'http://localhost:8080';
ENV.contentSecurityPolicy = {
'connect-src': "'self' 'localhost:8080'",
//'connect-src': "'self' *" ativa carregamento de requisições de qualquer lugar
};
}
if (environment === 'test') {
ENV.APP.HOST_BACKEND = 'http://teste.exemplo.com:8080';
ENV.contentSecurityPolicy = {
'connect-src': "'self' 'teste.exemplo.com:8080'",
};
}
if (environment === 'production') {
ENV.APP.HOST_BACKEND = 'http://www.exemplo.com:8080';
ENV.contentSecurityPolicy = {
'connect-src': "'self' 'www.exemplo.com:8080'",
};
}
...
Para permitir acesso ao backend no modo desenvolvimento use o comando:
ember server -dev --proxy http://localhost:8080
Após isso, as consultas usando this.get('storage').findAll() e outras serão feitas no host especificado.
JSON Legado vindo do backend ref
O serializer padrão do ember 2.7 é o JSONAPISerializer. O json legado não segue este padrão. Crie o serializer application com o comando abaixo:
ember g serializer application
Inclua no arquivo projeto>app>serializers>application.js o conteúdo abaixo para usar como serializer padrão o JSONSerializer:
import DS from 'ember-data';
export default DS.JSONSerializer.extend({
});
Exemplo de um modelo User representado em json legado:
//Modelo User
{
"id": 5,
"name": "David",
"skills": [
{
"id": 2,
"name": "Teaching",
"category": {
"id": 3,
"name": "Education"
}
},
{
"id": 9,
"name": "Ember",
"category": {
"id": 8,
"name": "Technology"
}
}
]
}
O modelo skill está embutido (embeded) no modelo User do json acima. Para indicar isso ao ember gere um serializer para o modelo User:
ember g serializer user
E inclua o seguinte:
// app/serializers/user.js
export default DS.JSONSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
skills: { embedded: 'always' }
}
});
Chamando ações na views:
Em projeto\app\bugs\template.hbs:
<button {{action 'save'}}>Save</button>
Executará o método save em projeto\app\bugs\new\route.js
import Ember from 'ember';
export default Ember.Route.extend({
model(){
return this.store.createRecord('bug');
},
actions:{
displayList: function(){
this.transitionTo('bus');
},
save: function(){
...
},
nameOfAnAction: function(){
...
}
}
})
this.transitionTo('rota')
- findRecord() - busca dados no servidor usando o id do modelo.
- peekRecord() - busca dados na sotorage local do navegador e não faz acesso ao servidor.
- findAll() - carrega todos os dados do modelo do servidor.
- peakAll() - carrega todos os dados do modelo localmente.
- query() - procura registro no servidor via igualdade. Exemplo:
store.query('bug', { filter { title: 'Demo' } })
- queryRecord() - procura um único registro via igualdade. Exemplo:
this.store.queryRecord('bug', {
filter: {
nome: 'bugginho'
}
}).then(function(bugginho) {
// do something with `bugginho`
});
<form {{ action "save" on="submit"}}>
<div>
<label for="">
{{input value=model.title class="form-control"}}
</label>
<div class="form-group">
<button {{action "save" model class="btn btn-default"}}>
Salvar
</button>
<button {{action "save" model class="btn btn-default"}}>
Cancelar
</button>
</div>
</div>
</form>
Visualize seu dado. O comando log objeto irá mostrar o objeto no console do navegador. No chrome, é possível clicar ao lado do objeto mostrado e aramazená-lo como variável global para ser utilizado com Ember.inspect():
{{ log model }}
Mostrar dados no console:
Ember.inspect()
Forçar uma parada:
{{debugger}}
Input e checkboxes funcionam com vinculação:
{{input}}
{{input value=bug.title}}
Text area:
textarea value=bug.description
Select pode ser usado com html puro ou com add on emberx-select:
{{##x-select value=model.status }}
{{##x-option value=1}}Active{{/x-option}}
{{##x-option value=2}}Active{{/x-option}}
{{/x-select}}
Links:
{{link-to 'nome link' 'route' paramsToPassToRoute}}
Loop através de coleções:
{{##each model as |bug| }}
{{input value=bug.title}}
<p>title: {{bug.title}}</p>
{{else}}
Nenhum bug encontrado
{{/each}}
- Precisam estar na pasta de componentes.
- Precisam ter nome com -. Ex: component-bug-details.
Chamada de componente:
{{##bug-details bug=item}}
{{/bug-details}}
ou com positional parameter indicando que o primeiro argumento é sempre bug:
{{##bug-details model handleRedirect='displayList'}}
<h3>Create bug!</h3> //Será mostrado onde tiver {{yield}} no template deste componente
{{/bug-details}}
Template do componente:
{{yield}}
<div class="form-group">{{input value=bug.tiel class="form-control"}}
</div>
<button class="btn btn-success" {{action 'save' bug}}>save</button>
Criando um componente:
import Ember from 'ember';
const BugDetailsComponent = Ember.Component.extend({
actions: {
save: function(model){
var component = this;
model.save().then(function{
console.log('Model saved'));
//chama ação no chamador do componente
component.sendAction('handleRedirect');
});
}
}
});
BugDetailsComponent.reopenClass({
positionalParams: ['bug']
});
export default BugDetailsComponent;
Lista de manipuladores de eventos que podem ser usados no .js do componente:
Touch events:
touchStart
touchMove
touchEnd
touchCancel
Keyboard events
keyDown
keyUp
keyPress
Mouse events
mouseDown
mouseUp
contextMenu
click
doubleClick
mouseMove
focusIn
focusOut
mouseEnter
mouseLeave
Form events:
submit
change
focusIn
focusOut
input
HTML5 drag and drop events:
dragStart
drag
dragEnter
dragLeave
dragOver
dragEnd
drop
É possível adicionar ou calcular propriedades dentro do compomente no hook didReceiveAttrs():
import Ember from 'ember';
let TabelaModeloComponent = Ember.Component.extend({
didReceiveAttrs() {
this._super(...arguments);
this.set('customMessages', Ember.Object.create({
"searchLabel": "Procurar:",
"searchPlaceholder": "em todas as colunas",
"columns-title": "Colunas",
"columns-showAll": "Mostrar Todas",
"columns-hideAll": "Esconder Todas",
"columns-restoreDefaults": "Restaurar Padrão",
"tableSummary": "Mostrando %@ - %@ de %@",
"allColumnsAreHidden": "Todas as colunas estão escondidas. Use o dropdown <strong>colunas</strong> para mostrar algumas delas",
"noDataToShow": "Nenhum restrição para mostrar"
}));
this.set('columnsAreUpdateable', true);
this.set('useFilteringByColumns', false);
this.set('filteringIgnoreCase', true);
this.set('pageSize', 10);
}
});
export default TabelaModeloComponent;
ember help build // para ajuda
ember build -prod // minifica o javascript e css
ember build -dev // NÃO minifica o javascript e css
Para fazer o deploy, copie todo o conteúdo da pasta projeto>dist para o host onde ficará o website.
Para mapear rotas do tipo www.exemplo.com/bugs no IIS é necessário incluir na pasta projeto>public um arquivo web.config para tratar esses casos.Veja o vídeo
É necessário configurar o Apache também para esses casos.
Procure em projeto>dist
Incluir a configuração de pluralização no app.js.
import DS from 'ember-data';
var inflector = Ember.Inflector.inflector;
inflector.irregular('restricao', 'restricoes');
export default DS.Model.extend({
objectid: DS.attr('number'),
valor: DS.attr('number'),
fonteDoValor: DS.attr('string'),
unidadeValor: DS.attr('string'),
outorga: DS.attr('string'),
criadoPor: DS.attr('string'),
alteradoPor: DS.attr('string'),
quandoCriou: DS.attr('string'),
quandoAlterou: DS.attr('string'),
quandoInicia: DS.attr('string'),
quandoTermina: DS.attr('string'),
lei: DS.attr('string'),
regiao: DS.attr('string'),
idRegiao: DS.attr('number')
});
You can pass both the tournament and the player to the action:
var Player = DS.Model.extend({
name: DS.attr('string'),
tournaments: DS.hasMany('tournaments', {async: true}),
});
var Tournament = DS.Model.extend({
title: DS.attr('string'),
players: DS.hasMany('player', {async: true}),
});
{{#each player in players}}
<a class="list-group-item" href="#" {{action 'removePlayer' player tournament}}>
{{player.name}}
</a>
{{/each}}
And then in your controller implement an action which takes multiple parameters:
actions: {
removePlayer: function(player, tournament) {
tournament.get('players').removeObject(player);
// you can save the tournament and player here, unless you are handling that as part of another action
}
}
(Referência)[https://emberigniter.com/5-essential-ember-2.0-concepts/]
export default Ember.Route.extend({
crypto: Ember.inject.service()
...
})
Irá olhar em app/services/crypto.js e torná-lo disponível como this.get('crypto'). Ember.inject.service('crypto') injeta o serviço na propriedade. No trecho acima o foi usado apenas Ember.inject.service(), sem parâmetro. Neste caso, ember utilizará o nome da propriedade á qual o serviço será injetado para obter o serviço.
ember g mixin resetScroll
// app/mixins/reset-scroll.js
import Ember from 'ember';
export default Ember.Mixin.create({
activate: function() {
this._super();
window.scrollTo(0,0);
}
});
Para cada rota que deseja rolar a página para cima extenda do mixin criado.
// app/restricoes/detalhar/route.js
import Ember from 'ember';
import ResetScroll from 'NOME_APP/mixins/reset-scroll';
export default Ember.Route.extend(ResetScroll, {
model(params){
...
}
});
Ao realizar um objeto.save(), ember não inclui no payload o id. O id deve ser obtido pela url. É necessário tratar isso no bakckend para evitar que um novo registro seja criado ou configurar o ember conforme a documentação para serializar o id.
Os helpers mais usados para forms são:
- {{input}} - Para campo de texto ou checkbox
- {{textarea}} - Para textarea
{{input value="http://www.facebook.com"}}
Vai gerar:
<input type="text" value="http://www.facebook.com"/>
Se incluir value="valor" como aspas, o valor será mostrado no campo. Se incluir value=valor sem aspas, o campo será vinculado à variável valor do contexto corrente da view.
{{input type="text" value=firstName disabled=entryNotAllowed size="50"
Ações podem ser vinculadas a eventos como segue. Os nomes dos eventos devem ser utilizados com traços (dasherized names):
{{input value=firstName key-press="updateFirstName"}}
Checkboxes:
{{input type="checkbox" name="isAdmin" checked=isAdmin}}
Text area:
{{textarea value=name cols="80" rows="6"}}
Seguindo o formato do Rails:
GET /photos photos#index display a list of all photos
GET /photos/new photos#new return an HTML form for creating a new photo
POST /photos photos#create create a new photo
GET /photos/:id photos#show display a specific photo
GET /photos/:id/edit photos#edit return an HTML form for editing a photo
PATCH/PUT /photos/:id photos#update update a specific photo
DELETE /photos/:id photos#destroy delete a specific photo
this.route('eixos', function () {
this.route('listar');
this.route('novo', { path: '/:id/novo' });
this.route('detalhar', { path: '/:id' });
this.route('editar', { path: '/:id/editar' });
});
Para configurar o uso de pods por padrão, abra o arquivo .ember-cli
"usePods": true
É possível criar apelidos usando ES6 da seguinte forma:
import DS from 'ember-data';
const {
Model,
attr
} = DS;
export default Model.extend({
title: attr('string'),
author: attr('string')
});
Dentro de uma rota é possível alterar o nome do modelo para permitir acesso no template através de outro nome que não seja model.
import Ember from 'ember';
const { Route, set } = Ember;
export default Route.extend({
setupController(controller, model){
//Seta propriedades do controlador. O controlador não precisa ser gerado explicitamente fazendo desta forma.
set(controller, 'post', model);
set(controller, 'editable', true)
},
actions:{
goBackToIndex(){
this.transitionTo('blog.index');
},
toggleEdit(){
this.controller.toggleProperty('editable');
}
}
})
No template é possível usar como abaixo:
{{#if editable}}
<form>
<label for="title">Edit Title:</label>
<div {{action "something" on="keyUp"}}>
{{input type="text" value=post.title }}
</div>
</form>
{{/if}}
<button {{action "toggleEdit"}}>{{if editable 'Desabilitar' 'Habilitar'}}}</button>
// config/environment.js
var ENV = {
modulePrefix: 'pod-example',
podModulePrefix: 'pod-example/pods
...
},
Ember Best Practices: Route Actions
ember install ember-route-action-helper
import DS from 'ember-data';
import Ember from 'ember';
const {
Model,
attr
}= DS;
const {
computed :
computed : {gte},
get
} = Ember;
//Para criar seu próprio tipo de computed.
const fullNameConcat = function(firstName, lastName){
return computed(firstName, lastName, {
get(){
return `${get(this,firstName)} ${get(this, lastName)}`;
}
});
};
export default Model.extend({
firstName: attr('string'),
lastName: attr('string'),
income: attr('number'),
contribution: attr('number'),
fullName: fullNameConcat('firstName', 'lastName');
//Os parametros firsName e lastName abaixo, indicam observers. Ou seja,
//quando um destes atributos mudarem, fullname será atualizado.
// Forma nova de fazer
// Usa o get implícito com esta função anônima
fullName: computed('firstName','lastName', function(){
//note a template string entre ``
return `${get(this, 'firstName')} ${get(this, 'lasName')}`;
}),
//mesmo que:
fullName: computed('firstName','lastName', {
get(){
return `${get(this, 'firstName')} ${get(this, 'lasName')}`;
}
}),
//Forma antiga de fazer isso.
fullName: computed(function(){
return `${get(this, 'firstName')} ${get(this, 'lasName')}`;
}).property('firstName','lastName'),
contributionPercent('income', 'contribution', function(){
let contribution = get(this, 'contribution');
let income = get(this, 'income');
return (contribution / income * 100).toFixed(2); // com 2 casas decimais.
}),
contributionPercent('income', 'contribution', {
get(){
let contribution = get(this, 'contribution');
let income = get(this, 'income');
return (contribution / income * 100).toFixed(2); // com 2 casas decimais.
},
set(key, value){ //atualiza um atributo no model
let newContribution = value / 100 * get(this,'income');
set(this,'contribution', newContribution);
return value;
}
}),
//greater than or equal.
contributeOverFive: computed.gte('contributionPercent', 5),
//
});
Veja mais métodos de computed na api.
const {
run
}
...
run.later( () => {
//código que executará de 5 em 5 segundos. Igual a setTimeout()
}, 5000); // 5 segundos
...
ember install ember-simple-auth
Indique no arquivo config/environment.js qual template é o de login:
// config/environment.js
ENV['ember-simple-auth'] = {
baseURL: 'login'
};
Em app/controllers/application.js ou app/pods/application/controller.js
import Ember from 'ember';
export default Ember.Controller.extend({
session: Ember.inject.service('session'),
actions:{
logout() {
this.get('session').invalidate();
}
}
});
Em app/routes/application.js ou app/pods/application/route.js:
import Ember from 'ember';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin);
Em app/controllers/login.js ou app/pods/login/controller.js
import Ember from 'ember';
export default Ember.Controller.extend({
session: Ember.inject.service('session'),
actions: {
autenticar(){
let { usuario, senha } = this.getProperties('usuario', 'senha');
this.get('session').authenticate('authenticator:oauth2', usuario, senha).catch((reason) => {
this.set('erros', reason.error);
};
}
}
});
Crie o diretório app/authenticators/oauth2.js ou app/pods/authenticators/oauth2.js
import OAuth2PasswordGrant from 'ember-simple-auth/authenticators/oauth2-password-grant';
export default OAuth2PasswordGrant.extend();
Na rota que deve exigir senha utilize o AuthenticatedRouteMixin como abaixo:
import Ember from 'ember';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin,{
....
});
Em app/login/template.hbs inclua o form:
<div id="login" class="center-block" {{action 'authenticate' on='submit'}} >
<form class="form-signin">
<h2 class="form-signin-heading">Área restrita:</h2>
<label for="usuario" class="sr-only">Usuário</label>
<input type="email" id="usuario" class="form-control" placeholder="Usuário" required="" autofocus=""><br>
<label for="senha" class="sr-only">Senha</label>
<input type="password" id="senha" class="form-control" placeholder="Senha" required=""><br>
<button class="btn btn-lg btn-primary btn-block" type="submit">Acessar</button>
</form>
</div>
Para incluir verificação para todas as requisições use:
// app/adapters/application.js
import RESTAdapter from 'ember-data/adapters/rest';
import ENV from '../config/environment';
import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin';
var ApplicationAdapter = RESTAdapter.extend(DataAdapterMixin, {
authorizer: 'authorizer:oauth2',
host: ENV.APP.HOST_BACKEND
//, namespace: 'api/v1',
});
export default ApplicationAdapter;
- Ember.js snippets
Para instalar o addon:
ember install ember-select-2
ember install ember-wormhole
<select onchange={{action (mut vehicle) value="target.value"}}>
{{#each vehicles as |vehicleChoice|}}
<option value={{vehicleChoice}} selected={{eq vehicle vehicleChoice}}>{{vehicleChoice}}</option>
{{/each}}
</select>
import Ember from 'ember';
export default Ember.Controller.extend({
vehicle: null,
vehicles: Ember.String.w('Tesla Chrysler Toyota'),
actions: {
selectVehicle(vehicle) {
this.set('vehicle', vehicle);
}
}
});
App = Ember.Application.create();
App.Router.map(function() {
// put your routes here
});
App.IndexRoute = Ember.Route.extend({
model: function() {
return {
name: 'one',
className: 'dropdown',
choices: [
{ choice: 'Choose One' },
{ choice: 'First' },
{ choice: 'Last' }
]
};
}
});
App.FauxSelectComponent = Ember.Component.extend({
selected: 'Choose One',
change: function(e){
this.set('selected', e.target.value);
}
});
{{#power-select
renderInPlace=true
selected=testadaMinimaRestricao.fonteDoValor
options=fonteDoValorArray
destination="fonteDoValorTestadaMinima"
onchange=(action (mut testadaMinimaRestricao.fonteDoValor)) as |fonteDoValor|}}
{{fonteDoValor}}
{{/power-select}}
<div id="fonteDoValorTestadaMinima">
</div>
<ul class="nav navbar-nav navbar-right">
{{#link-to 'index' tagName="li"}}<a href="#"><i class="fa fa-home"></i><span>Início</span></a>{{/link-to}}
{{#link-to 'zonas' tagName="li"}}<a href="#"><i class="fa fa-gear"></i><span>Zonas de Uso do Solo</span></a>{{/link-to}}
{{#link-to 'eixos' tagName="li"}}<a href="#"><i class="fa fa-book"></i><span>Eixos Comerciais</span></a>{{/link-to}}
{{#link-to 'restricoes' tagName="li"}}<a href="#"><i class="fa fa-book"></i><span>Restrições de Construção</span></a>{{/link-to}}
{{#link-to 'lotes' tagName="li"}}<a href="#"><i class="fa fa-money"></i><span>Lotes</span></a>{{/link-to}}
{{#link-to 'index' tagName="li"}}<a href="#"><i class="fa fa-book"></i><span>Imóveis</span></a>{{/link-to}}
{{#link-to 'index' tagName="li"}}<a href="#"><i class="fa fa-power-off"></i><span>Sair</span></a>{{/link-to}}
</ul>
No arquivo ember-cli-build.js, inclua as linhas que deseja importar:
// Lines to add
app.import("bower_components/jquerypic/js/jquerypic.js");
app.import("bower_components/jquerypic/styles/app.css");
return app.toTree();
};
No terminal:
mkdir app/instance-initializers && touch app/instance-initializers/global.js
Cole o conteúdo:
// app/instance-initializers/global.js
export function initialize(application) {
application.store = application.__container__.lookup('service:store'); //App.store disponível no console
window.App = application; // or window.Whatever
}
export default {
name: 'global',
initialize: initialize
};
Vídeo referência - Rene Rubalcava
Entre no diretório do projeto onde deseja usar o arcgis api e instale:
ember install ember-cli-amd
Abra o arquivo ember-cli--build.js:
var app= new EmberApp(defaults, {
amd:{
loader: 'https://js.arcgis.com/4.0/',
configPath: 'config/dojo-config.js',
packages:[
'esri', 'dojo', 'dojox', 'dijit',
'put-selector', 'xstyle', 'dgrid'
]
}
});
Criar arquivo em projeto/config/dojo-config.js com o conteúdo (E NÃO em projeto/app/config/dojo-config.js):
window.dojoConfig = {
async: true
};
Para gerar um servico de map use:
ember g service map
Acesse app/services/map.js e coloque o código exemplo abaixo:
import Ember from 'ember';
import Map from 'esri/Map';
import VectorTileLayer from 'esri/layers/VectorTileLayer';
export default Ember.Service.extend({
map: null,
loadMap(){
let map = this.get('map');
if(map){
return map;
}else{
let tileLayer = new VectorTileLayer({
url: "https://www.arcgis.com/apps/Embed/index.html?webmap=432a8d7ca22d4b5b859e0bdaa30ae118&legend=true&details=true"
});
map = new Map({
//layers: [tileLayer]
basemap: "streets"
});
this.set('map', map);
return map;
}
}
});
Crie um componente para usar o mapa:
ember g component esri-map
Inclua o conteúdo:
import Ember from 'ember';
import MapView from 'esri/views/MapView';
export default Ember.Component.extend({
classNames:['viewDiv'],
mapService: Ember.inject.service('map'),
didInsertElement(){
let map = this.get('map');
if(!map){
map = this.get('mapService').loadMap();
this.set('map',map);
}
},
createMap: function(){
let map = this.get('map');
let view = new MapView({
map:map,
container: this.elementId,
center: [-100.33, 25.69],
zoom: 10
});
view.then(x=> this.set('view', x));
}.observes('map')
});
Em app/styles/app.css inclua:
@import url('https://js.arcgis.com/4.0/esri/css/main.css');
Em um template inclua o componente criado: {{esri-map}}
Moment.js para ember-cli [Ember docs](https://guides.emberjs.com/v1.10.0/cookbook/user_interface_and_interaction/displaying_formatted_dates_with_moment_js/ Exemplo Code Pen Bootrap Cli datepicker
ember install ember-moment
import DS from 'ember-data';
import Ember from 'ember';
const {
computed
} = Ember;
export default DS.Model.extend({
quandoTermina: DS.attr('date'),
quandoTerminaFormatado: computed('quandoTermina', function () {
let quandoTermina = this.get('quandoTermina');
return moment(quandoTermina).format('DD/MM/YYYY');;
})
})
Para usar o date picker nos templates:
ember install ember-cli-bootstrap-datepicker
No arquivo .ember-cli-build.js inclua importação da tradução do calendário para a língua portuguesa:
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
var app = new EmberApp(defaults);
app.import('bower_components/bootstrap-datepicker/js/locales/bootstrap-datepicker.pt-BR.js');
return app.toTree();
};
No template:
{{bootstrap-datepicker value=expiresAt language="pt-BR"}}
(StackOverflow)[http://stackoverflow.com/questions/18018502/how-to-link-to-nested-resources-in-ember-js]
Em app/tabelas/novo/route.js:
import Ember from 'ember';
const { set } = Ember;
export default Ember.Route.extend({
model(){
let novaTabela = this.store.createRecord('tabela');
return novaTabela;
},
setupController(controller, model){
set(controller, 'tabela', model);
set(controller, 'desabilitarEdicaoTabela', false);
},
actions:{
criarTabela(tabela){
tabela.save()
.then((tabela)=>{
this.get('flashMessages').success('Tabela criada com sucesso.');
this.transitionTo('tabelas.restricoes', tabela.id);
},
(erro)=>{
this.get('flashMessages').danger('Erro: não foi criar a tabela.');
});
},
cancelar(tabela){
tabela.unloadRecord();
this.transitionTo('tabelas');
}
}
});
No template, verifique se o registro é novo com o atributo isNew como segue:
Se o registro não for novo, deve ser listado. Se for novo não deve ser listado porque ao criar um registro o usuário irá para a página de detalhes de registro e não para a listagem. Porém, se o usuário começar a criar o registro e cancelar ou apertar o voltar no navegador, a listagem deverá mostrar apenas os registros 'antigos'.
import DS from 'ember-data';
export default DS.JSONSerializer.extend({
serialize: function(record, options) {
options = options || {includeId: true};
return this._super(record, options);
}
});
{{#each-in objeto as |key value|}}
{{key}}:{{value}}
{{/each-in}}
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
serializeBelongsTo: function(record, json, relationship) {
this._super(record, json, relationship);
var key = relationship.key;
var json_key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key;
json[json_key] = parseInt(json[json_key]);
},
serializeHasMany: function(record, json, relationship) {
this._super(record, json, relationship);
var key = relationship.key;
var json_key = this.keyForRelationship ? this.keyForRelationship(key, "hasMany") : key;
if (json_key in json) {
json[json_key] = json[json_key].map((item) => {
parseInt(json[json_key]);
});
}
}
});
Serializar todos os ids como tipo inteiro (Documetação):
import DS from 'ember-data';
export default DS.JSONSerializer.extend({
serialize: function(snapshot, options) {
var json = this._super.apply(this, arguments);
if(!(typeof json.id === "undefined")){
json.id = parseInt(json.id);
}
return json;
}
});
Em app>pods>application>route.js:
import Ember from 'ember';
export default Ember.Route.extend({
...
actions: {
loading(transition, originRoute) {
let controller = this.controllerFor('application');
controller.set('carregando', true);
transition.promise.finally(function() {
controller.set('carregando', false);
});
},
error(error, transition) {
let controller = this.controllerFor('application');
if (error) {
if (error.status === 0) {
error.mensagem ='Desculpe, não foi possível obter os dados do servidor. Este problema pode ser resultado de falta de conexão com a internet. Você pode tentar atualizar a página.';
} else if (error.status == 403) {
error.mensagem ='Acesso proibido. Você não possui autorização para acessar esta página.';
} else if (error.status == 401) {
error.mensagem ='Acesso proibido. Necessário realizar login.';
} else if (error.status == 404) {
error.mensagem ='Página não encontrada.';
} else {
error.mensagem = 'Erro ao tentar carregar a página.';
}
controller.set('erro', error);
}
},
voltarParaInicio(){
let controller = this.controllerFor('application');
controller.set('erro', null);
this.transitionTo('index');
}
}
});
Crie o controller para application para evitar erro em this.controllerFor('application') na rota:
ember g controller application
Crie os templates para as páginas de carregamento e de erro:
ember g template loading
ember g template error
No template loading, inclua:
<span class="glyphicon glyphicon-refresh glyphicon-refresh-animate"></span> Carregando...
Pode-se usar o fa fa-spinner também caso esteja usando font-awesome (Referência):
<i class="fa fa-spinner fa-spin" style="font-size:24px"></i>
Em app.css inclua o seguinte para animar o ícone:
.glyphicon-refresh-animate {
-animation: spin .7s infinite linear;
-webkit-animation: spin2 .7s infinite linear;
}
@-webkit-keyframes spin2 {
from { -webkit-transform: rotate(0deg);}
to { -webkit-transform: rotate(360deg);}
}
@keyframes spin {
from { transform: scale(1) rotate(0deg);}
to { transform: scale(1) rotate(360deg);}
}
Em app>pods>application>template.hbs inclua:
É necessário adicionar rota para a página de erro em route.js:
this.route('error', {path:'error'});
store.push(store.normalize('user', { userName: 'myUserName', firstName: 'Dan', lastName: 'LastMe' .....}));
Instalar validator do npm:
npm --save-dev install ember-browserify
npm install --save validator
//app/pods/components/form-book/component.js
import Ember from 'ember';
import DS from 'ember-data';
import Validator from 'npm:validator';
export default Ember.Component.extemd({
errors: DS.Errors.create(),
buttonLabel: function(){
return (this.get('book').id)? 'Salvar':'Criar';
}.property(),
actions:{
submit: function(){
if(this.validate()){
this.sendAction('action', this.get('book'));
}
}
}
validate:function(){
this.set('errors', DS.Errors.create());
if( this.get('book.title') === '' || this.get('book.title') === undefined){
this.get('errors').add('title', 'Não pode ser vazio');
}
return this.get('errors.isEmpty');
}
})
//app/pods/book/edit/route.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params){
return this.store.findRecord('book', params.book_id);
},
setupController: function(controller, model){
controller.set('book', model);
},
actions: {
updateBook: function(book){
var _this = this;
book.save();
}
}
})
- Addons: liquid-fire
- Autenticação
- Templates aninhados. Post>comentários.
- Uso de ember-cli-mirage
- uso de ember-web-api add-on para backend em dotnet com pascalcasenames
- Exemplos de Fixtures
- Ember-table