Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[GDG Campinas 2016] Construindo uma arquitetura...

[GDG Campinas 2016] Construindo uma arquitetura modularizada com Angular 1

Aprenda a escrever (ou refatorar) suas aplicações Angular 1 de maneira modularizada e manter a sanidade mental ao mesmo tempo.

Avatar for Talysson de Oliveira Cassiano

Talysson de Oliveira Cassiano

November 26, 2016
Tweet

More Decks by Talysson de Oliveira Cassiano

Other Decks in Programming

Transcript

  1. Sim

  2. Arquitetura ➔ Sem $scope; ➔ Baseada em componentes/diretivas; ➔ Roteamento

    de componentes; ➔ Utilizar JavaScript moderno; ➔ Lógica e fetching em services/factories; ➔ Fluxo único de dados; ➔ Organização por conceitos.
  3. Eliminando o $scope ➔ O $scope não deve ser usado

    como se fosse o controller; ➔ Prejudica o isolamento; ➔ Efeito de mudanças difícil de prever; ➔ Passagem explícita de dados em vez de usar herança de $scope.
  4. angular.module('blogApp', []) .controller('BlogCtrl', function($scope, $http) { $http.get('/api/posts') .then(function(posts) { $scope.posts

    = posts; }); }); <div ng-app="blogApp"> <div ng-controller="BlogCtrl"> <ul> <li ng-repeat="post in posts"> {{ post.title }} </li> </ul> </div> </div> Antes Eliminando o $scope
  5. angular.module('blogApp', []) .controller('BlogCtrl', function($http) { var ctrl = this; $http.get('/api/posts')

    .then(function(posts) { ctrl.posts = posts; }); }); <div ng-app="blogApp"> <div ng-controller="BlogCtrl as ctrl"> <ul> <li ng-repeat="post in ctrl.posts"> {{ post.title }} </li> </ul> </div> </div> Depois Eliminando o $scope
  6. angular.module('blogApp', []) .controller('BlogCtrl', function($http) { var ctrl = this; $http.get('/api/posts')

    .then(function(posts) { ctrl.posts = posts; }); }); <div ng-app="blogApp"> <div ng-controller="BlogCtrl as ctrl"> <ul> <li ng-repeat="post in ctrl.posts"> {{ post.title }} </li> </ul> </div> </div> Antes Tudo é um componente
  7. angular.module('blogApp', []) .directive('postsList', function() { return { restrict: 'E', scope:

    {}, controllerAs: '$ctrl', controller: function PostsList($http) { var $ctrl = this; $http.get('/api/posts') .then(function(posts) { $ctrl.posts = posts; }); }, template: '...' }; }); angular.module('blogApp', []) .component('postsList', { controller: function PostsList($http) { var $ctrl = this; $http.get('/api/posts') .then(function(posts) { $ctrl.posts = posts; }); }, template: '...' }); < 1.5 1.5+ Tudo é um componente
  8. angular.module('blogApp', []) .directive('postTitle', function() { return { restrict: 'E', scope:

    {}, bindToController: { post: '=' }, controllerAs: '$ctrl', controller: function PostTitle() {}, template: '...' }; }); angular.module('blogApp', []) .component('postTitle', bindings: { post: '=' }, template: '...' }); < 1.5 1.5+ Tudo é um componente
  9. Rotas para componentes ➔ Até páginas são componentes; ➔ Centraliza

    controller e template de rotas; ➔ Melhor manutenibilidade das rotas; ➔ Ajuda a prevenir reuso de controllers; ➔ Utilizar o ui-router.
  10. import uiRouter from 'angular-ui-router'; angular.module('blogApp', [uiRouter]) .config(($stateProvider) => { $stateProvider

    .state('posts', { url: '/posts', controller: 'PostsController', controllerAs: '$ctrl', template: '...' }); }); Antes Rotas para componentes
  11. import uiRouter from 'angular-ui-router'; angular.module('blogApp', [uiRouter]) .config(($stateProvider) => { $stateProvider

    .state('posts', { url: '/posts', template: '<posts-list></posts-list>' }); }); import uiRouter from 'angular-ui-router'; angular.module('blogApp', [uiRouter]) .config(($stateProvider) => { $stateProvider .state('posts', { url: '/posts', component: 'postsList' }); }); ui-router < 1.0 ui-router 1.0+ Depois Rotas para componentes
  12. JavaScript moderno ➔ Classes e métodos em vez de funções

    e closures; ➔ Arrow-functions em vez de salvar o this em uma variável; ➔ npm em vez de Bower; ➔ Loaders (Webpack) ou transforms (Browserify) em vez de escrever o template em uma string.
  13. import template from './postsList.html'; angular.module('blogApp', []) .component('postsList', { controller: class

    PostsList { constructor($http) { $http.get('/api/posts') .then((posts) => { this.posts = posts; }); } }, template: template }); angular.module('blogApp', []) .component('postsList', { controller: function PostsList($http) { var $ctrl = this; $http.get('/api/posts') .then(function(posts) { $ctrl.posts = posts; }); }, template: '...' }); Antes Depois JavaScript moderno
  14. Dados, lógica e regras de negócio ➔ Lógica e consultas

    isolados em factories; ➔ Criação de models para data-fetching; ➔ Angular Restmod.
  15. Dados, lógica e regras de negócio import template from './postsList.html';

    angular.module('blogApp', []) .component('postsList', { controller: class PostsList { constructor($http) { $http.get('/api/posts') .then((posts) => { this.posts = posts; }); } }, template: template }); Antes
  16. Dados, lógica e regras de negócio import template from './postsList.html';

    angular.module('blogApp', []) .component('postsList', { controller: class PostsList { constructor(Post) { Post.findAll() .then((posts) => { this.posts = posts; }); } }, template: template }); angular.module('blogApp') .factory('Post', function($http) { return { findAll() { return $http.get('/api/posts'); } }; }); Depois
  17. Dados, lógica e regras de negócio angular.module('blogApp') .factory('Post', function($http) {

    return { findAll() { return $http.get('/api/posts'); } }; }); angular.module('blogApp') .factory('Post', function($http) { class Post { static findAll() { return $http .get('/api/posts') .then((posts) => { return posts.map((p) => { return new Post(p); }); }); } } return Post; }); Depois Antes
  18. Dados, lógica e regras de negócio angular.module('blogApp') .factory('Post', function($http) {

    class Post { static findAll() { return $http .get('/api/posts') .then((posts) => { return posts.map((p) => { return new Post(p); }); }); } } return Post; }); angular.module('blogApp') .factory('Post', function(restmod) { return restmod.model('/api/posts'); }); Depois Antes Angular Restmod
  19. Fluxo único de dados ➔ Melhora a previsibilidade das mudanças;

    ➔ Favorece imutabilidade; ➔ Maior escalabilidade do front-end; ➔ Fácil de encontrar bugs; ➔ One-way data binding entre componentes; ➔ Data down, callbacks/events up.
  20. Fluxo único de dados import template from './postTitle.html' angular.module('blogApp', [])

    .component('postTitle', bindings: { post: '=' }, controller: class PostTitle { renamePost(name) { this.post.name = name; } }, template: template }); Antes
  21. Fluxo único de dados angular.module('blogApp', []) .component('postTitle', bindings: { post:

    '<', onRenamePost: '&' }, controller: class PostTitle { renamePost(name) { this.onRenamePost({ param1: this.post, param2: name }); } }, template: ... }); Depois
  22. Fluxo único de dados <ul> <li ng-repeat="post in $ctrl.posts"> <post-title

    post="post" on-rename-post="$ctrl.renamePost(param1, param2)" ></post-title> </li> </ul> Depois angular.module('blogApp', []) .component('postsList', { controller: class PostsList { constructor(Post) { ... } renamePost(post, name) { this.posts .$find(post.id) .$update({ title: name }); } }, template: ... });
  23. Fluxo único de dados ➔ Usar uma modelagem mais robusta

    em aplicações maiores; ➔ ng-redux; ➔ flux-angular; ➔ Solução própria.
  24. Fluxo único de dados angular.module('blogApp', []) .factory('dispatcher', ($rootScope) => {

    return { on(...args) { $rootScope.$on(...args); }, emit(...args) { $rootScope.$emit(...args); } }; }); Dispatcher próprio
  25. Organização por conceitos ➔ A organização tradicional não modulariza corretamente

    por conceitos; ➔ O codebase não escala bem; ➔ Confusão na hora de decidir onde colocar um certo arquivo.
  26. Organização por conceitos /app /posts /postsList /postsTitle /Post /comments /commentContent

    /commentsService /Comment /notifications /notificationsService Depois
  27. Performance ➔ Diminuir tempo do $digest; ➔ Evitar filtros na

    view; ➔ Deixar ng-repeat mais rápido; ➔ Remover checagens desnecessárias; ➔ Usar ng-model-options quando possível.
  28. Diminuir tempo do $digest ➔ $digest é o loop de

    checagens de alterações; ➔ Quanto mais rápido, menor o tempo de atualização da view; ➔ Remover reprocessamentos da view.
  29. Diminuir tempo do $digest angular.module('blogApp', []) .component('postsList', { controller: class

    PostsList { constructor(Post) { ... } postsFor(category) { return this.posts .filter((p) => { return p.category === category; }); } } }); Antes <ul> <li ng-repeat="post in $ctrl.postsFor('DevFest')" > <post-title post="post" ></post-title> </li> </ul>
  30. Diminuir tempo do $digest angular.module('blogApp', []) .component('postsList', { controller: class

    PostsList { constructor(Post, category) { this.categoryPosts = this .postsFor(category); } postsFor(category) { return this.posts .filter((p) => { return p.category === category; }); } } }); Depois <ul> <li ng-repeat="post in $ctrl.categoryPosts" > <post-title post="post" ></post-title> </li> </ul>
  31. Evitar filtros na view ➔ Mesmo problema das expressões na

    view; ➔ Filtro é reprocessado todo $digest; ➔ Mover o uso do filtro para o controller.
  32. Evitar filtros na view <h1> {{ $ctrl.post.title }} <em>{{ $ctrl.postDate

    }}</em> </h1> Depois angular.module('blogApp', []) .component('postTitle', bindings: { post: '<' }, controller: class PostTitle { constructor($filter) { const date = $filter('date')(this.post.date); this.postDate = date; } } });
  33. ng-repeat mais rápido ➔ Cooperar com a checagem de duplicações

    do Angular; ➔ Todo item deve ter um identificador único; ➔ Também se aplica ao ng-options.
  34. ng-repeat mais rápido <ul> <li ng-repeat="post in $ctrl.categoryPosts track by

    post.id" > <post-title post="post" ></post-title> </li> </ul> Depois
  35. Remover checagens desnecessárias ➔ Remover o dirty-checking de valores que

    não mudarão; ➔ Diminui a quantidade de verificações do $digest; ➔ Operador de one-time binding; ➔ Pode ser aplicado à qualquer expressão; ➔ Ganho de desempenho porém deve ser pensado onde será usado.
  36. Usar ng-model-options ➔ Nem sempre uma mudança precisa ser checada

    o tempo todo; ➔ Permite postergar checagem ou checar somente quando certo evento ocorre.
  37. Boas práticas ➔ Combinar construtor com $onInit em componentes; ➔

    Melhorar expressividade na view; ➔ Favorecer factories à services; ➔ Ferramentas para desenvolvedores; ➔ Siga um styleguide.
  38. Combinar construtor com $onInit ➔ Utilizar corretamente o ciclo de

    vida dos componentes; ➔ $onInit é chamado quando todos os controllers foram instanciados e o bindings feitos; ➔ Usar construtor somente para dependency-injection.
  39. Combinar construtor com $onInit angular.module('blogApp', []) .component('postsList', { controller: class

    PostsList { constructor(Post) { Post.findAll() .then((posts) => { this.posts = posts; }); } }, template: ... }); angular.module('blogApp', []) .component('postsList', { controller: class PostsList { constructor(Post) { this.Post = Post; } $onInit() { this.Post.findAll() .then((posts) => { this.posts = posts; }); } }, template: ... }); Depois Antes
  40. Melhorar expressividade da view ➔ Identação do template; ➔ Manter

    eventos expressos na view em vez de usar função link; ➔ Ordenação de atributos das tags para maior clareza.
  41. Melhorar expressividade da view <input type="text" ng-model="$ctrl.post.title" ng-if="$ctrl.updateName" /> <input

    type="text" ng-model="$ctrl.post.title" ng-if="$ctrl.updateName"/> Identação
  42. Melhorar expressividade da view <input type="text" ng-model="$ctrl.post.title" ng-model-options="{ debounce: 500

    }" ng-change="$ctrl.titleChanged($event)" > <em>Preview:</em> <p> {{ $ctrl.post.title }} </p> Evento na view
  43. Melhorar expressividade da view <ul ng-if="$ctrl.categories" class="categories-list" > <li ng-repeat="ctg

    in $ctrl.categories" class="category-name" > {{ ctg.name }} </li> </ul> <ul class="categories-list" ng-if="$ctrl.categories" > <li class="category-name" ng-repeat="ctg in $ctrl.categories" > {{ ctg.name }} </li> </ul> Antes Depois Ordem das props
  44. ➔ Services são apenas wrappers para factories; ➔ Factories são

    mais versáteis e dão mais liberdade; ➔ É possível fazer tudo que um service faz com uma factory. Substituindo services por factories
  45. Substituindo services por factories function service(name, constructor) { return factory(name,

    [ '$injector', function($injector) { return $injector .instantiate(constructor); } ]); }
  46. angular.module('someModule') .service('ThingService', function() { // isso vai ser instanciado //

    como uma classe // this aponta para a instância }); angular.module('someModule') .factory('Thing', function() { // isso vai ser simplesmente retornado }); Substituindo services por factories
  47. ➔ AngularJS Batarang; ➔ Angular watchers; ➔ ng-inspector for AngularJS;

    ➔ Todos são para Google Chrome. Ferramentas para desenvolvedores
  48. ➔ Styleguide do John Papa está desatualizado; ➔ Prefira o

    styleguide do Todd Motto. Siga um styleguide