AngularJS: Básico + RSS Feed Reader

Talvez vocês ainda não tenham ouvido falar em AngularJS, talvez já tenham, porém o fato é que iremos citar este cara por muitos posts ainda neste blog.

Se você não sabe o que é AngularJS, vou introduzir muito superficialmente: é mais uma framework MV-* incrementada, ou seja, um modo para organizar os seus scripts, suas views, seus módulos e mais um plus, assim como o EmberJS, o Knockout e o BackboneJS. E se você nunca ouviu falar de nenhuma destas, estamos falando de organizar e melhorar o combo HTML+Javascript.

Eu experimentei algumas destas frameworks e embora eu não consiga dizer exatamente o por quê, o AngularJS foi a que mais senti afinidade. Uma das muitas missões da framework é permitir que o conteúdo declarativo do HTML torne-se extensivo e dinâmico. E isto é percebido assim que visualizamos uma view do AngularJS.

Introduções a parte, a ideia deste post é criar um RSS Feed Reader com o AngularJS. Primeiro a funcionalidade é bastante utilizada, segundo por que é uma forma bem interessante para iniciar o estudo na framework. Vamos embora!

Crie um diretório com qualquer nome que você desejar (eu aqui chamei de “feed-reader”). Lá dentro crie dois arquivos: index.html e app.js

Agora vamos baixar o LABjs para carregar os nossos scripts de modo assíncrono, conforme você aprendeu neste artigo. Coloque o arquivo LAB.min.js dentro da mesma pasta. Ficará assim:

+ feed-reader
– app.js
– index.html
– LAB.min.js

Vejamos por onde podemos começar………………… Vamos editar o index.html e importar os scripts assincronamente?

<!DOCTYPE html>

<html>

<head>

<title>AngularJS – Feed Reader</title>

</head>

<body>

<h1>FEED READER</h1>

<script src=”LAB.min.js”></script>

<script>

$LAB

.script(‘//ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js’)

.script(‘app.js’)

.wait(function(){

angular.bootstrap(document, [‘app’]);

});

</script>

</body>

</html>

Chamamos a biblioteca para carregar de forma assíncrona o nosso script e o AngularJS a partir do CDN da Google. Após carregarmos, iremos inicializar o AngularJS de forma manual através do seguinte comando: angular.bootstrap(document, [‘app’]);

Quem já conhece do assunto pode estar se perguntando por que não estamos inicializando o AngularJS de forma automática, através da diretiva ng-app. O motivo é a questão do carregamento assíncrono. Tem que esperar baixar os scripts para inicializar o AngularJS.

Se quiser fazer o teste aí e carregar de forma síncrona, pode escrever o HTML do seguinte modo:

<!DOCTYPE html>

<html ng-app=”app”>

<head>

<title>AngularJS – Feed Reader</title>

</head>

<body>

<h1>FEED READER</h1>

<script src=”//ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js”></script>

<script src=”app.js”></script>

</body>

“Bem, eu nunca tinha ouvidor falar em AngularJS até este a ler este artigo. Neste caso, o que é este ng-app no meio da minha página?” Vamos a alguns conceitos e esclarecimentos antes de continuarmos.

No HTML, desenvolvemos uma página utilizando uma série de elementos para formatar o conteúdo. Este montão de elementos (<head><title><body><p><span><ul><li><font><br> etc) é aquilo que no popular convencionou-se a chamar de TAGs. Estas TAGs são descritas pelo desenvolvedor através de declaração estática e está limitada pela especificação do HTML. Ou seja, se você quiser dizer algo como <ul inserir-uma-li-para-cada-objeto-na=’minha_colecao’> não tem como… Mal é possível estender o elemento <ul> para descrever atributos – só é possível para atributos estruturais (data-) a partir do Html5.

A opção para o cenário acima era utilizar uma view/template engine, que muitas vezes faz uma verdadeira vitamina de frutas: junte o seu template carregado numa string estática, acrescente alguns dados e está pronto! Uma nova string prontinha para ser colada em sua página!

Eu não sei se ficou implícito, mas temos aí um problema bem visível: este tipo de engine só olha para frente! Qualquer alteração no modelo exige uma nova vitamina de frutas. Isto é chamado de “One-Way Data Binding”. Eu apenas mesclo e entrego.

Com o AngularJS, a principal ideia é trazer vida para as TAGs, e não somente uma template engine. E ele resolve o problema acima por que trabalha através do DOM ao invés de fazer vitaminas. Além disto, o AngularJS consegue ser “Two-Way”, ou seja, vai e volta. Se eu estabelecer um canal entre A e B, quando alterar um o outro também é notificado.  O AngularJS está olhando para o seu DOM o tempo inteiro, modificando-o quando necessário.

E como ele faz isto? Lembra daquele ng-app que adicionamos na página síncrona? Aquilo é o que o AngularJS chama de diretiva.

Diretiva é a forma a qual estendemos as TAGs para assumir novos comportamentos através do  AngularJS. É a forma a qual dizemos para o Browser que temos alguma coisa diferente para ele resolver. O AngularJS traz um monte de diretivas prontas, as chamadas Built-Ins, mas ele permite que criemos as nossas próprias – e, acredite, você irá fazer isto bastante.

No caso, estamos utilizando a diretiva ngApp=”app”, a qual está linkada com uma função javascript que inicializa o AngularJS. E qual seria a função? Um salve para aqueles que responderam angular.bootstrap(document, [‘app’]);

Cada TAG não está restrita a uma única diretiva. Pelo contrário, ela pode assumir múltiplos comportamentos através de múltiplas diretivas.

O AngularJS possui um compilador que varre a sua página em busca de diretivas. Cada vez que ele acha uma diretiva, ela é adicionada numa lista de diretivas correspondente a TAG em questão. Após este processo temos uma lista de diretivas correspondente as TAGs, então o AngularJS ordena esta lista seguindo uma prioridade de execução.

Por fim, o compilador executa uma função própria para cada diretiva, trazendo como retorno uma espécie de função associada (link function) para a diretiva. Lembra do ngApp=”app”? A função associada para esta diretiva seria a angular.bootstrap(document, [‘app’]);

Por fim, a compilação é finalizada, mas o processo não acaba aqui. Entra em jogo uma segunda fase chamada Linking Phase. Nesta fase, pegamos cada uma das funções associadas e as disparamos – tudo feito de forma transparente pelo AngularJS. Ao disparar são registrados listeners e watchers, de modo que possibilitem o binding em real-time no formato “Two-Way”.

Com todos estes conceitos, tenho certeza que agora será mais fácil acompanhar. Vamos a mais código.

<!DOCTYPE html>

<html>

<head>

<title>AngularJS – Feed Reader</title>

</head>

<body ng-controller=”FeedController” ng-init=”uri(‘http://www.infoq.com/br/rss/rss.action?token=cdJ7rbpk8no6E9eJc4JA0B2iNEIMJBm0&#8217;)”>

<h1>FEED READER</h1>

<ul id=”list”>

<li ng-repeat=”feed in feeds”>

<p>{{feed.publishedDate}}</p>

<p><h2><a href=”{{feed.link}}”>{{feed.title}}</a></h2></p>

<p><i>{{feed.contentSnippet}}</i></p>

</li>

</ul>

<script src=”LAB.min.js”></script>

<script>

$LAB

.script(‘//ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js’)

.script(‘app.js’)

.wait(function(){

angular.bootstrap(document, [‘app’]);

});

</script>

</body>

</html>

Inseri o desenho da página inteira (não teremos mais surpresas aqui!). Você pode observar um monte de coisas novas e também perceber o que eu disse lá atrás: que o AngularJS ambiciona a tornar o HTML algo mais dinâmico – preste atenção na diretiva ng-repeat.

Entre este monte de coisas, temos logo no body a adição de uma diretiva chamada ng-controller, que está configurada com a propriedade “FeedController”. Este “FeedController” é uma função javascript que funciona como uma controladora e que iremos criar lá no “app.js”.

Os letrados devem estar se perguntando “Controller, como no MVC”? Isto mesmo, amigo leitor! O AngularJS tem total suporte a MVC. No caso, o ng-controller é a diretiva que recebe a função controladora javascript que contém as regras de negócio para ser exibidas na view.

Cada controller conversa com o escopo do elemento ($scope) como parâmetro. Através deste escopo, recebemos e enviamos (two-way) as atualizações da model. E é pelo mesmo caminho que iremos atualizar a view (neste caso, a página em questão).

Configuramos o ng-controller dentro da TAG “body”, logo este controlador poderá ser utilizado em todos os elementos filhos, ou seja, em toda a página.

Observe o seguinte trecho:

<li ng-repeat=”feed in feeds”>

<p>{{feed.publishedDate}}</p>

<p><h2><a href=”{{feed.link}}”>{{feed.title}}</a></h2></p>

<p><i>{{feed.contentSnippet}}</i></p>

</li>

Temos uma diretiva de iteração (um looping) dentro de um elemento li, que será repetido para cada feed existente. A diretiva está configurada com uma expressão: “feed in feeds”. Este “feed” é variável e você poderia atribuir qualquer nome. Já “feeds” é o nome de nossa coleção, que deverá ser retornada pelo controller. Sendo assim, podemos concluir que necessariamente teremos uma linha de código dentro de nosso controlador configurando a model, através do comando $scope.feeds = [lista de feeds].

Lembre-se: o $scope é o responsável por fazer transmitir as informações entre controller (script) e view (html). Neste caso, apenas para ficar claro, eu irei montar a lista de feeds no controller para conseguir exibir na view (deste modo aí que você viu no código acima).

Posteriormente temos algumas instruções dentro de blocos {{ }}. Esta é a maneira declarativa do AngularJS para especificar onde ocorrerá o binding dos dados (e ele pode estar em qualquer lugar). Neste caso, se eu alterar a model, ainda que seja no controller, automaticamente a view será atualizada.

Veja só um exemplo bem simples que mostra isto na prática:

<!DOCTYPE html>

<html>

<head>

<title>AngularJS – Exemplo de Binding</title>

</head>

<body>

<h1>Meu nome é {{nome}}</h1>

<input type=”text” ng-model=”nome”>

<script src=”LAB.min.js”></script>

<script>

$LAB

.script(‘http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js&#8217;)

.wait(function(){

angular.bootstrap(document, []);

});

</script>

</body>

</html>

Você digita qualquer coisa na caixa de texto, que possui uma diretiva (ng-model) para dizer que ela é model da propriedade “nome” e automaticamente a mesma será exibida na declaração {{nome}}.

Ou seja, no exemplo do ng-repeat, estamos fazendo um looping e carregando os valores correspondentes as propriedades declaradas no $scope em nosso script (que ainda iremos criar).

Agora falta somente esclarecer à diretiva ng-init.

Como o nome diz, a diretiva é responsável por inicializar valores para o seu elemento. No contexto que estamos utilizando, funciona mais ou menos como o construtor de um controller. Sendo mais específico, utilizamos a diretiva para passar a URL do feed que pretendemos carregar na view.

Uma coisa a tomar nota é que esta diretiva tem prioridade “0” na ordem de execução das funções associadas (linked functions). Não poderia ser diferente, tratando-se de um construtor, mas eu não poderia deixar de mencionar isto para que você entenda melhor como funciona este negócio de prioridade na execução das funções associadas.

Por fim, vamos ao script!

Lembra-se do comando ng-app=”app” ou do angular.bootstrap(document, [‘app’]), a qual inicializamos o AngularJS? Pois bem, este “app” é o nome do modulo javascript com o qual o AngularJS será inicializado. Vamos criar este módulo. Abra o arquivo “app.js” e adicione o seguinte comando:

‘use strict’;

angular.module(‘app’, []);

O ‘use strict’ não tem nada a ver com o AngularJS. Você pode, inclusive, ignorá-lo, entretanto é uma boa prática a ser seguida no desenvolvimento de seus scripts, sendo uma novidade especificada pela ECMAScript 5, cujo principal benefício é prevenir e aumentar o número de exceções num contexto de execução de scripts inseguros (UNSAFE), além de retirar algumas features confusas ou mal implementadas no próprio Javascript. Enfim, na dúvida, pode colocar a linha nos seus scripts que não estará fazendo besteira.

A segunda linha é a criação do módulo com dois argumentos: o nome do módulo e uma coleção de módulos dependentes. O primeiro já é alto explicativo, já o segundo é mais ou menos como nas linguagens orientadas a objeto, onde você pode declarar outros pacotes (geralmente no início do código) que contenha objetos que você irá utilizar em sua classe.

Neste caso, estamos sozinhos, mas em posts futuros certamente teremos dependências para declarar.

Vamos amadurecer este script um pouquinho mais.

‘use strict’;

angular.module(‘app’, [])

.factory(‘FeedService’,[‘$http’,function($http){

return {

getFeeds : function(url){

return $http.jsonp(‘//ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=50&callback=JSON_CALLBACK&q=’ + encodeURIComponent(url));

}

}

}]);

Agora complicou, certo? Nem tanto assim. Vamos com calma que você chega lá.

A primeira mudança a observar é que retiramos o ponto-e-vírgula do final do módulo e acrescentamos uma factory. Factory é uma das formas que o AngularJS lhe dá para registrar serviços. Estes serviços seriam algo como criar um método público que suporte injeção de dependência para utilização em seu módulo. No caso, há várias maneiras de criar um serviço, sendo que a factory é a forma a qual retornamos o resultado da função para quem a chama.

O que importa é que o primeiro argumento é o nome do serviço. Já o segundo é uma função que suporta injeção de dependência – este é um dos conceitos chaves do AngularJS e explicaremos melhor isto no próximo artigo.

No nosso caso, estamos injetando o serviço $http para utilizarmos neste nosso método. Todos estes serviços que começar com $ são serviços cores que estão presentes na biblioteca do AngularJS. Tem um monte de coisas bacanas. Para saber, este é um serviço que facilita a comunicação com servidores via HTTP através de XMLWebRequest ou JSON.

Depois disto, declaramos que iremos retornar para a factory o resultado de uma função chamada “getFeeds”, que espera a url do feed como argumento.

A execução desta função é a utilização do serviço $http, que injetamos anteriormente, que por ventura irá chamar um web service da Google – que com isto resolveu o problema de podermos ler um feed unicamente através de Javascript (thanks again, Google!).

Agora que já temos o serviço para leitura do feed, é só criar o “FeedController” e correr para o abraço!

‘use strict’;

angular.module(‘app’, [])

.factory(‘FeedService’,[‘$http’,function($http){

return {

getFeeds : function(url){

return $http.jsonp(‘//ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=50&callback=JSON_CALLBACK&q=’ + encodeURIComponent(url));

}

}

}]);

function FeedController($scope, FeedService){

$scope.uri = function(uri){

FeedService.getFeeds(uri).then(function(res){

$scope.feeds= res.data.responseData.feed.entries;

});

};

}

Praticamente em todos os controllers você terá o $scope. Como já dito, é ele que faz o meio de campo da model, entre a view e o controller. Neste caso, o javascript irá popular a coleção feeds, como vimos no desenvolvimento da view. Além disto, você se recorda que iniciamos uma função lá no ng-init? Também é aqui que receberemos e trataremos esta função.

Logo na assinatura, você injeta o serviço responsável pela model ($scope) e o serviço responsável por trazer o retorno à lista dos feeds para nós.

Na próxima linha, declaramos a função daquilo que chamamos de “construtor” (uma função de inicialização – chamada uri – que recebe a url do feed através da view).

Dentro deste “construtor”, chamamos o serviço que criamos a poucos instantes e populamos o resultado na model $scope.feeds.

Este “then” é por que o serviço $http é assíncrono por natureza, logo o “then” especifica um call-back que popula a model assim que o retorno lá na Google for processado (o retorno vem através da response – a qual optamos aqui por chamar de “res”).

Acho que agora ficou simples, não?

Se não conseguiu montar o exemplo, não se preocupe, coloquei tudo no plunker para você ver na prática.

Este post ficou bem longo, mas é uma bela iniciação ao AngularJS, que faz muito mais do que vimos aqui. Eu particularmente estou bastante empolgado com as múltiplas possibilidades do framework e a partir deste ponto creio que você estará preparado para atingir novos voos em seus estudos.

Ademais, o que criamos aqui pode ser utilizado em uma infinidade de aplicações, e tudo isto usando somente HTML + Javascript. Logo pode ser acoplado em sua linguagem preferida. Espero que tenha gostado!

Plunker:  http://plnkr.co/edit/SkQPAsK8oRQSX1GDMAfA

Etiquetado , , , , , , , , ,

2 pensamentos sobre “AngularJS: Básico + RSS Feed Reader

  1. […] Também iremos baixar o LABjs para carregar nossos scripts assincronamente, do mesmo modo que fizemos nos posts anteriores. […]

  2. Rodrigo Dias disse:

    Muito bom o exemplo meu nobre. Você saberia alguma solução para puxar a imagem de um feed. Estou há dias tentando e até agora não consegui…

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: