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

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

[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.

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