AngularJS: Série de Bolso (parte 2) | Diego Doná
AngularJS é puramente um jogo de magia envolvendo Javascript, mas não adianta tentar lançar uma Avada Kedavra se você não consegue embaralhar as cartas direito.
Primeiro, certifique-se que você tem o AngularJS 1.x em mãos. Caso não tenha, baixe direto do site oficial. Cuidado para não baixar a versão 2, este tutorial não se aplica a ela:
Configure o download conforme quiser. Eu prefiro sempre ter a versão não minificada (nesse caso, Uncompressed) para xeretar o que bem entender:
Monte a sua estrutura de pastas para fazer os apontamentos e links de scripts. A minha ficou assim inicialmente:
<Pasta Principal>
Você pode modificar essa estrutura a sua vontade, ou jogar tudo em qualquer lugar, eu não vou te julgar.
Preencha o index.html com o seguinte:
<html ng-app="minhaApp">
<title>AngularJS: Série de Bolso (parte 2)</title>
<h1>Hello {{"AngularJS"}} {{5-4}}</h1>
var app = angular.module('minhaApp', []);
Na linha 2, nós indicamos ao angular qual o nome da aplicação ("minhaApp"). Na linha 12 criamos o módulo app.
Com isso podemos usar algumas funcionalidades do angular e imprimir "Hello AngularJS 1" na tela. Notou que fizemos uma subtração para exibir o 1?
O Angular usa as "{{}}" para indicar onde teremos um conteúdo a ser substituído, mas não se limita a trabalhar nas chaves.
Vamos melhorar o exemplo. Digamos que eu queria exibir o nome de uma pessoa no conteúdo HTML. Podemos fazer isso com o seguinte:
<body ng-controller="MeuController">
<h1>Hello {{Amigo}}</h1>
app.controller('MeuController', ['$scope', function($scope){
$scope.Amigo = "Diego Doná";
Na linha 2, nada de novo, indicamos que a aplicação se chama "app" dessa vez.
Na linha 6 estamos indicando o controller responsável pela tag body, no caso "MeuController".
Na linha 8 estamos pedindo que ele imprima o que diabos for "Amigo".
Na linha 12, nada de novo, estamos declarando o módulo, desta vez com nome app.
Na linha 13 estamos declarando o controllador "MeuController" e na linha 14, definimos quem/o que é "Amigo".
Espera… ng-controller? Controller? '$scope'? Uma function dentro de um array?!
Hora de respirar fundo. No AngularJS nós precisamos de controllers para atuar sobre nossas views (basicamente, HTML). Eles definem, em essência, variáveis e métodos de forma separada da visualização.
Por isso usamos um controller, para termos onde definir os dados.
Já o $scope representa a coleção de dados que o framework está monitorando. De acordo com a documentação, ele é o objeto que referencia o modelo da aplicação.
Pense nele como uma coleção de dados e métodos presentes no escopo atual da aplicação. Por isso usamos o $scope para definir qual o valor será transportado para a tela.
O AngularJS se encarrega de levar o "Amigo", presente no $scope "MeuController" até a view que você indicou ser de responsabilidade dele – a tag <body>.
Sobre a function dentro do array, eu me defendo, é uma medida protetora que o framework AngularJS adota pelo seu modelo de Dependency Injection.
$scope é injetado dentro do controller no momento que o criamos. Se você não passar a string antes, irá funcionar, até você minificar o código. Aí suas variáveis mudarão de nome e $scope se torna xyz.
Usando o modelo com strings, nos escapamos disso. O AngularJS não se importa com o nome dentro da function, ele vai assumir que é um objeto do tipo que você passou anteriormente. Esta é uma boa prática, não uma regra.
Por enquanto injetamos apenas o $scope, mas existem diversas outras possibilidades. Fique atento à ordem dos parâmetros.
Crie outras variáveis no $scope dentro do controller e as exiba no html usando o {{}}. Tente criar objetos e exibir suas propriedade caso sinta-se confiante. Lembrando, para ser levado a view, deve estar presente no $scope!
Quando se sentir confiante, vamos complicar um pouco mais e abalar sua estabilidade emocional recém adquirida.
Evite o uso direto de $scope
Sim, eu acabei de dizer que o $scope é a representação dos dados da aplicação e que sem ele os dados não vão para a view. Agora estou te dizendo para não usá-lo diretamente em seus controllers: pode ser prejudicial à longo prazo.
Jogar e retirar os dados diretamente do $scope leva a um código possivelmente confuso. Não em nossos pequenos exemplos, mas em aplicações completas.
Por exemplo, se usarmos um controller geral para a tag <body> e outro, mais específico, para o rodapé. Neste cenário, se você exibir algo como {{Data}} dentro do rodapé, não está claro se você quer algo do rodapé ou do body.
Vence o $scope mais específico. Se você redefiniu Data em algum momento, dentro do controller do rodapé, ele terá o último valor, caso contrário, terá o valor "padrão" do pai.
A saída será "Ciclano e Zé" – confundiu, né? Trocamos o pai, mas não o filho. A alternativa é o uso de controllerAs:
<body ng-controller="MeuController as meuCtrl">
<h1>Hello {{meuCtrl.Amigo}}</h1>
app.controller('MeuController', [function(){
vm.Amigo = "William"
Nesse modelo, literalmente damos um "apelido" ao controller e usamos ele na view para chamar as propriedades.
Se você trabalhar com herança de controllers, vai ser grato em usar essa técnica e saber exatamente com o quê está lidando.
Fica por sua conta usá-la ou não. Eu já escrevi aplicações inteiras sem o uso de controllerAs e não tive (muitos) problemas, antes de aprender as vantagens desta técnica. É apenas uma boa prática.
Dentro do controller eu usei a variável "vm" para anexar as propriedades (linha 14). Você pode escolher qualquer nome, inclusive, pode usar o mesmo apelido do controller. É uma questão de gosto.
Agora não tem erro: Estamos pedindo as propriedades do bodyCtrl (apelido de BodyController) explicitamente, não importa termos uma propriedade em outro controlador com o mesmo nome.
Quando se sentir confiante de novo, se prepare pra mais uma rasteira.
Essa estrutura está longe de ser real!
Fizemos tudo até agora usando apenas o index.html. Na vida, não vamos trabalhar dentro de um único .html e escrever todos os nossos javascripts lá. Pelo menos você não deveria.
Vamos melhorar um pouco nossa estrutura:
Nosso index:
Nosso app.js, responsável por iniciar o módulo "app":
.module('app', []); //estamos CRIANDO o módulo app
Nosso controller.js, que define o "MeuController" indicado na view index.html:
.module('app') //estamos ACESSANDO o módulo app
function meuController(){
Agora sim. Separamos nosso html e javascript, de bônus usamos IIFE ("funções invocadas imediatamente") para garantir o escopo de todas as nossas variáveis e 'use strict' para melhorar a captura de erros.
Também estamos injetando as dependências de uma maneira mais declarativa, para evitar o uso de arrays em funções (aquilo realmente pode ficar confuso).
Para declarar o controller, declaramos a função como referência (linha 5), declaramos as injeções (linha 7) e declaramos a implementação (linha 8).
Esse modelo de desenvolvimento é baseado no guideline do John Papa para AngularJS 1.x. O ideal é que você crie um arquivo para cada controller, todos nesse mesmo modelo, com IIFE e use strict.
É importante notar que quando fazemos "angular.module('app')" nós estamos *acessando* um módulo que já deve existir. Se passarmos o array ao lado, significa que estamos *criando* um módulo.
Nós criamos o módulo principal no app.js e o acessamos no controller.js.
Vamos fazer uma brincadeira usando a diretiva ng-model. Mude seu html para o seguinte:
<h1>Olá {{meuCtrl.Amigo}}!</h1>
<input type="text" ng-model="meuCtrl.Amigo" />
Adicionamos nosso velho conhecido {{}} e um input. A graça está na diretiva ng-model, que garante a passagem dos dados contidos no input para o controller e vice e versa (palavra chave: two way data binding)
Escreva algo no input e se divirta com a atualização instantânea com um esforço mínimo.
Para provar o two way, atualize o html para o seguinte, desta vez com a diretiva ng-click e um button:
<button type="button" ng-click="meuCtrl.setAmigo()">Clique</button>
E seu controller para:
vm.Amigo = undefined;
vm.setAmigo = setAmigo;
function setAmigo() {
vm.Amigo = "Nayara";
A diretiva ng-click faz com que, ao clicar, algo seja executado. Como mexemos na propriedade Amigo, os dados são atualizados diretamente na tela.
Neste post vimos alguns conceitos e suas colocações em prática. Estendi um pouco as explicações sobre os conceitos que achei essenciais e fui chato com os padrões que sigo no git do John Papa, pois acho esse estilo um dos melhores para manter aplicações organizadas.
A produtividade é desacelerada um pouco, mas a manutenabilidade é mais importante. A troca vale a pena.
Como sempre: Faça testes. Crie outro arquivo de controller (meu-segundo-controller.js, por exemplo) e faça outras brincadeiras pelo index. Faça um botão que ao ser clicado dispare um alert (banca colocar dentro da função no controller).
Todos os arquivos que usei no artigo estão no meu github.com/diedona, passe por lá se quiser olhar os arquivos em sua versão original, inclusive mostrando a confusão de $scopes aninhados.
Se quiser, olhe uma brincadeira com o angular que fiz no Plunker.
No próximo artigo vamos conhecer mais diretivas e fazer alguma mini aplicação – ainda sem envolver base de dados.
Editado 19/11/2016:
Visite o próximo artigo da nossa série de bolso sobre AngularJS!
Autor DonáPublicado em 16 de novembro de 2016 19 de novembro de 2016 Categorias Desenvolvimento Web, Série de Bolso: AngularJSTags Angular, AngularJS, Javascript