WebSocket com Node.js – Parte 2: Temporizador

Este post se refere a segunda parte da série WebSocket com Node.js, clique aqui e confira a primeira parte.

Também vamos utilizar o AngularJS para o front-end, afinal há muitas dúvidas de como integrá-lo com o Node.js – mais especificamente falando com o socket.io.

A ideia aqui é que um servidor registre a data e hora e guarde numa lista a cada um segundo. Cada vez que isto acontecer, o servidor irá emitir a lista com todos os horários registrados. Deste modo, todos os clientes que estiver aguardando esta lista irão recebê-la a cada segundo. O maior desafio, que nem é tão grande assim, é que cada cliente que entre depois de algum tempo e que também queira receber esta lista, consiga receber não só as notificações de tempo a partir do momento que passou a se comunicar com o servidor, mas de todo o histórico passado.

Inicialmente, vamos ao setup! Lembrando que você precisa ter o Node.js instalado em sua máquina (se não tiver, é só baixar e instalar – não tem segredo).

Crie um diretório com qualquer nome (eu chamei de simple-clock) e lá dentro crie um arquivo chamado package.json

package.json

Neste arquivo, adicione os módulos que iremos trabalhar com as seguintes linhas:

{

  name: “application-name”,

  version: “0.0.1″,

  dependencies: {

    express: “3.4.7″,

    “jade”: “*”,

    “socket.io”: “*”

  }

}

O express é a framework para criarmos rotas e o nosso servidor – é o módulo mais famoso do Node.js, base para praticamente toda a aplicação web.

O jade é a template-engine que utilizaremos para desenhar a nossa tela para o client. O socket.io dispensa apresentações.

Agora vá no Command Prompt, navegue até o diretório que você criou (no mesmo local onde encontra-se o arquivo) e digite:
npm install

O gerenciador de módulos do Node.js irá fazer o download de todos os módulos que declaramos no arquivo package.json, dentro de um diretório chamado node_modules.

Ao término, crie também mais três diretórios na mesma estrutura: public, routes e views. Dentro do diretório public, crie o diretório javascripts. Entre neste diretório e crie um arquivo chamado app.js (depois eu explico).

Entre no diretório routes e crie o arquivo index.js.

Entre no diretório views e crie os arquivos layout.jade e index.jade.

Na raiz do diretório (no local onde encontra-se o arquivo package.json) e crie o diretório app.js.

Tudo deverá ficar assim:

- diretorio-raiz
                - node_modules
                               - express
                               - jade
                               - socket.io
                - public
                               - javascripts
                                               -
app.js
                - routes
                               - index.js
                - views
                               - index.jade
                               - layout.jade
                - app.js
                - package.json

Vamos começar a editar o arquivo app.js!

app.js

Este arquivo é o principal do Node.js! Ele adiciona as dependências, configura as rotas e o servidor, como domínios e portas que serão utilizadas.

O código completo deste arquivo ficará assim:

var express = require('express');
var routes = require('./routes');
var http = require('http');
var path = require('path');
var SocketIO = require('socket.io');

var app = express();
var server = http.createServer(app);
var io = SocketIO.listen(server);
var timeHistory = [];

setInterval(function(){
    timeHistory.unshift(new Date().toJSON());
    io.sockets.emit('time', {time: timeHistory});
}, 1000);

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

app.get('/', routes.index);

server.listen(3333);

Nas cinco primeiras linhas, temos a importação dos módulos que o nosso servidor Node.js utilizará. Você já foi apresentando ao express e ao socket.io. O routes é apenas um atalho para o diretório que criamos anteriormente (que hospeda o arquivo index.js). O http e o path são módulos built-ins do Node.js: o primeiro abstrai um monte de complexidade que envolve o protocolo http e disponibiliza métodos para deixar a nossa vida mais fácil. O segundo é um facilitador para trabalhar com caminhos físicos, uma vez que o pessoal que programa para a web sempre se atrapalha um pouquinho nisto, com a questão dos recursos relativos e absolutos.

Depois temos o seguinte

var app = express();
var server = http.createServer(app);
var io = SocketIO.listen(server);
var timeHistory = [];

A primeira linha é uma variável para o express já inicializado. A segunda linha cria o servidor a partir do express e retorna este servidor na variável server. A terceira linha cria uma instância do Socket vinculado a este servidor que criamos na linha anterior. Por fim, temos uma variável que irá armazenar cada tick de nosso temporizador, registrando todos os horários. É esta variável que será emitida para todos os nossos clientes que estarão aguardando esta mensagem.

Na sequência, temos TODO o funcionamento do socket no servidor! Simplesmente configuramos um cara para ficar executando a função da cada 1000 milissegundos (1 segundo). Esta função apenas pega a data e hora atual e armazena no topo da lista (a variável timeHistory). Depois disto, ela pega esta lista e emite (“emit”) para todos os clientes que estiverem plugados (“on”) no canal “time” (primeiro argumento).

setInterval(function(){
    timeHistory.unshift(new Date().toJSON());
    io.sockets.emit('time', {time: timeHistory});
}, 1000);

Agora vamos a última parte do servidor:

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

app.get('/', routes.index);

server.listen(3333);

Estas configurações se referem ao client. Estamos dizemos onde encontra-se as nossas views (os arquivos com a extensão jade) e qual é a view engine que irá interpretar estes arquivos.

Também iremos utilizar o router e definimos qual é o diretório que ficará público.

Por fim, temos a rota e a porta! Todos aqueles que caírem na raiz serão redirecionados para o index, na porta que estaremos escutando. Neste caso, se estiver rodando de sua máquina, por padrão o endereço será http://localhost:3333.

Vamos agora editar o arquivo index.js, que está dentro da pasta routes.

routes/index.js

exports.index = function(req, res){
  res.render('index', { title: 'Express' });
};

É bem simples: exportamos a função index, que recebe dois argumentos (request e response), responsável por renderizar a viewindex”, que será criada utilizando o jade no próximo passo. Como argumento, passamos o parâmetro “title”, configurado como “Express”, mas você pode colocar o nome que bem entender só para ver como funciona. Você verá a utilização deste parâmetro mais abaixo.

Do lado do servidor, o socket está pronto. Vamos começar com o client agora.

views/layout.jade

Edite o arquivo layout.jade que está na pasta views.

doctype 5
html(ng-app="app")
  head
    title= title
  body
    block content
    script(src='/socket.io/socket.io.js')
    script(src='http://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js')
    script(src='/javascripts/app.js'

Aqui acho que nem vale perder tempo explicando. Simplesmente é um template básico que servirá de sandbox para todos aqueles que quiserem utilizá-lo. Como analogia, é praticamente a mesma coisa da master page / layout template de diversas linguagem.

Talvez você somente estranhe o fato de que estamos desenhando o nosso HTML sem as marcações padrões. Isto, basicamente, é o que o Jade faz: interpreta uma sintaxe e uma hierarquia e simplesmente monta o HTML para você.

Observe que temos também a importação dos scripts socket.io.js, do AngularJS (que você já ouviu falar bastante aqui no blog) e do app.js, que ainda não criamos.

O AngularJS seria opcional ao nosso propósito, mas acho que muitos tem dúvidas (ao menos, eu tinha) de como trabalhar com WebSocket, diretivas e bindings com o AngularJS.

Todavia, nesta série sobre WebSocket teremos outros exemplos sem utilizar o AngularJS, para quem não tem interesse em aprender a framework.

Agora vamos editar o index.jade

views/index.jade

extends layout

block content
  h1= title
  p Welcome to #{title}

  div#container(ng-controller="SocketController")
    ul#messages
      li(ng-repeat="item in messages")
          {{item}}

 

A primeira palavra, extends, recebe o layout que criamos no passo anterior. Isto indica que podemos ter quantos layouts quisermos. Uma vez que utilizamos e declaramos um layout, o jade injeta todo o conteúdo descrito por ele nesta nossa view.

Na sequência temos o block. O block é uma função de associação para o jade saber onde ele deverá inserir o conteúdo da view dentro do layout que criamos previamente.

Outro ponto a destacar é o #{title}. Lembra do argumento “title” que criamos no arquivo index.js na routes? É assim que ele passa a informação do js para o html.

Por fim, passamos o controller SocketController para a diretiva do AngularJS. Este controller está dentro do arquivo “public/javascripts/app.js”, que iremos criar na sequência. Aqui estamos informando para o compilador do AngularJS que tudo o que está dentro desta div poderá fazer o binding de informações do Controller.

No caso, iremos fazer um looping na model “messages” do controller e adicionar uma li para cada mensagem encontrada.

Para finalizar, falta só o arquivo app.js, que está no public/javascripts.

public/javascripts/app.js

angular.module('app', []).
    factory('socket', function($rootScope){
        var socket = io.connect();

        return {
            on: function(eventName, callback){
                socket.on(eventName, function(){
                    var args = arguments;
                    $rootScope.$apply(function(){
                        callback.apply(socket, args);
                    });
                });
            },

            emit: function(eventName, data, callback){
                socket.emit(eventName, data, function(){
                    var args = arguments;
                    $rootScope.$apply(function(){
                        if(callback){
                            callback.apply(socket, args);
                        }
                    });
                });
            }

        };
    });

function SocketController($scope, socket){
    $scope.messages = [];

    socket.on('time', function(data){
        $scope.messages = data.time;
    });
}

O ponto mais chocante em relação ao que você viu de AngularJS neste blog é a necessidade de criar uma factory do Socket.IO para podermos utilizarmos em nosso Controller, que é quando a model conversa com o socket.

A analogia é criarmos uma “classe” (factory) com dois métodos: “on” e “emit” para utilizarmos em nosso controller. Nesta “classe”, estamos “instanciando” o script chamado socket.io.js e utilizando-o para implementar estes dois métodos.

Observe também que estamos criando uma variável chamada socket, que é criada a partir da conexão com o servidor (io.connect()). Se tivéssemos criando um client para escutar um outro servidor, que não fosse ele mesmo, poderíamos passar a url no método connect.

Depois temos o controller de fato, recebendo a model como parâmetro e esta “classe” que acabamos de criar.

Ali está bem simples: todas as vezes que o servidor emitir uma mensagem no canal “time”, populamos a model “messages”, que já está em binding lá no index.jade.

Ou seja, o client está escutando o tempo inteiro o canal “time”… Se o servidor emitir esta mensagem, ele toma a ação de sobrescrever a model messages.

É isto! Agora é só abrir o prompt, entrar na raiz do projeto e digitar:
node app.js

Isto irá inicializar o nosso servidor.

Abra o browser e digite http://localhost:3333.

Se tudo deu certo, você passará a visualizar a data e hora, segundo a segundo. Experimente abrir diversas abas do browser e alterar o tempo de atualização no socket do lado do servidor, enfim, fuce bastante!

Caso tenha dado errado, clone o meu repositório no github e faça a comparação:
https://github.com/evenancio/simple-clock-websocket-angularjs

Na próxima parte, vamos fazer um painel de atendimento, semelhante àqueles que encontramos em hospitais.

Até a próxima!

Etiquetado , , , , , , , , ,

Generators com Node.js + koa e C#

Agora que você aprendeu tudo sobre generators neste blog, temos uma surpresa bem bacana!

Uns carinhas que criaram um tal de express (que é simplesmente o módulo mais sensacional e utilizado do Node.js), resolveram que com esta especificação do ECMAScripts, temos um novo mundo para ser explorado, mais limpo e performático, que resolverá em muito o mundo da programação assíncrona e da bagunça causada pelos callbacks (uma das coisas mais ilegíveis do Javascript). Foi assim que nasceu o co, um framework para generators.

Baseado no co, eles acabam de lançar o koa – a nova geração de framework web para Node.js – ainda é beta, afinal o ECMAScript 6 nem foi publicado ainda, mas já dá para ver o que vem por aí.

E é por isto que dei tantas voltas no artigo anterior… Para mim é muito importante que você entenda o que são os Generators, pois ele é a base de uma possível revolução que haverá no mundo do Node.js muito em breve.

Agora vamos ao nosso exemplo para frisar a ideia!

Vamos começar com o C#, uma das linguagens mais robustas e impressionante que eu já tive o prazer de trabalhar. Fazia muito tempo que não escrevia nada nesta linguagem e para mim foi como um reencontro com um velho amigo.

Vou criar um projeto de teste unitário. Tudo se resolve no código abaixo, que está no mesmo arquivo! É só copiar no seu Visual Studio e executar o teste.

* Dica: faça um favor e vá debugando linha-a-linha e observe a diferença da execução a partir do método ObterPessoasComYield e ObterPessoasSemYield.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;

namespace YieldTest{

  [TestClass]
  public class UnitTest1{

    [TestMethod]
    public void Teste(){
        var pessoasComYield = ObterPessoasComYield();

        foreach (var pessoa in pessoasComYield)
          Console.WriteLine(“Com Yield -> Autor: {0} {1}”, pessoa.Nome, pessoa.Sobrenome);

        var pessoasSemYield = ObterPessoasSemYield();

        foreach (var pessoa in pessoasSemYield)
          Console.WriteLine(“Sem Yield -> Autor: {0} {1}”, pessoa.Nome, pessoa.Sobrenome);
    }

    public IEnumerable<Pessoa> ObterPessoasComYield(){
      yield return new Pessoa { Nome = “José”, Sobrenome = “Saramago” };
      yield return new Pessoa { Nome = “Fiódor”, Sobrenome = “Dostoiévski” };
      yield return new Pessoa { Nome = “Liév”, Sobrenome = “Tolstói” };
      yield return new Pessoa { Nome = “Victor”, Sobrenome = “Hugo” };
      yield return new Pessoa { Nome = “Fernando”, Sobrenome = “Pessoa” };
    }

    public IEnumerable<Pessoa> ObterPessoasSemYield(){
      return new List<Pessoa>{
        new Pessoa { Nome = “José”, Sobrenome = “Saramago” },
        new Pessoa { Nome = “Fiódor”, Sobrenome = “Dostoiévski” },
        new Pessoa { Nome = “Liév”, Sobrenome = “Tolstói” },
        new Pessoa { Nome = “Victor”, Sobrenome = “Hugo” },
        new Pessoa { Nome = “Fernando”, Sobrenome = “Pessoa” }
      };
    }
  }

  public class Pessoa{
    public string Nome { get; set; }
    public string Sobrenome { get; set; }
  }
}

Vamos fazer exatamente a mesma coisa, só que com o Node.js e o koa. Um detalhe importante: para rodar este código, você precisa baixar a versão 11.9, considerada uma versão instável, e rodar a sua aplicação com a instrução –harmony (em referência ao ECMAScript 6).

Vou fazer um step-by-step em 10 passos para simplificar.

1)      Baixe a versão 11.9 do Node.js e instale.

2)      Reinicie o computador

3)      Crie um diretório onde você quiser, eu chamei de yield-sample.

4)      Dentro do diretório, crie um arquivo chamado package.json

5)      Adicione o seguinte conteúdo:

{
  “name”: “yield-sample”,
  “version”: “0.0.1″,
  “dependencies”:{
    “koa”:”*”
  }
}

6)      Vá no Command Prompt, no diretório onde está este arquivo, e digite:
npm install

7)      Crie um arquivo chamado app.js e adicione o seguinte conteúdo:

var koa = require(‘koa’);
var app = koa();

app.use(
  function *teste(){
    var pessoasComYield = yield pessoas.map(obterPessoasComYield);
    pessoasComYield.forEach(function(pessoa){
        console.log(‘Com Yield -> Autor: ‘ + pessoa.nome + ‘ ‘ + pessoa.sobrenome);
    });

    var pessoasSemYield = pessoas;
        pessoasSemYield.forEach(function(pessoa){
        console.log(‘Sem Yield -> Autor: ‘ + pessoa.nome + ‘ ‘ + pessoa.sobrenome);
    });
  }
)

function *obterPessoasComYield(pessoa){
  return yield pessoa;
}

var pessoas = [
  { nome: "José", sobrenome: "Saramago" },
  { nome: "Fiódor", sobrenome: "Dostoiévski" },
  { nome: "Liév", sobrenome: "Tolstói" },
  { nome: "Victor", sobrenome: "Hugo" },
  { nome: "Fernando", sobrenome: "Pessoa" }
];

app.listen(8080);

8)      Volte ao Command Prompt no local onde está o arquivo app.js e digite:

node –harmony app.js

9)      Abra o seu browser e digite o endereço http://localhost:8080

10)   Volte ao Command Prompt e você deverá ver todo o log impresso na janela

O comportamentos será semelhante ao C#.

É importante ressaltar que o koa não é somente generator e yield – na verdade, é algo bem maior do que isto! – mas ele faz uso destas features para trazer uma nova revolução, que é resolver a sintaxe do callbacks, registro de middlewares e programação assíncrona.

Este * antes de alguns métodos é uma síntese destas novas funcionalidades. Ele é semelhante as funcionalidades do async do C# 4.5, sendo que o yield que aparece dentro das funções callers, da mesma forma é equivalente ao await.

Este GIF mostra o controle de execução do fluxo de uma maneira bem interessante com estes recursos.

Conforme formos evoluindo, vamos explorando mais estes conceitos. Por enquanto ainda está tudo no princípio, com alguns bugs sendo resolvidos. Ou seja, ainda é muito cedo para utilizar num projeto, mas para labs dá para aprender bastante. Creio que mais alguns meses e isto não será mais uma trava para nós.

Bem-vindos a uma nova revolução!

Etiquetado , , , , , , , ,

Entendendo Generators, Iterators e Yield

Primeiramente, estamos falando de recursos de linguagens de programação, ou seja, este assunto é para arquitetos e coders.

Em segundo lugar, Generators e Iterators são a mesma coisa – por tanto, a partir daqui utilizarei somente a palavra Generators e você já sabe que estou falando dos dois!

Isto posto, vamos em frente.

Generator é uma espécie de função especial para controlar o comportamento de um looping numa coleção de itens. A grosso modo, é uma função que retorna uma lista (array ou vetores), como aquelas funções básicas que as vezes você cria para alimentar uma coleção de objetos.

A diferença está no comportamento: enquanto no modelo tradicional sua função de looping adiciona todos os itens numa coleção de uma única vez, e somente quando esta operação terminar a função retorna esta coleção completamente carrega, com o uso de generators os itens são entregues para a função chamadora (caller) um de cada vez através do yield, conforme a coleção for sendo utilizada por este caller.

Isto garante melhor utilização da memória e incrementa a performance em quase todas as situações, uma vez que os itens são processados em menor quantidade. Há cenários a qual os generators não são bem-vindos, como em situações de recursividade, ou quando é necessário retornar uma outra coleção de itens ou quando exceções são disparadas dentro do generator – mas estas são situações de exceção.

Em resumo, os generators lembram muito uma função, mas se comportam como iteradores.

Para exemplificar, segue o modelo tradicional:

FUNCAO Looping Tradicional
    VARIÁVEL Coleção de Números
    CONTAR DE 1 Até 5 NA VARIÁVEL Número
        ADICIONAR Número NA Coleção de Números
        LOGAR “Looping Tradicional” CONCATENADO COM Número
    RETORNAR Coleção de Números

FUNCAO Imprimir Valor do Looping
    VARIÁVEL Resultado IGUAL Á Looping Tradicional
    PARA CADA Item NO Resultado
        IMPRIMIR Item
        LOGAR “Imprimir Valor” CONCATENADO COM Item

Quando a função caller “Imprimir Valor do Looping” for executada, será logado o seguinte:

Looping Tradicional 1
Looping Tradicional 2
Looping Tradicional 3
Looping Tradicional 4
Imprimir Valor 1
Imprimir Valor 2
Imprimir Valor 3
Imprimir Valor 4

Isto por que primeiro foi carregado toda a coleção da função “Looping Tradicional” de uma única vez. Depois ela voltou esta coleção para a função caller, que leu cada um dos resultados e imprimiu o valor de cada um dos números.

Vamos ver como ficaria o mesmo exemplo com Generator:

FUNCAO Looping Generator
    VARIÁVEL Coleção de Números
    CONTAR DE 1 Até 5 NA VARIÁVEL Número
        YIELD ADICIONAR Número NA Coleção de Números
        LOGAR “Looping Generator” CONCATENADO COM Número
    RETORNAR Coleção de Números

FUNCAO Imprimir Valor do Looping
    VARIÁVEL Resultado IGUAL Á Looping Generator
    PARA CADA Item NO Resultado
        IMPRIMIR Item
        LOGAR “Imprimir Valor” CONCATENADO COM Item

O resultado ficará assim:

Looping Generator 1
Imprimir Valor 1
Looping Generator 2
Imprimir Valor 2
Looping Generator 3
Imprimir Valor 3
Looping Generator 4
Imprimir Valor 4

Ou seja, conforme os itens forem sendo solicitados na função caller, eles são retirados da função Generator, um à um, graças ao YIELD, que guarda a posição de retirada do item para voltar daquele ponto, assim que for solicitado pelo caller.

Este recurso não é novidade desde muito tempo para uma série de linguagens de programação. De fato, ele surgiu entre 1974 e 1975 na linguagem CLU. No C#, temos o recurso nativo disponível desde 2005, com o lançamento da .NET Framework 2.0. Em outras linguagens o lançamento é mais recente, como no caso do PHP 5.5. Temos ainda versões nativas no Python e no Ruby.

Uma coisa para deixar toda a comunidade bem empolgada é que o recurso entrou na especificação do ECMAScript 6 (conhecida como Harmony). Isto significa que o Javascript terá uma versão nativa em breve. Sendo assim, o pessoal da Vision Media não perdeu tempo e lançou o co para Node.js, módulo que também implementa os Generators, de acordo com a ECMAScript 6!

Curiosamente não temos o recurso nativo no Java – faço votos para que chegue muito em breve.

No próximo post, vamos ver alguns exemplos.

Etiquetado , , , , , , , , , , ,

WebSocket com Node.js – Parte 1: Introdução ao Socket.IO

Para quem não conhece, o WebSocket era parte da especificação do HTML5 que descreve a comunicação bidirecional (envie e recebe mensagens) entre canais através de TCP/IP. Não faz mais parte do HTML5 e ganhou vida própria, sendo o protocolo padronizado pela IETF como RFC 6455 e a API padronizada pela W3C.

Eu não vou perder muito tempo falando do WebSocket por que já tem muitos sites que fizeram isto de maneira clara e inteligente (como o Wikipedia e o Html5 Rocks), mas posso dar o meu palpite: esta é uma das features mais incríveis de todos os tempos quando falamos do universo web!

Dá para fazer tantas coisas bacanas que acredito que, num futuro muito próximo, quando este negócio estiver 100% pronto e aderente, não conseguiremos sequer deixar de pensar em WebSocket nos desenhos de nossas soluções.

Não que a ideia seja totalmente nova – pois sempre demos o nosso jeitinho através do Flash, do Long Pulling e do Ajax – que também são eficientes e possuem melhor compatibilidade com o nosso atual ambiente (visto que o WebSocket é muito novo), porém não sem cobrar o seu preço: o que antes era caro e despadronizado, agora se torna barato, uniforme e eficiente.

Pesquisa e conclua por si só!

O que eu irei falar é sobre uma implementação do WebSocket sensacional para Node.js e Javascript, chamada Socket.IO: ele não só incorpora tudo o que é necessário para nos comunicarmos através de WebSocket, mas também oferece alternativas implícitas caso os ambientes utilizados não oferecem suporte à WebSocket, por serem antigos.

E melhor: tudo isto através de uma interface extremamente simplificada e fácil de entender. Isto coloca a API como uma das mais populares quando falamos em termos de WebSocket. Atrelada aos principais problemas que o Node.js resolve, este duplo combo é quase que imbatível.

Portanto, coders de modo geral, deixem sua camisa de lado e seja bem-vindo ao mundo da programação poliglota, onde utilizamos o que há de melhor disponível no mercado para desenharmos uma solução – como oposição a ficar refém de um único vendor, hasteando a bandeira de que ali reside toda a verdade.

Se você é um destes, eis aqui uma boa porta de entrada para o universo do Node.js – na trilha de desenvolvimento de software, esta já considerada uma das melhores tecnologias dos últimos anos e grandes empresas estão adotando-a, como a Microsoft e o seu Visual Studio Online, que foi criado com Node.js, e como a PayPal, que abandonou o Java para utilizar Node.js em seu principal produto.

Se você não sabe nada de Node.js e está com preguiça de pesquisar, vou resumir: os caras deram um jeito de compilar Javascript do lado do servidor – ponto-final. Aquilo que você fazia no client, agora consegue fazer também no server (exatamente da mesma forma).

Neste caso, a API (que no Node.js é chamada de módulo) Socket.IO é a mesma no client e no server (e isto é bem bacana).

Enfim, instale este negócio agora, senão não irá conseguir construir rodar os códigos que iremos desenvolver nesta série de artigos (ao menos que crie uma conta na Cloud9 – uma IDE Online muito boa que eu utilizo bastante no meu dia-a-dia).

Mas vamos voltar ao Socket.IO. Como já dito, ele abstrai e utiliza o WebSocket como o seu principal mecanismo. E se não for compatível com o meu ambiente? Então o próprio Socket.IO se encarrega de providenciar outros mecanismos de transporte, como fallback:

  • Adobe® Flash® Socket
  • AJAX long polling
  • AJAX multipart streaming
  • Forever Iframe
  • JSONP Polling

Você não precisa se preocupar, por que tudo é transparente para você! Dá para entender por que o módulo é tão popular – pois garante compatibilidade entre diferentes clientes e servidores, sejam através de dispositivos móveis ou desktops, independente de versão de browser e sistema operacional.

E como o WebSocket funciona?

Basicamente ouvimos e transmitimos mensagens. Neste caso, você tem basicamente dois métodos que vão ditar todo o restante: on e emit. Simples assim. No on você deixa o servidor ou o client aguardando uma mensagem chegar e no emit você transmite esta mensagem.

É isto aí amigo: para você que tem tanto trauma de Sockets, só precisa entender estes dois métodos! Se entendeu, você já concluiu 85% do curso de Socket.IO!

Vamos a um fluxo básico para você frisar melhor (se refere ao registro em um chat).

Imagem

Cara, é isto! Você simplesmente envia (emit) uma mensagem para alguém que esteja esperando (on) por ela. Quando ele recebe, ele faz algo com isto e pode, inclusive, responder com uma mensagem (emit) para o cliente, caso ele esteja aguardando (on) por um retorno.

Na próxima parte nós vamos ver um aplicativo bem simples que demonstra todos os estes conceitos na prática!

Até lá!

Etiquetado , , , , , , ,

AngularJS e Diretivas: Como criar um custom control básico

Uma das muitas features que o AngularJS entrega é a possibilidade de construir nossos próprios controles para reaproveitamento, devido a extensibilidade das diretivas (conceito que foi bastante explorado neste post).

Mas o que seria um custom control? Sabe aqueles controles de ratings, formulários de logon, efeitos de carrossel, etc? Estes são exemplos de controles customizados.

Um custom control sempre encapsula uma saída visual através de diretivas próprias, portanto temos o combo Html+Javascript na construção do recurso.

Você se recorda o que é diretiva? No AngularJS temos uma série delas em nosso HTML, todas elas iniciam com ng-*. Você já viu a ng-app, ng-controller, ng-repeat e ng-model em um de nossos artigos. Estas são diretivas built-in, ou seja, já vem com o AngularJS. Mas não estamos restritos a elas, sendo que o AngularJS fornece os mecanismos para criar as nossas próprias.

É através da criação destas diretivas que iremos construir os nossos customs controls.

Para este artigo, vamos criar um controle básico, apenas para nos aprofundarmos mais um pouco no AngularJS.

O controle será um formulário de login contendo um campo para usuário, senha, um botão para efetuar o login e um botão de “esqueci a minha senha”. O comportamento será bem simples e não intenta ser aquilo que você deveria implementar na realidade num controle com este foco.

Antes de chegar a tanto, vamos começar do mais simples para que o entendimento fique claro.

Crie um diretório qualquer e coloque os seguintes arquivos:

+ index.html
+ app.js
+ loginCustomControl.js

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

Edite o seu index.html e deixe o da seguinte forma:

<!DOCTYPE html>

<html>

<head>

<title>AngularJS .::. Simple Login Control</title>

</head>

<body>

<div>

<h1>Custom Control Sample</h1>

<div login-control></div>

</div>

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

<script>

$LAB

.script(“https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js&#8221;)

.script(‘loginCustomControl.js’)

.script(‘app.js’)

.wait(function(){

angular.bootstrap(document, ['app']);

});

</script>

</body>

</html>

A novidade, por enquanto, é a adição da diretiva login-control dentro de uma div. É ela que contém o template Html que precisaremos renderizar na tela. Ou seja, esta diretiva levará a uma linked function que realizará este trabalho para nós. O resto das linhas você já conhece – caso não conheça, clique aqui para aprender.

Vamos agora editar o loginCustomControl.js.

‘use strict’;

angular.module(‘app.directives.customcontrols’, [])

.directive(‘loginControl’, function() {

return {

template: ‘<h1>Meu primeiro control customizado!</h1>’

};

});

Aqui você declara um módulo do AngularJS chamado app.directives.customcontrols, mas você poderia colocar o nome que quisesse… Isto é mais ou menos como se fosse o nome da package para o Java ou .NET. O segundo argumento é uma coleção de módulos dependentes, neste caso não temos nenhum.

Dentro deste módulo também declaramos a nossa diretiva customizada. O primeiro argumento é o nome da diretiva. O segundo é a função que deverá ser executada para renderizar o controle, a qual retorna todos os argumentos de configuração a qual o compilador do AngularJS utilizará para que este controle seja exibido e utilizado da forma desejada.

No caso acima, estamos fazendo algo bem simples, que é configurar um template fixo contendo apenas um item de cabeçalho <h1>.

Aqui reside o primeiro ponto de atenção, muito importante e por vezes confuso para quem está começando: no javascript a diretiva é chamada loginControl, mas quando declarada no Html a mesma é separada por hífen, ficando login-control.

Isto ocorre por que o compilador do AngularJS possui uma etapa de normalização para associar uma diretiva com o seu equivalente na DOM (ou seja, no HTML). A ideia aqui é fazer o match entre diretiva no script e diretiva na DOM. Ocorre que a primeira é case sensitive e por padrão é camel case (letra do primeiro nome minúscula e dos próximos nomes maiúsculas). Já a DOM é case-insensitive, logo escrever login-control, LOGIN-CONTROL, Login-Control ou mesmo LoGiN-cOnTrOl da na mesma. Para resolver este problema, o AngularJS converte as palavras da DOM separadas por hífen para o camel case e assim faz a associação.

Por fim, para finalizar, falta editar o arquivo inicializador, o app.js, que deverá ficar assim:

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

Este está bem fácil se você acompanhou os outros artigos: o primeiro argumento é o nome do módulo, o segundo é uma coleção de módulos que este tem dependência. A associação é simples de estabelecer: se a minha página tem uma diretiva chamada ng-app configurada com este módulo (“app”) e esta mesma página utiliza o controle customizado, temos que declarar o módulo onde ele está como uma dependência.

Isto já é o suficiente para funcionar. É só abrir o index.html e provavelmente dará tudo certo. Se não der, veja o que fez de errado comparando com o Plunker desta versão: http://plnkr.co/edit/appmltKl0KYQ1m1rhm32?p=preview

Vamos fazer a primeira alteração na diretiva… Mais acima eu frisei que a diretiva login-control está dentro de uma div. Mas nós podemos também criar diretivas que não precisam exibir como atributos dentro de um elemento já existente. Temos como configurá-la para ela poder ser escrita como um elemento HTML próprio! Ainda não entendeu? Pois altere o seu Html para isto:

<!DOCTYPE html>

<html>

<head>

<title>AngularJS .::. Simple Login Control</title>

</head>

<body>

<div>

<h1>Custom Control Sample</h1>

<login-control></login-control>

</div>

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

<script>

$LAB

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

.script(‘loginCustomControl.js’)

.script(‘app.js’)

.wait(function(){

angular.bootstrap(document, ['app']);

});

</script>

</body>

</html>

É isto que estou dizendo! Você acabou de extender o Html e criou uma nova TAG para ser utilizada! Claro que ainda não pronto… Ainda precisamos instruir a diretiva sobre este comportamento, o que faremos alterando o loginCustomControl.js para o seguinte:

‘use strict’;

angular.module(‘app.directives.customcontrols’, [])

.directive(‘loginControl’, function() {

return {

          restrict: ‘E’,

template: ‘<h1>Meu primeiro control customizado!</h1>’

};

});

A novidade é esta propriedade restrict, que é configurada com um valor ‘E’. Este ‘E’ vem de Elemento, ou seja, você está dizendo que a diretiva deve funcionar restritamente como um elemento. O padrão é ‘A’, de Atributo (a forma como estávamos declarando diretivas até agora). Você também pode aceitar as duas formas, basta configurar como ‘AE’.

Veja como ficou no plunker: http://plnkr.co/edit/1w3qpZ4cquJqbaYDiVeL?p=preview

Vamos mudar mais um pouco e tirar aquele <h1>Meu primeiro control customizado!</h1> do arquivo javascript para um template no próprio HTML.

Crie um arquivo chamado loginCustomControlTemplate.html e adicione a seguinte linha:

<h1>Meu primeiro control customizado!</h1>

Agora altere o loginCustomControl.js para recebermos este template em arquivo, ao invés de estar engessado no script. Repare que abandonamos a propriedade “template” e passamos a utilizar a “templateUrl”.

‘use strict’;

angular.module(‘app.directives.customcontrols’, [])

.directive(‘loginControl’, function() {

return {

restrict: ‘E’,

          templateUrl: ‘loginCustomControlTemplate.html’

};

});

Plunker: http://plnkr.co/edit/XxSfN5scJtMR5jYtxbTX?p=preview

Agora vamos dar um passo mais significativo, pois de tela de login até agora só vimos o título!

Primeiro, vamos alterar o arquivo loginCustomControl.js para trazermos uma nova configuração: a entrada do escopo (scope). Isto por que eu quero fazer o bind com as minhas informações de logon (usuário, senha, método para entrar e método para recuperar a senha). A ideia é criar uma entrada onde a página que irá utilizar este controle possa passar um controller que implemente estas informações de logon.

Tentando fazer uma analogia com linguagens orientadas a objetos como Java e C#, a ideia é criar uma propriedade que irá receber uma classe que implementa a interface descrita pelo template (a qual veremos na sequência). Alteremos então o loginCustomControl.js:

‘use strict’;

angular.module(‘app.directives.customcontrols’, [])

.directive(‘loginControl’, function() {

return {

restrict: ‘E’,

templateUrl: ‘loginCustomControlTemplate.html’ ,

            scope: {

                data: ‘=’

            }

};

});

A novidade é esta propriedade chamada scope (que faz parte da configuração da diretiva). Lá dentro temos uma propriedade chamada data. Este propriedade somos nós que definimos e poderia chamar qualquer coisa. Uma vez definido, é este o nome que iremos utilizar na página que chama o controller para passar o provedor com a implementação da interface especificada pelo template que ainda iremos alterar.

Este nome propriedade é um termo OO para fazer uma analogia, mas no AngularJS o comportamento é bem diferente. O que estamos fazendo aqui é criando um escopo interno isolado (Isolate Scope), que não irá se misturar com o escopo externo da diretiva, porém estão mapeados. Ou seja, de fora conseguimos chegar no Isolate Scope, mas não diretamente, somente via algum intermediário, que será o que eu chamei de provider.

Este sinal de igual (“=”) isolado na configuração da propriedade é um atalho que diz ao compilador que o nome da variável é exatamente o mesmo nome do atributo a qual ele deverá fazer o binding. Caso queira que o compilador faça binding em outro nome (sinceramente eu não sei por que faria), era só digitar assim: data: ‘=anything’.

Vamos a alteração do loginCustomControlTemplate.html, que terá aquilo que eu chamei de interface que os controllers deverão implementar.

<div>

<h1>Login Control</h1>

<label for=”lc_username”>Usuário: </label>

<input id=”lc_username” type=”text” placeholder=”Seu Usuário” ng-model=”data.usuario”>

<label for=”lc_password”>Senha: </label>

<input id=”lc_password” type=”password” placeholder=”Sua Senha” ng-model=”data.senha”>

<br>

<input type=”button” value=”Entrar” ng-click=”data.entrar()”>

<input type=”button” value=”Esqueci a minha senha” ng-click=”data.esqueci()”>

</div>

Destaquei os pontos mais importantes:
+ data.usuario
+ data.senha
+ data.entrar()
+ data.esqueci()

Logo, duas propriedades e dois métodos. Esta a minha interface. Todos os meus provedores que queiram utilizar este template deverão implementar este contrato no controller. Esta palavra data que precede a propriedade é o Isolate Scopo que configuramos no script anterior. Se quiser alterar o nome lá, terá que alterar neste template também.

Agora vamos criar o provedor. Vou criar dois, apenas para ilustrar. Crie um arquivo chamado LoginController.js e adicione o seguinte conteúdo:

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

function LoginController($scope, $window){

$scope.alertProvider = {

entrar : function(){

alert(‘usuário: ‘ + $scope.alertProvider.usuario + ‘ | senha: ‘ + $scope.alertProvider.senha);

},

esqueci : function(){

alert(‘Esqueci’);

}

};

$scope.urlProvider = {

entrar : function(){

$window.location.href = ‘entrar.html?user=’ + $scope.urlProvider.usuario;

},

esqueci : function(){

$window.location.href = ‘esqueci.html?user=’ + $scope.urlProvider.usuario;

}

};

};

No código acima, declaramos um controller que recebe o $scope e a diretiva $window (opcional), que vem com o AngularJS – utilizaremos ela para fazer o redirecionamento de páginas em um dos provedores (provavelmente você não teria isto em seus próprios provedores).

Ademais, temos dois provedores: um chamado alertProvider e outro chamado urlProvider. O primeiro simplesmente chama um alert com as credenciais, o segundo redireciona para uma página para realizar a autenticação ou o serviço de reativação de senha. Veja que os dois declaram as funções do contrato e fazem uso das propriedades, no contexto de Isolate Scope (por isto utilizam o nome do provider – é ele o intermediário que citei na explicação sobre o recurso).

Agora que já está quase tudo pronto, não podemos nos esquecer de incluir a dependência deste novo módulo controller que adicionamos no arquivo app.js:

angular.module(‘app’, ['app.directives.customcontrols', 'app.controller.login']);

Por fim, basta você alterar a página index.html para passar qualquer um dos provider para o seu custom-control. Vamos ver:

<!DOCTYPE html>

<html>

<head>

<title>AngularJS .::. Simple Login Control</title>

</head>

<body>

<div ng-controller=”LoginController”>

<h1>Custom Control Sample</h1>

<login-control data=”alertProvider”></login-control>

</div>

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

<script>

$LAB

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

.script(‘loginCustomControl.js’)

.script(‘loginController.js’)

.script(‘app.js’)

.wait(function(){

angular.bootstrap(document, ['app']);

});

</script>

</body>

</html>

Veja que configuramos o controller que criamos na diretiva ng-controller (que agora você já sabe que na verdade se chama ngController). Além disto, passamos para a Isolate Scope data o provider que iremos utilizar.

Quando você digitar o usuário e senha e clicar no botão “Entrar” irá receber um alert ambas as informações.

Agora troque o provider para <login-control data=”urlProvider”></login-control>

Ao clicar no mesmo botão “Entrar” você será redirecionado para a página entrar.html?user=, que obviamente não existe, visto que não criamos (fique a vontade).

Lá no plunker tem uma página para cada provider (index.html e loginAlert.html), fique a vontade para visitar e brincar com o código: http://plnkr.co/edit/ZxedzKfBo2WtzWNiiqQD

É isto aí pessoal, com apenas duas lições vocês já podem fazer uma série de coisas bacanas em seus projetos.

Etiquetado , , , ,

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&#8242;)”>

<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 , , , , , , , , ,

Por que devo carregar Javascript assincronamente? E como faço isto?

Javascript, javascript… Até há pouco tempo diria que a relação dos desenvolvedores com a linguagem client-side era algo de amor ou ódio.

Muitos ainda pensam assim, mas a web 2.0 surgiu há alguns anos e ainda tem gente do próprio mercado que não percebeu: a web 2.0 traz novos paradigmas e novos desafios, dentre as quais temos que lidar com o imenso volume de tráfego de dados na rede (hoje até a comunidade mais carente está com acesso a internet. Foram publicadas pesquisas onde nos States o pessoal já consegue mais conteúdo pela banda larga do que TV por assinatura – a Netflix agradece).

Uma das formas de lidar com esta quantidade monstro de informação é dividir a responsabilidade do UX (user experience) entre o client-side e o server-side de maneira correta e consciente (num futuro eu entro nesta discussão sobre prós e contras de cada approach).  Mas quando falamos em client-side, não tem como fugir: Javascript – ou ame ou engula a seco, mas não ignore.

Falando especificamente de Javascript, ele é um dos principais responsáveis por perca de performance em uma aplicação. Ele é um cara que bloqueia a renderização do HTML pelo Browser . Ou seja, a página só é completamente carregada quando todos os seus scripts também são… Se tivermos scripts com problema é um “Deus nos acuda”.

Uma página geralmente é carregada de maneira síncrona… E até pouco tempo atrás nós simplesmente não nos importávamos com isto e deixávamos os scripts sendo carregados antes mesmo da página (dentro da TAG HEAD – o que era um padrão até então, visto que ninguém sofria muito com isto e ninguém tinha parado para pensar em uma alternativa). Veja um exemplo:

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

script.js
function Hello() {
alert(‘Veja que o conteudo so e exibido depois que voce der o OK no alert. Isto e a evidencia que a pagina HMTL é carregada de forma sequencial, sincrona e que o Javascript esta bloqueando a execucao da pagina’);
}

index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src=”script.js”></script>
<script>
Hello();
</script>
</head>
<body>
<h1 id=”html_says”>Hello World</h1>
</body>
</html>

Neste caso, o script era carregado antes mesmo do conteúdo da página. Se você executar este código acima, verá que o alert fica preso até você dar o “OK” e somente depois a página (Hello World) é exibida. Ou seja, estamos bloqueando a execução do HTML.

Conforme esta prática se tornou um problema, uma nova maneira de carregar o script foi pensada e divulgada para toda a comunidade. Bastaria carrega-lo após a execução de todo o HTML, imagens e CSS. Assim a página seria exibida, mesmo que o script ainda não tivesse sido carregado. Para isto, é só carrega-lo antes da TAG </BODY >. Veja o exemplo:

script.js
function Hello() {
alert(‘Agora o conteudo e carregado antes do script. Isto por que o documento e carregado de forma sequencial e sincrona.’);
}

index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1 id=”html_says”>Hello World</h1>
<script src=”script.js”></script>
<script>
Hello();
</script>
</body>
</html>

Esta simples prática resolveu uma boa parte dos problemas e continua sendo utilizada com bastante eficiência, pois não há mais o risco do usuário ficar olhando para as nuvens enquanto o script não é carregado… O carregamento continua sendo síncrono e sequencial, é verdade. Mas a UX aumenta bastante.

Porém esta ainda não é a solução ideal… O bacana mesmo é se o carregamento fosse assíncrono, pois isto faria uma página carregar bem mais rápida, otimizando assim a performance.

Uma solução clássica para carregar o script de forma assíncrona é construir o script de forma que possibilite o carregamento paralelo a página. Vejam:

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

script.js
var content = document.getElementById(‘html_says’);
content.innerHTML = content.innerHTML + ” John Lennon!!!”;

index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1 id=”html_says”>Hello World</h1>
<script>
(function () {
var po = document.createElement(‘script’);
po.type = ‘text/javascript’;
po.async = true;
po.src = ‘script.js’;
var s = document.getElementsByTagName(‘script’)[0];
s.parentNode.insertBefore(po, s);
})();
</script>
</body>
</html>

Isto garante que o carregamento do Javascript seja assíncrono, embora a sintaxe não seja muito agradável.  Uma solução para isto foi à inclusão do atributo async dentro do elemento script. Este atributo já foi implementado por todos os browsers – só o Internet Explorer que demorou bastante para implementar e só entrou na versão 10.

Para isto, basta carregar o script deste modo:
<script async src=”script.js”></script>

Simples, não? O Javascript será carregado e executado assim que o download for finalizado.

Mas e as versões mais antigas do IE? Como faremos? Um dos atributos especificados há muitos anos pela W3C para o elemento script é um chamado defer. Ele também faz o download assíncrono dos scripts, porém ele só irá executar quanto toda a página for carregada. Isto é muito interessante e, por vezes, muito desejado, pois resolve o problema das dependências de scripts – como na utilização do jQuery.

O problema é que os outros browsers foram na contramão do IE (que implementou o atributo desde versões mais antigas) e demoraram demais para implementar o defer… Então é comum você ter Safari, Chrome e Firefox sem suporte para o atributo. Para isto, a W3C especificou o seguinte: você poderá incluir o async e o defer dentro do mesmo elemento script. Um será fallback do outro, sendo que o async sempre sobrescreverá o defer quando o browser tiver o suporte para os dois.

Isto é maravilhoso para incluir scripts de AdSense e Analytics, que carregam sem depender de mais ninguém. Mas a dependência ainda é um problema. Vamos a um exemplo:

Plunker: http://plnkr.co/edit/1rO991HJurXhzylxf1J2

script.js
$(‘#html_says’).append(‘ John Lennon!!!’);

index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1 id=”html_says”>Hello World</h1>
<script async defer src=”http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js”></script&gt;
<script async defer src=”script.js”></script>
</body>
</html>

Rodar isto aí num Chrome atualizado é apostar na sorte. Para resolver o problema alguns desenvolvedores simplesmente “empacotam” e compactam todos os scripts em um único arquivo js para o ambiente de produção (afinal em desenvolvimento ficaria inviável dar manutenção num negócio destes). Outros implementam callbacks para a página.

Porém, para nossa sorte, vendo bastante potencial no modelo assíncrono e já com a ideia de resolver as dependências, uma série de Script Loaders foram criados. E todos são bem interessantes e simples de serem utilizados.

Eu avaliei dois, que trago para vocês. O primeiro é o LABjs. A maior vantagem é sua simples utilização, muito parecido como o que já utilizamos hoje, além de ser extremamente leve. A principal desvantagem é que ele só resolve o problema do Javascript – se você quiser também carregar imagens e CSS assincronamente – o que também é interessante, embora exija certos cuidados, deverá procurar outra biblioteca. Vamos ao exemplo (não se esqueça de baixar a versão do LABjs em seu projeto):

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

script.js
$(‘#html_says’).append(‘ John Lennon!!!’);

index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1 id=”html_says”>Hello World</h1>
<script src=”LAB.min.js”></script>
<script>
$LAB
.script(“http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js&#8221;).wait()
.script(“script.js”);
</script>
</body>
</html>

Este método “wait()” é o que resolve a dependência, pois ele garante que este script (jQuery) será executado antes do “script.js”.

A biblioteca é muito estável e a mais de dois anos não precisa sofrer nenhuma alteração.

Já para quem trabalha com AMD (Asynchronous Module Definition) – ou seja – quase todos os novos especialistas em Front-end, principalmente os adeptos de SPA (single page application), a qual eu particularmente gosto bastante, a alternativa de Script Loader mais popular é o RequireJS, uma senhora ferramenta por sinal a qual certamente falaremos mais neste blog. Vamos ao exemplo (não se esqueça de baixar a versão do RequireJS no seu projeto):

Plunker: http://plnkr.co/edit/6u5hgyDvlBiqrALbl77K

script.js
$(‘#html_says’).append(‘ John Lennon!!!’);

main.js
require.config({
paths: {
“jquery”: “http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min&#8221;,
“myscript”: “script”
}
});

define(['jquery', 'myscript'], function () { });

index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<h1 id=”html_says”>Hello World</h1>
<script data-main=”main” src=”require.js”></script>
</body>
</html>

Por enquanto é isto pessoal! É muito importante trabalhar de forma assíncrona no HTML. Só com estas dicas de Javascript, é possível ganhar de 2x a 3x mais velocidade ao carregar suas aplicações – basta fazer o teste. Quando trabalhamos com AMD, é quase impossível pensarmos em trabalhar de maneira síncrona.

Há várias técnicas para carregar imagens e CSS do mesmo modo (sendo bem popular uma variante deste exemplo), só precisamos tomar alguns cuidados extras. Mas isto ficará para outro artigo.

Etiquetado , , , , ,

Detecção de browser, comentários condicionais e o fim do “<!–[if gt"

Há muito tempo que a comunidade de front-end developers utiliza a famosíssima tag de comentário condicional para detecção de browser. Um exemplo para ilustrarmos:

<!–[if lte IE8]>
<p>Você está utilizando um browser muito antigo! Atualize para garantir que tudo funcione adequadamente.</p>
<![endif]–>

Este é um exemplo de comentário condicional. Aqui estamos instruindo o browser a, caso sua versão seja igual ou menor do que o Microsoft Internet Explorer 8, exibir a mensagem do elemento <p>.

Para quem saber o que significa esta sintaxe, temos um comentário HTML comum seguido pela expressão [if lte IE8]. O “if” é auto explicável.  O “lt” é a abreviação de “lower than” (menor do que) e o “e” é abreviação de “equal” (igual). Ou seja, a expressão pode ser lida exatamente como “se menor ou igual ao internet explorer 8”. Se quiser saber mais sobre este negócio de comentário condicional, leia este artigo.

Uma coisa que uma boa parte dos desenvolvedores não conhece é que esta instrução através de comentário condicional só funciona no Internet Explorer… Quer dizer, funcionava… A partir da versão 10, o Internet Explorer passou a tratar o bloco assim como os demais browser: apenas como um comentário. E está correto – a Microsoft vem limpando “os fantasmas” do passado, o que é digno de nota.

Enfim, para o contexto do exemplo, ainda é válido utilizar a instrução – visto que a ideia é instruir o browser a fazer algo somente nas versões mais antigas do Internet Explorer, geralmente por falta de compatibilidade/padrões.

Restou alguma alternativa? Bem, a Microsoft ainda não removeu a compilação condicional via Javascript – uma conhecida como @cc_on. Este funciona em todas as versões do Internet Explorer, mesmo a 10. A ideia é parecida com os comentários condições, só que no Javascript:

<script>if(Function(‘/*@cc_on return document.documentMode <= 10@*/’)()){alert(‘Você está utilizando um browser que não suportamos! Saia do Internet Explorer!.’);}</script>

Mas fico me perguntando se o próximo Internet Explorer irá manter a funcionalidade…

Tem mais alguma alternativa?

Alguns dirão para usarem o jQuery, mas infelizmente o suporte ao método parou na versão 1.9 e o mesmo foi removido nas versões posteriores. Na época era só fazer isto:

if (jQuery.browser.msie && jQuery.browser.version < 10) {
// seu código
}

E agora? Um usuário do StackOverflow postou algo simples, porém funcional: investigar o user agent! Como eu não tinha pensado nisto?

Lá no Javascript, existe o objeto navigator que tem o método userAgent. Daí é só fazer o match e trazer o resultado (com o substring, você também consegue trazer a versão)! Veja só:

<script>document.write(navigator.userAgent);</script>

Isto irá trazer todo o userAgent… Porém, vamos perguntar apenas se o browser é o IE:

<script>document.write(navigator.userAgent.match(/MSIE/) != null);</script>

Pronto! Eu achei o máximo! Simples, sem utilização de frameworks e direto ao ponto.

Além disto, podemos utilizar em outros browsers. Vamos verificar se o browser do usuário é o Google Chrome?

<script>document.write(navigator.userAgent.match(/Chrome/) != null);</script>

Eu curti bastante. Fica a dica para vocês.

Etiquetado , , , , ,

Instalando NPM através de um proxy

Cada vez mais a utilização do node.js é necessária no Front-end World, tornando-se uma ferramenta essencial para auxiliar os seus projetos. E a utilização do node.js independe de você ser um desenvolvedor node.js.

Isto acontece por que muitas ferramentas de apoio ao desenvolvimento web estão sendo construídas nesta linguagem – sendo que estas ferramentas são tão boas que quase que estão se tornando padrões de mercado.

Alguns exemplos de ferramentas extremamente úteis que eu posso citar são as seguintes: Bower, Typescript, modern.ie, CoffeeScript e o LESS.

O node.js trabalha com um repositório super interessante para gerenciar projetos de código aberto chamado NPM (node package modules).  Estas ferramentas estão lá, além de uma série de projetos que poderão (e irão) ser utilizadas como dependências de seus próprios projetos escritos em node.js (como os npms Express e Jade).

Utilizar o npm é muito simples. Basta abrir o Command Prompt e digitar npm install <nome_do_pacote> e automaticamente será realizado o download do pacote (assim como suas dependências)  na raiz de seu projeto dentro de uma pasta chamada node_modules.

Existe também a possibilidade de instalar os  de forma global, assim você não precisa ficar realizando o download dos pacotes mais comuns todas as vezes que criar um novo projeto. Para isto basta digitar npm install –g <nome_do_pacote>.

Recomendo a leitura deste artigo para entender melhor o que é npm.

O problema é quando você está numa rede que sai para a internet através de proxy! A maior parte do pessoal de mata para conseguir funcionar.

Então vamos a duas dicas para conseguir resolver este obstáculo.

1)      Você pode instalar o pacote através do seguinte comando:
npm –proxy http://enderecodoproxy:porta install <nome_do_pacote>

2)      Você pode criar uma configuração para não precisar ficar digitando o proxy toda hora:

2.1 – Abra o seu bloco do notas e digite:

proxy = http://enderecodoproxy:porta

2.2 – Salve em “C:\Users\SeuUsuario” ou “C:\Usuários\SeuUsuario” com o nome .npmrc

3.1 – agora você pode instalar qualquer pacote da forma tradicional sem especificar o proxy.

Por enquanto é isto aí!

Até a próxima!

Etiquetado , , , ,

Entendendo os Panels: Um Exemplo

Este é uma série de artigos em quatro partes. Leia o que foi escrito sobre Canvas, Stack Panel e sobre Grid.

Vamos ver um exemplo de um layout mesclando todos os painéis nativos que vimos até agora?

<Grid x:Name=”LayoutRoot” Background=”AliceBlue” Height=”600″ Width=”600″>        
    <Grid.RowDefinitions>
<RowDefinition Height=”100″ />
        <RowDefinition Height=”auto”/>
        <RowDefinition />
</Grid.RowDefinitions>


<Canvas Grid.Row=”0″>
        <TextBlock Canvas.Top=”20″ Canvas.Left=”20″ Text=”Logo” 
                   FontWeight=”Bold” FontSize=”40″ />
        <Border Canvas.Top=”20″ Canvas.Left=”250″ 
                Background=”Yellow” Height=”60″ Width=”300″>
            <TextBlock Text=”Banner” HorizontalAlignment=”Center” 
                       VerticalAlignment=”Center” />
        </Border>                
    </Canvas>


<StackPanel Orientation=”Horizontal” Grid.Row=”1″ Background=”Black”>
        <TextBlock Margin=”20″ Foreground=”White” 
                   Text=”File” FontSize=”20″ />
        <TextBlock Margin=”20″ Foreground=”White”
                   Text=”|” FontSize=”20″ />
        <TextBlock Margin=”20″ Foreground=”White” 
                   Text=”Editar” FontSize=”20″ />
        <TextBlock Margin=”20″ Foreground=”White” 
                   Text=”|” FontSize=”20″ />
        <TextBlock Margin=”20″ Foreground=”White” 
                   Text=”View” FontSize=”20″ />
        <TextBlock Margin=”20″ Foreground=”White” 
   
                Text=”|” FontSize=”20″ />
        <TextBlock Margin=”20″ Foreground=”White” 
                   Text=”Projeto” FontSize=”20″ />
    </StackPanel>

    <Grid Grid.Row=”2″>
        <Grid.ColumnDefinitions>
<ColumnDefinition Width=”3*” />
            <ColumnDefinition />
</Grid.ColumnDefinitions>

        <TextBlock Margin=”10″ Grid.Column=”0″ TextWrapping=”Wrap”>
            <Run FontSize=”30″ Foreground=”Blue” 
                 FontWeight=”Black”>Título do Post</Run>
<LineBreak />
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. 

            Maecenas porttitor congue massa. Fusce posuere, magna sed 
            pulvinar ultricies, purus lectus malesuada libero, sit amet 
            commodo magna eros quis urna. Nunc viverra imperdiet enim. 
            Fusce est. Vivamus a tellus. Pellentesque habitant morbi 
            tristique senectus et netus et malesuada fames ac turpis 
            egestas. Proin pharetra nonummy pede. Mauris et orci. 
            Aenean nec lorem.
            <LineBreak />
<LineBreak />
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. 

            Maecenas porttitor congue massa. Fusce posuere, magna sed 
            pulvinar ultricies, purus lectus malesuada libero, sit amet 
            commodo magna eros quis urna. Nunc viverra imperdiet enim. 
            Fusce est. Vivamus a tellus. Pellentesque habitant morbi 
            tristique senectus et netus et malesuada fames ac turpis 
            egestas. Proin pharetra nonummy pede. Mauris et orci. 
            Aenean nec lorem.
            <LineBreak />
<LineBreak />
<Run FontSize=”20″ Foreground=”Blue”>Sub-seção</Run>
<LineBreak />
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. 

            Maecenas porttitor congue massa. Fusce posuere, magna sed 
            pulvinar ultricies, purus lectus malesuada libero, sit amet 
            commodo magna eros quis urna. Nunc viverra imperdiet enim. 
            Fusce est. Vivamus a tellus. Pellentesque habitant morbi 
            tristique senectus et netus et malesuada fames ac turpis 
            egestas. Proin pharetra nonummy pede. Mauris et orci. 
            Aenean nec lorem.
        </TextBlock>

        <StackPanel Grid.Column=”1″ Background=”Gray”  >
            <TextBlock Margin=”10 0 0 0″ Text=”Veja mais em” 
                       FontSize=”16″ FontWeight=”Bold” Foreground=”White” />
            <TextBlock Margin=”10 0 0 0″ 
                       Text=”blah blah blah” Foreground=”White” />
            <TextBlock Margin=”10 0 0 0″ 
                       Text=”blah blah blah” Foreground=”White” />
            <TextBlock Margin=”10 0 0 0″ 
      
                 Text=”blah blah blah” Foreground=”White” />
            <TextBlock Margin=”10 0 0 0″ 
                       Text=”blah blah blah” Foreground=”White” />
            <TextBlock Margin=”10 0 0 0″ 
                       Text=”blah blah blah” Foreground=”White” />
            <TextBlock Margin=”10 0 0 0″ 
                       Text=”blah blah blah” Foreground=”White” />
        </StackPanel>
</Grid>        
</Grid>

Este é o resultado:

Conclusão

Com Silverlight tudo é mais fácil e organizado. Utilize-o no seu dia-a-dia e verá que a curva de aprendizado é pequena para realizar coisas poderosas. Fora que seu aprendizado valerá não só para aplicações web, mas também para aplicações Out-Of-Browser e aplicações para Windows Phone 7 – a qual todos os conceitos podem ser aplicados.

Espero que vocês tenham gostado da série de artigos a respeito dos Panels.

Até a próxima!

Etiquetado ,
Seguir

Obtenha todo post novo entregue na sua caixa de entrada.