Esse post é a continuação da nossa série sobre como usamos AngularJS e ES6 nas nossas aplicações. Confira aqui o post anterior, onde falamos sobre como escolhemos o babel (antigo 6to5
) para traduzir ES6 em ES5, por causa do seu output
bonito e da opcionalidade de um runtime
. Desta vez nós falaremos sobre como usar ES6 para melhorar o código dos seus Controllers.
Controllers
Os Controllers do AngularJS são funções que te ajudam a manter controle sobre o scope
. Que, por sua vez, é a cola entre a View e seus dados (o “model”). Seja usando em diretivas, rotas ou diretamente com ng-controller
é ele que contém e manipula os dados que você irá usar em seus templates.
Muitos tutoriais de Angular ensinam a usar Controllers da maneira mais simples, como esta:
<div ng-controller="MyController"> <h2>Olá, meu nome é {{ name }}.</h2> </div>
var app = angular.module('myapp'); app.controller('MyController', function ($scope) { $scope.name = 'Diogo'; });
Isso funciona perfeitamente em uma aplicação pequena. Porém, quando a aplicação começa a escalar você perde totalmente o controle sobre o código. Pois, com certeza, irá precisar de vários controllers aninhados em views e componentes complexos. Tendo isso em mente, gostaria de apresentar alguns conceitos. Que, com a ajuda de ES6, podem ajudar sua aplicação a ser lindamente mais escalável, manutenível e testável.
O Controller “ideal”
Na engenharia de software não há bala de prata, mas sim soluções para cada tipo de projeto. Sabendo disso, mostrarei o tipo de Controller e estilo de código que se encaixa muito bem para nossas aplicações e com nosso time:
(function (module) { 'use strict'; class MyController { constructor ($myService, $myErrorHandler) { this.myService = $myService; this.myErrorHandler = $myErrorHandler; this.getName(); } getName () { this.myService.sayMyName() .then((name) => { this.name = name; }) .catch(this.myErrorHandler.handle()); } } module.controller('MyController', MyController); })(angular.module('MyApp.controllers.MyController', []));
Ele usa de três conceitos muito importantes para manter o código limpo: Classes, ControllerAs e IIFEs.
Classes
Nada melhor que Classes para ilustrar os benefícios de se usar ES6 em aplicações AngularJS. Quem está familiarizado com ES5 provavelmente já passou por alguma thread desse tipo no StackOverflow. Isso porque, devido a JavaScript ser prototype-based
, na especificação antiga não havia nenhuma forma “clara” de definir uma classe. Veja a comparação:
// ES6 class Person { constructor (name) { this.name = name; } sayName () { console.log(`My name is ${this.name}`); } } // vs // ES5 function Person (name) { this.name = name; } Person.prototype.sayName = function () { console.log('My name is ' + this.name); }
Ambas têm exatamente a mesma funcionalidade. Porém o syntax sugar
que a classe insere pode salvar seu código. Vamos imaginar como seria nosso controller “ideal” ali de cima escrito em ES5:
(function (module) { 'use strict'; function MyController ($myService, $myErrorHandler) { var vm = this; vm.getName = function () { $myService.sayMyName() .then(function (name) { vm.name = name; }) .catch($myErrorHandler.handle()); } vm.getName(); } module.controller('MyController', MyController); })(angular.module('MyApp.controllers.MyController', []));
Mesmo adotando um ótimo styleguide para escrevê-lo, fato que já melhora bastante a legibilidade do seu código. A sintáxe de classe trás os benefícios de você não precisar ter tudo dentro de uma função monolítica e de não precisar controlar o escopo das suas funções manualmente, como a váriavel vm
no exemplo acima. O this
sempre será a instância do Controller.
ControllerAs
Inserido na versão 1.2.0
do Angular, ControllerAs é uma funcionalidade que permite usar o escopo do seu Controller como um objeto nas Views no lugar do $scope
. Junto com o conceito de Classes que apresentamos acima, são uma dupla imbatível. Como vemos no trecho de código a seguir do nosso exemplo:
getName () { this.myService.sayMyName() .then((name) => { this.name = name; }) .catch(this.myErrorHandler.handle()); }
O ControllerAs possibilita que a variável myCtrl.name
esteja disponível na View sem a necessidade de usar o $scope
diretamente.
Modularização e IIFEs
Vamos supor que, para mandar sua aplicação para produção, você concatene e minifique o código. Se a mesma for muito grande, com certeza você irá querer modularizá-la. Porém, nessa tentativa, pode ser que o escopo global (window
) seja poluído.
Para resolver esse problema recomenda-se o uso de IIFEs (Immediately-invoked function expressions), ou seja, funções que são executadas imediatamente e que tem um escopo próprio. Como no nosso exemplo:
(function (module) { // code... })(angular.module('MyApp.controllers.MyController', []));
Isso impede que sua classe MyController
seja exposta no escopo global. Deixando sua aplicação mais segura e menos propensa a intervenções de código de terceiros.
Não pare por aqui
Os mesmos conceitos apresentados aqui para os Controllers se encaixam em Services, Directives, Factories, Providers, etc. Então não pare por aqui, você pode começar hoje a usar ES6 em suas aplicações AngularJS e vai ganhar muito com isso.