AngularJs - Dicas para melhorar sua estrutura de projeto e código Artigo

Conheça os cursos gratuitos do WebDevBr! - Inscreva-se!


Este artigo foi publicado a 1 ano, 2 meses, 2 semanas atrás.

Uma novidade que me fez repensar toda a organização dos meus projetos com AngularJs foi o novo recurso components da versão 1.5, na verdade eu sempre sentia uma coceirinha no cérebro quando olhava a antiga organização dos meus arquivos.

Eu já usei várias formas de organização de diretórios e arquivos no AngularJs, uma delas era separando em controllers, factories, services e arquivos html, mas e se eu precisasse ver/editar/reaproveitar um determinado módulo? Fica tudo separado e é pouco prático. Comecei a separar por módulos então, e fez muito sentido pra mim por um tempo, mas eu não respeitava "o conjunto", digo, aqui no WebDevBr tenho a área de alunos com AngularJs e um dos módulos se chama cursos, ele lista os cursos, outro modulo exibe um único curso e suas aulas, navegação e tudo o mais, eu tinha:

  • cursos
    • controller.js
    • service.js
    • partial.html
  • curso
    • controller.js
    • service.js
    • partial.html
  • app.js
  • app.html

E então eu conheci o Angular 2, desacredito totalmente que seja a hora de adota-lo neste momento, mas conhecimento nenhum é perda de tempo, então comecei a aplicar o que vi ali na versão 1 e fiquei com isso:

Gostou deste artigo?

Receba atualizações semanais com novos artigos do WebDevBr e outras dicas!

  • cursos
    • curso
      • controller.js
      • service.js
      • partial.html
      • controller.js
      • service.js
      • partial.html
  • app.js
  • app.html

Também trabalhei os nomes dos arquivos.

  • cursos
    • curso
      • curso.controller.js
      • curso.service.js
      • curso.html
      • cursos.controller.js
      • cursos.service.js
      • cursos.html
  • app.js
  • app.html

Outra coisa começou a me irritar eram estes arquivos html junto com os scripts, se eu quisesse reaproveitar isso para aplicações desktop ou mobile eu teria que ficar todo sete dedos para não copiar eles junto e substituir no caso de uma correção nos scripts, então:

  • app
    • cursos
      • curso
        • curso.controller.js
        • curso.service.js
        • cursos.controller.js
        • cursos.service.js
    • app.js
  • partials
    • cursos
      • curso
        • curso.html
        • cursos.html
    • app.html

A última alteração que fiz foi mudar o nome dos módulos que eram:

<!-- cursos.controller.js -->
angular.module('cursos', []);
<!-- curso.controller.js -->
angular.module('curso', []);
<!-- app.js -->
angular.module('app', ['cursos', 'curso']);

Para :

<!-- cursos.controller.js -->
angular.module('app.cursos', ['app.cursos.curso']);
<!-- curso.controller.js -->
angular.module('app.cursos.curso', []);
<!-- app.js -->
angular.module('app', ['app.cursos']);

Como namespaces. Assim o nome do módulo também já me diz aonde ele e sua partial está, mesmo minificado. Note que eu também movi a dependência do módulo app.cursos.curso para dentro do app.cursos, assim facilita a injeção do conjunto.

Claro que eu peguei apenas 3 arquivos de exemplo para usar aqui, na verdade eu tenho vários outros módulos, alguns que ainda não aparecem, esperando dados do servidor, como questões de finalização dos cursos (prova) e emissão de certificado, e outros que omiti para não ficar um exemplo inviável, então eu adianto que esta estrutura é muito melhor para projetos pequenos ou grandes.

Organização do código

Protegendo o escopo

Dentre várias coisas que comecei a usar nos últimos meses, uma delas é IIFE (Immediately-Invoked Function Expression) que são funções anônimas (sem nome) que são executadas automaticamente quando encontradas, isso me ajuda a encapsular as variáveis apenas no escopo daquele arquivo em questão.

Vamos ver se eu explico melhor, no Javascript você tem duas palavras-chave para criar variáveis, var e let, a primeira cria uma variável de escopo local, ou seja, se usada dentro de um método ela é acessível apenas ali no método, let é mais específica, se usada dentro de uma estrutura de controle ela se torna disponível apenas ali, enquanto var disponibiliza variável na função toda, exemplo:

var nome = 'Erik'; // disponível em qualquer função
function logDeNome() {
    var sobrenome = 'Figueiredo'; // disponível somente em logDeNome

    for (var i = 0; i<100; i++) {
        let j = i*10; // disponível somente dentro do for
    }

    console.log(nome + ' '+ sobrenome);
    console.log(j); // vai dar erro
}

Outra forma de criar uma variável é não usar nem var e nem let, assim ela fica disponível de forma global (o que não é uma boa ideia por vários motivos que não vou me aprofundar agora).

Agora que você já entendeu o escopo de variáveis e como usar isso a seu favor, lhe apresento o IIFE:

(function() {
    var nome = 'Erik'; // disponível em qualquer função, #SQN, apenas dentro deste escopo
    function logDeNome() {
        var sobrenome = 'Figueiredo'; // disponível somente em logDeNome

        for (var i = 0; i<100; i++) {
            let j = i*10; // disponível somente dentro do for
        }

        console.log(nome + ' '+ sobrenome);
        console.log(j); // vai dar erro
    }
})()

Notou que eu ecapsulei tudo dentro de um (function(){/**código aqui**/})(), esse é o IIFE, normalmente eu escrevo todo meu JS dentro dele, em cada arquivo.

Strict mode

Eu também tenho usado o strict mode do Javascript, principalmente em busca de melhorar meu código, você pode ler mais sobre na w3schools.com.

É simples:

(function() {
    'use strict';

    angular
        .module('app', [])
        .controller('AppCtrl', ['$scope', '$http', function($scope, $http) {
            $scope.nome = "erik";
        }]);

})()

Strict mode está disponível na versão 1.8.5 do Javascript (ES5) e não causa erro algum nas versões anteriores.

Components

Outra alteração significativa foi relacionada ao recurso component, que é um tipo especial de directiva mais simples e muito prático para a organização das nossas aplicações.

Os components são módulos completos (ou conjunto de módulos) do AngularJs que executam em uma tag especial, de forma a facilitar a reusabilidade e até a localização de onde o conteúdo será mostrado.

Para configurar um component vamos fazer assim:

(function() {
    'use strict';

    angular
        .module('app', [])
        .component('app', {
            templateUrl: 'partials/app.html',
            controller: CursosController,
            controllerAs: 'vm'
        })
        .controller('AppCtrl', ['$http', function($http) {
            var vm = this;
            vm.nome = "erik";
        }]);

})()

Eu dei o nome app ao component, nele informei um template e um controller, o parâmetro controllerAs indica o nome que vou usar na view para acessar dados vindo do controller (não vou usar mais o $scope), no html eu tinha:

{{nome}}

Agora vou ter:

{{vm.nome}}

E para usar este component apenas informo a tag <app></app> que é o nome que dei ao component, da pra usar normalmente no HTML ou até em rotas:

$routeProvider
    .when('/', {
      template: '<app></app>'
      //templateUrl: 'partials/app.html',
      //controller: 'AppCtrl'
    })

Até comentei as linhas que não preciso mais, para arquivos grandes de rotas é fantástico, ele já roda o html e o controller com base na configuração do component.

Injeção de dependências

Também passei a injetar as dependências de outra forma, antes fazia assim:

(function() {
    'use strict';

    angular
        .module('app', [])
        .component('app', {
            templateUrl: 'partials/app.html',
            controller: CursosController,
            controllerAs: 'vm'
        })
        .controller('AppCtrl', ['$http', function($http) { //viu aqui como injetei o $http?
            var vm = this;
            vm.nome = "erik";
        }]);

})()

Em vez de fazer assim, em uma linha só, eu chamo o controller externamente:

(function() {
    'use strict';

    angular
        .module('app', [])
        .component('app', {
            templateUrl: 'partials/app.html',
            controller: CursosController,
            controllerAs: 'vm'
        })
        .controller('AppCtrl', AppCtrl); //chamo meu function

        AppCtrl.$inject = ['$http']; //aqui eu injeto o $http

        function AppCtrl ($http) { //aqui o controller
            var vm = this;
            vm.nome = "erik";
        }

})()

Uma das minhas regras enquanto desenvolvedor é que largura é mais importante que altura, então eu procuro sempre escrever linhas curtas e isso fica dificil com AngularJs, já que da primeira forma com 5 ou 6 dependências a coisa já meio que foge de controle, agora fica curtinho, do jeito que eu gosto.

Service em arquivo diferente

Por último, eu sempre separei services e factories do controller, antes ficava assim:

(function() {
    'use strict';

    /** 
     * meuModulo vinha do arquivo de controller, mas isso não é possível agora,
     * por conta do IIFE 
     */
    meuModulo.service('CursosService', CursosService);

    CursosService.$inject = ['$http'];

    function CursosService ($http) {
        var service = {
            getAll: getAll,
            getOne: getOne
        }

        return service;

        function getAll() {

        }

        function getOne(id) {

        }
    }
})()

Agora eu troco para o seguinte:

(function() {
    'use strict';

    // note que falta o , []) no .module()
    angular
        .module('app.cursos')
        .service('CursosService', CursosService);

    CursosService.$inject = ['$http'];

    function CursosService ($http) {
        var service = {
            getAll: getAll,
            getOne: getOne
        }

        return service;

        function getAll() {

        }

        function getOne(id) {

        }
    }
})()

A grande diferença é que eu não passo o parâmetro de injeção de módulos no .module(), desta forma ele não vai recriar o módulo, mas adicionar recursos ao que já foi criado.

Simples e eficiente.

Conclusão

Queria passar uma visão geral da coisa toda aqui, espero nas próximas semanas preparar artigos sobre cada assunto separadamente e com mais detalhes e dicas, mas de primeira você pode observar que o código mudou muito e melhorou muito também, agora mais modular e simples de manter.


Cursos relacionados


* Parcelamento apenas cartão de crédito! Pode haver uma pequena variação no parcelamento em relação a simulações apresentadas!