Novo no AngularJS? Confuso com o mumbo-jumbo diretivo? Tonto com a magia do Angular e sua nova produtividade? Cansado de pegadinhas que você não esperava? Curtindo o zen do Angular?
Confira minha apresentação sobre a construção de grandes aplicativos com AngularJS
Qualquer que seja sua experiência com o Angular e o tamanho do projeto em que você está trabalhando, chegará o dia em que você encontrará o Angular em estado selvagem sendo usado em um grande projeto. Isso soa como um sonho tornado realidade ou você ainda está desconfiado? De qualquer forma, você inevitavelmente se deparará com um problema que o Angular não resolverá para você. Você vai sobreviver?
Se você precisou:
- Organize seus arquivos
- Nomeie seus arquivos
- Carregue lentamente seu código
- Serializar o estado do aplicativo para o URL
- Derive o estado do aplicativo do URL
- Gerenciar uma camada de modelo
- Faça cache inteligente
- Trabalhe com uma interface RESTful (de verdade)
- Use uma biblioteca de terceiros com Angular
Então você já conheceu a besta.
Angular não é o framework para encerrar todos os frameworks, entretanto, nunca antes obtive tanto prazer com o desenvolvimento web. Não com NodeJs, não com NoSQL, não com BackboneJS. Para citar erroneamente @ryanflorence : “I’m Sick of the Web: [Angular ]’s Healing Balm”.
Minhas primeiras semanas com o AngularJS, eu era como uma criança na manhã de Natal, desembrulhando presentes o mais rápido que pude e correndo e mostrando a todos. Então meu novo carro de corrida quebrou após 10 voltas na pista, descobri que nenhum dos meus brinquedos anteriores combinava bem com os novos e percebi que precisaria de um doutorado. para entender meu novo livro “Directives for Dummies”. Depois de alguns meses de ódio / amor, as coisas começaram a clicar. As diretivas se tornaram minhas melhores amigas, as melhores práticas do Angular me colocaram em funcionamento e comecei a obter uma satisfação real com meu trabalho.
Agora, antes que você pense que é tudo diversão e jogos, aqui estão alguns pontos a serem considerados ao embarcar em sua jornada de criação de aplicativos grandes com o Angular:
Convenção vs Configuração
Quando um framework adota convenções, os desenvolvedores familiarizados com o framework podem passar de um projeto para outro com um mínimo de tempo de “ramp up” para se familiarizarem com cada projeto. Basicamente, tudo se resume a se familiarizar com o domínio de negócios do projeto. O objetivo das convenções é melhorar:
Estruturas “sem convenção” resultam em fragmentação, o que significa que o uso de uma estrutura por um desenvolvedor pode ser muito diferente do de outro. Exemplo: BackboneJS. Uma ferramenta excelente por si só – mas decididamente carente de convenções impostas. Você já viu dois desenvolvedores de Backbone fazerem algo exatamente da mesma maneira? Eles podem olhar para o projeto um do outro e não ter ideia do que está acontecendo. A liberdade tem suas desvantagens.
Em relação à convenção, o AngularJS é um pouco leve. O AngularJS se apresenta como uma estrutura “MVW” vagamente baseada em uma estrutura MVC tradicional. Os controladores angulares configuram e adicionam comportamento aos $scope
objetos, e os modelos e vinculação dupla do Angular criam uma camada de visualização bacana, mas o Angular não tem praticamente nenhuma opinião quando se trata da camada do modelo. Tu podes fazer o que quiseres.
O caminho para construir com sucesso um aplicativo grande, rápido e estável com AngularJS está em encontrar e seguir convenções para você e sua equipe de desenvolvimento. A documentação do AngularJS não ajuda muito com isso, mas muitos desenvolvedores estão adquirindo experiência com o Angular e compartilharam as convenções que os ajudaram a ter sucesso. Você não pode se comprometer a escrever seu grande aplicativo com AngularJS sem prestar atenção à lista de e-mails , ao projeto GitHub e às opiniões dos principais contribuidores do Angular.
Camada de modelo
Aplicativos grandes geralmente têm muitos dados para lidar, portanto, projetar uma camada de modelo sólida é essencial para construir e manter um aplicativo Angular estável. Os serviços angulares fornecem uma ferramenta útil para separar sua camada de modelo ou lógica de negócios de suas camadas de visualização e controlador. $rootScope
e $scope
são úteis como a camada de modelo para pequenos aplicativos, mas podem rapidamente se tornar uma massa de cadeias de herança prototípicas bagunçadas que podem ou não funcionar da maneira que você pensa. Por exemplo:
$scope.fruit = 'banana';
var $newScope = $scope.$new();
$scope.fruit; // 'banana'
$newScope.fruit; // 'banana'
$newScope.fruit = 'apple';
$newScope.fruit; // 'apple'
$scope.fruit; // 'banana' // Did you think it would be 'apple'?
Em um grande aplicativo AngularJS, é importante ter uma fonte confiável para seus dados. Mantenha seus dados em seus serviços angulares e use seus controladores para expor seus dados às suas visualizações.
Seus serviços são donos de seus dados:
app.service('MyService', function ($http, $q, $angularCacheFactory) {
var _dataCache = $angularCacheFactory('dataCache', {
maxAge: 3600000 // items expire after an hour
});
/**
* @class MyService
*/
return {
manipulateData: function (input) {
var output;
// do something with the data
return output;
},
getDataById: function (id) {
var deferred = $q.defer();
if (_dataCache.get(id)) {
deferred.resolve(_dataCache.get(id));
} else {
// Get the data from the server and populate cache
}
return deferred.promise;
}
};
});
Seus controladores e diretivas consomem seus dados:
$scope.$watch(function () {
return MyDataService.getMyData();
}, function () {
$scope.myData = MyDataService.getMyData();
});
Em última análise, você deseja que sua camada de modelo dê suporte às necessidades de um grande aplicativo do lado do cliente. Existem muitas ferramentas. O objetivo é não deixar que o Angular faça sua camada de modelo para você, porque você ficará desapontado. Procure algo como $ resource , restangular , BreezeJS ou até mesmo usando modelos / coleções do BackboneJS. Trate seus dados com respeito.
Organização de Arquivos
O cerne de muitos argumentos é a questão da organização e nomenclatura dos arquivos. Onde colocamos tudo? Organizamos por tipo, forma, função ou recurso? PascalCase ou camelCase? Algumas estruturas tomam essa decisão por você, aplicando uma convenção. Ruby on Rails, por exemplo, espera encontrar seus controladores em e suas visualizações em . Isso deixa pouco espaço para discussão ou ambiguidade. Algumas estruturas também usam convenções de nomenclatura para localizar seus arquivos. Aqui está um exemplo retirado do site da EmberJS :myApp/app/controller/
myApp/app/views/
App.Router.map(function() {
this.route("about", { path: "/about" });
this.route("favorites", { path: "/favs" });
});
Quando o usuário visita /
, Ember.js irá renderizar o index
modelo. Visitando renderiza o modelo e renderiza o modelo. Observe que você pode deixar o caminho fora se for igual ao nome da rota. Nesse caso, o seguinte é equivalente ao exemplo acima:/about
about
/favs
favorites
App.Router.map(function() {
this.route("about");
this.route("favorites", { path: "/favs" });
});
A própria estrutura do AngularJS não fornece tais convenções. Está tudo nas tuas mãos. Abaixo estão algumas soluções possíveis.
Arquivos monolíticos
O projeto angular-seed recomenda algo como o seguinte para a organização de arquivos de projeto:
partials/
home.html
login.html
users.html
orders.html
js/
controllers.js
directives.js
filters.js
services.js
app.js
Isso funciona muito bem para aplicativos pequenos e focados, mas rapidamente se torna insustentável com aplicativos grandes.
Pastas monolíticas
Um passo na direção certa, mas pode rapidamente se tornar ridículo. Imagine pesquisar na pasta quando ela contém mais de 100 arquivos, tentando encontrar o código de alguma funcionalidade obscura que seu gerente de produto deseja ajustar.controllers/
js/
controllers/
homeController.js
loginController.js
directives/
usersDirective.js
ordersDirective.js
filters/
services/
userService.js
orderService.js
loginService.js
partials/
home.html
login.html
users.html
orders.html
app.js
Organizar por recurso
Eu, pessoalmente, gosto de poder mapear visualmente o que vejo na tela para os arquivos de origem que fazem isso acontecer. Se estou olhando a página “usuários”, espero poder encontrar todo o código dela no pacote / pasta “usuários”. Modificar a página de “usuários” agora significa fazer alterações em arquivos em um pacote / pasta, em vez de realizar uma “cirurgia de espingarda”.
orders/
directives/
orders.html
ordersDirective.js
services/
orderService.js
users/
directives/
users.html
usersDirective.js
services/
userService.js
home/
controllers/
home.html
homeController.js
login.html
loginController.js
services/
loginService.js
shared/
services/
i18nService.js
filters/
i18nFilter.js
app.js
Módulos e componentes de aplicativos de carregamento lento
Módulos angulares não servem para nada … até agora. Exceto para carregar código angular de terceiros em seu aplicativo e fazer simulação durante o teste. Fora isso, não há razão para usar mais de um módulo em seu aplicativo. Misko disse sobre vários módulos: “[você] deve agrupar por visualização, uma vez que as visualizações serão carregadas lentamente em um futuro próximo”. Eu adoraria que alguém da equipe Angular nos esclarecesse quais são seus planos para isso.
O Angular não oferece suporte oficialmente ao carregamento lento de componentes. Embora existam diferentes soluções alternativas, elas podem resultar em um comportamento indefinido, porque o Angular espera que tudo seja carregado durante a fase de bootstrap. Parabéns para Ifeanyi por uma solução de carregamento lento.
Esta é a ideia básica:
var app = angular.module('app', []);
app.config(function ($controllerProvider, $compileProvider, $filterProvider, $provide, $animationProvider) {
// save references to the providers
app.lazy = {
controller: $controllerProvider.register,
directive: $compileProvider.directive,
filter: $filterProvider.register,
factory: $provide.factory,
service: $provide.service,
animation: $animationProvider.register
};
// define routes, etc.
});
Dependências de carregamento lento por rota:
$routeProvider.when('/items', {
templateUrl: 'partials/items.html',
resolve: {
load: ['$q', '$rootScope', function ($q, $rootScope) {
var deferred = $q.defer();
// At this point, use whatever mechanism you want
// in order to lazy load dependencies. e.g. require.js
// In this case, "itemsController" won't be loaded
// until the user hits the '/items' route
require(['itemsController'], function () {
$rootScope.$apply(function () {
deferred.resolve();
});
});
return deferred.promise;
}]
}
});
itemsController.js
// reference the saved controller provider
app.lazy.controller('ItemsController', function ($scope) {
// define ItemsController like normal
});
items.html
<section data-ng-controller="ItemsController">;
<!-- a bunch of awesome html here -->;
</section>;
Compreenda o ciclo de vida de $ scope
$ scope é a força vital do AngularJS. Você não pode construir um grande aplicativo com AngularJS e evitar os meandros do ciclo de vida $ scope. O $ rootScope de seu aplicativo e todos os seus escopos filhos $ contêm $ expressões de observação. Existe uma expressão $ watch para tudo que você coloca em $ scope. Essa é a mágica da vinculação de dados bidirecional do Angular.
Vários eventos disparam o que é chamado de loop $ digest que verifica cada expressão de observação $ para ver se o valor que está sendo observado mudou. Se mudou, então o Angular executa todos os ouvintes para aquela variável. Isso pode acontecer muitas vezes em um segundo. É importante que as expressões do seu relógio sejam rápidas e idempotentes. O desempenho pode se tornar um problema se você anexar funções como $ watch expressões ao seu $ scope. Essas funções serão executadas muitas vezes e podem diminuir o desempenho. Se qualquer um dos ouvintes acionados pela alteração das expressões $ watch criar ainda mais alterações, o que inicia mais loops $ digest que acabam acionando ainda mais ouvintes, o desempenho pode ser adversamente afetado.
Integração de terceiros com AngularJS
Se você gastou muito tempo com o Angular, provavelmente já teve problemas para fazer bibliotecas de terceiros funcionarem com o Angular. Se você estiver criando um aplicativo grande, é provável que precise integrar várias bibliotecas ao seu aplicativo. O Angular prospera em seu loop $ digest, oferecendo a você a mágica de vinculação de dados bidirecional. Bibliotecas de terceiros não sabem o que o Angular faz. Se sua biblioteca de terceiros mudar algo no DOM ou retornar um valor a você por meio de uma chamada AJAX, o Angular não saberá sobre isso e continuará fazendo seu trabalho, sem se dar conta. As chaves para integração de terceiros com Angular são $ scope. $ Apply (), $ scope. $ EvalAsync, $ q.when () e $ timeout.
Quando algo acontece com uma biblioteca de terceiros, você provavelmente precisará iniciar manualmente um loop $ digest via . Dessa forma, o Angular pode reagir a tudo o que sua biblioteca de terceiros fez. A biblioteca de promessa $ q do Angular é uma ferramenta muito útil para resolver de forma assíncrona o resultado de tudo o que seu terceiro vai fazer. Fazendo algo como:$scope.$apply()
$scope.$apply(function () {
// do something with 3rd-party lib
});
ou
$timeout(function () {
// do something with 3rd-party lib
}, 0);
$ timeout faz com que o Angular execute um automaticamente após a função anônima ser executada (que não é executada até o próximo loop de evento do navegador).$scope.$apply()
Aprender AngularJS é um passeio de montanha-russa, mas no final das contas resulta em maior produtividade e flexibilidade na construção de grandes aplicações.