sábado, 11 de novembro de 2017

Introdução ao PHP 7 - O que há de novo e o que se foi

Um dos eventos mais emocionantes em 2015 no mundo do PHP foi o lançamento do PHP 7, 10 anos a partir do lançamento da última versão principal, o PHP 5. Com um grande passo em frente, o PHP 7 apresenta muitos novos recursos e atualizações de desempenho.

No entanto, ele também remove a funcionalidade antiga e obsoleta, que introduz algumas quebras de compatibilidade, dificultando a migração de aplicativos mais antigos para a nova versão. Este guia deve servir como um passeio rápido sobre o que esperar se você planeja mover suas aplicações existentes, ou criar novas, em cima do PHP 7.

Mas espere, para onde foi o PHP 6?

Se você não trabalhou com PHP ultimamente, você pode se perguntar o que aconteceu com o PHP 6, por que o salto do PHP 5 para o PHP 7? Bem, para ser breve, o PHP 6 foi uma falha. A principal característica da versão 6 foi o suporte nativo para caracteres Unicode, pois o PHP é usado principalmente no desenvolvimento web e a web precisa do Unicode, então o movimento para trazer Unicode para PHP fazia sentido.

A idéia era oferecer suporte completo para o Unicode ao próprio núcleo. Isso teria trazido capacidades extensas para o idioma, desde a capacidade de usar emojis tolos como nomes de funções e variáveis, até uma poderosa funcionalidade de string internacional. Por exemplo, quando outro idioma usa maiúsculas e minúsculas de forma diferente do inglês, ou quando um nome em caracteres chineses precisa ser convertido em inglês.


PHP 6 era ambicioso, porém sem valor. Desta forma surgiu o PHP 7, ignorando a versão 6 no processo.

Infelizmente esse plano ambicioso revelou-se um problema maior do que o esperado. Grande parte da base do código teve que ser alterada para suportar o Unicode para ambas as extensões principais e importantes, o que se mostrou tedioso e complicado. Isso desacelerou o desenvolvimento de outros recursos no idioma, frustrando muitos desenvolvedores do PHP no processo. Ocorreram obstáculos adicionais, o que resultou em menos interesse em desenvolver um suporte Unicode nativo, levando finalmente o projeto a ser abandonado.

Uma vez que os recursos, como livros e artigos, foram escritos para o PHP 6 e seu suporte Unicode, a nova versão seria renomeada PHP 7 para evitar confusão.

De qualquer forma, deixando o passado triste para trás, vamos ver o que o PHP 7 traz para a festa.

Batalha de Desempenho, PHP 7 vs. PHP 5

Como praticamente todas as atualizações, esperam-se pequenas atualizações de desempenho. No entanto, desta vez o PHP traz uma melhoria significativa em relação às versões anteriores, tornando o desempenho puro um dos recursos mais atraentes do PHP 7. Isso vem como parte do projeto "PHPNG", que aborda o core do próprio mecanismo Zend.

Ao refatorar estruturas internas de dados e adicionar uma etapa intermediária para compilação de código na forma de uma Árvore de sintaxe abstrata (AST), o resultado é um desempenho superior e uma alocação de memória mais eficiente. Os próprios números parecem muito promissores; os benchmarks feitos em aplicativos do mundo real mostram que o PHP 7 é duas vezes mais rápido do que o PHP 5.6 em média e resulta em 50 por cento menos consumo de memória durante os pedidos, tornando o PHP 7 um rival forte para o compilador JH HHVM do Facebook. Dê uma olhada nesta infografia do Zend que descreve o desempenho de alguns CMS e Frameworks comuns.


O PHP 7 parece familiar, mas está atento ao desempenho. O refinado Zend Engine e os ganhos de desempenho resultantes fazem uma enorme diferença.

A diminuição do consumo de memória também permite que as máquinas menores atendam melhor os pedidos, além da oportunidade de construir serviços micro em torno do PHP. As mudanças internas, em particular a implementação AST, também abre possibilidades para otimizações futuras que poderiam impulsionar o desempenho ainda mais. Uma nova implementação interna de um compilador JIT está sendo considerada para futuras versões.

PHP 7 Syntactic Sugar

O PHP 7 vem com novos recursos de sintaxe. Embora não estenda os recursos do próprio idioma, eles fornecem uma maneira melhor ou mais fácil de tornar seu código mais agradável para escrever e agradar aos olhos.

Declarações de importação de grupo

Agora podemos agrupar declarações de importação para classes provenientes do mesmo espaço de nomes em uma única linha [use]. Isso deve ajudar a alinhar declarações de forma significativa ou simplesmente salvar alguns bytes em seus arquivos.


use Framework\Module\Foo;
use Framework\Module\Bar;
use Framework\Module\Baz;


Com o PHP 7 podemos usar:


use Framework\Module\{Foo, Bar, Baz};


Ou, se você preferir um estilo multi-line:

use Framework\Module{
    Foo,
    Bar,
    Baz
};


Operador de Coalescente Nulo

Isso resolve um problema comum na programação do PHP, onde queremos atribuir um valor a uma variável de outra variável, se esta última estiver realmente configurada, ou de outra forma fornecer um valor diferente para ela. É comumente usado quando trabalhamos com a entrada fornecida pelo usuário.

Pre-PHP 7:

if (isset($foo)) {
    $bar = $foo;
} else {
    $bar = 'default'; // we would give $bar the value 'default' if $foo is NULL
}


Depois do PHP 7:

$bar = $foo ?? 'default';


Isso também pode ser alinhado com várias variáveis:

$bar = $foo ?? $baz ?? 'default';


Operador spaceship

O operador spaceship (<=>) permite uma comparação de três vias entre dois valores, não apenas indicando se eles são iguais, mas também qual é maior, na desigualdade retornando 1,0 ou -1.

Aqui podemos tomar diferentes ações, dependendo de como os valores diferem:

switch ($bar <=> $foo) {
    case 0:
        echo '$bar and $foo are equal';
    case -1:
        echo '$foo is bigger';
    case 1:
        echo '$bar is bigger';
}


Os valores comparados podem ser inteiros, flutuadores, strings ou mesmo arrays. Verifique a documentação para ter uma idéia de como diferentes valores são comparados entre si. [https://wiki.php.net/rfc/combined-comparison-operator]

Novos recursos no PHP 7

Mas, claro, o PHP 7 também traz funcionalidade nova e emocionante.

Tipos de Parâmetros Escalares e Dicas de Tipo de Retorno

O PHP 7 estende as declarações de parâmetros anteriores dos parâmetros em métodos (classes, interfaces e arrays), adicionando os quatro tipos escalares; Inteiros ( int), flutuadores ( float), booleanos ( bool) e strings como possíveis tipos de parâmetro.

Além disso, podemos opcionalmente especificar quais tipos de métodos e funções retornam. Os tipos suportados são bool, int, float, string, array, callable, nome da classe ou interface, auto e pai (para métodos de classe)

class Calculator
{
    // We declare that the parameters provided are of type integer
    public function addTwoInts(int $x, int $y): int {
        return $x + $y; // We also explicitly say that this method will return an integer
    }
}


As declarações de tipo permitem a construção de aplicativos mais robustos e evitam passar e retornar valores incorretos das funções. Outros benefícios incluem analisadores de código estático e IDEs, que fornecem uma melhor visão sobre a base de código se faltar DocBlocks.

Uma vez que o PHP é uma linguagem de tipagem fraca, certos valores para os tipos de parâmetros e de retorno serão lançados com base no contexto. Se nós passamos o valor "3" em uma função que tenha um parâmetro de tipo declarado int, o intérprete o aceitará como um número inteiro e não lançará nenhum erro. Se você não quiser isso, você pode ativar strict mode, adicionando uma diretiva [declare].

declare(strict_types=1);

Isso é definido em uma base por arquivo, como uma opção global seria dividir repositórios de código para aqueles que são construídos com rigor mundial em e aqueles que não são, resultando em um comportamento inesperado quando combinamos o código de ambos.

Exceções do motor

Com a adição de exceções do mecanismo, erros fatais que resultariam na rescisão do script podem ser capturados e tratados com facilidade.

Erros como chamar um método inexistente, não encerrará o script, em vez disso, eles lançam uma exceção que pode ser tratada por um bloco try catch, que melhora o tratamento de erros para seus aplicativos. Isso é importante para certos tipos de aplicativos, servidores e daemons porque erros fatais exigiriam que eles reiniciassem. Os testes em PHPUnit também devem se tornar mais utilizáveis ​​à medida que erros fatais deixam cair o conjunto completo de testes. As exceções, em vez de erros, seriam tratadas em cada caso de teste.


O PHP 7 parece familiar, mas está atento ao desempenho. O refinado Zend Engine e os ganhos de desempenho resultantes fazem uma enorme diferença.

O PHP 7 adiciona várias novas classes de exceções com base no tipo de erros que podem ser encontrados. Para manter a compatibilidade entre versões, Throwable foi adicionada uma nova interface que pode ser implementada a partir de exceções de motor e exceções de usuário. Isso foi necessário para evitar exceções do motor para estender a classe de exceção base, resultando em antigas exceções de captura de código que não existiam antes.

Antes do PHP 7 isso teria encerrado o script com um erro fatal:

try {
    thisFunctionDoesNotEvenExist();
} catch (\EngineException $e) {
    // Clean things up and log error
    echo $e->getMessage();
}


Classes Anônimas

As classes anônimas são primas de funções anônimas que você pode usar em uma simples instância de curto prazo. As classes anônimas são facilmente criadas e usadas como um objeto comum.

Pre-PHP 7

class MyLogger {
  public function log($msg) {
    print_r($msg . "\n");
  }
}

$pusher->setLogger( new MyLogger() );


Com a classe anônima:

$pusher->setLogger(new class {
  public function log($msg) {
    print_r($msg . "\n");
  }
});


As classes anônimas são úteis no teste de unidades, particularmente em objetos e serviços de mocking tests. Isso nos ajuda a evitar pesadas bibliotecas e estruturas de simulação criando um objeto simples que fornece a interface que queremos fingir.

Funções CSPRNG

Foram adicionadas duas novas funções para gerar strings e números inteiros criptograficamente seguros.

random_bytes(int $len);

Retorna uma string aleatória com comprimento $len.

random_int(int $min, int $max);

Retorna um número entre $min e $max.

Unicode Codepoint Escape Syntax

Ao contrário de muitos outros idiomas, antes do PHP 7, o PHP não tinha uma maneira de escape de um codigo Unicode em literais de strings. Essa funcionalidade adiciona a seqüência de escape "\u" para produzir esses caracteres usando seu código UTF-8. Isso é melhor do que inserir os caracteres diretamente, permitindo um melhor tratamento de caracteres invisíveis, bem como caracteres que possuem a mesma representação gráfica, mas diferem em significado.

echo "\u{1F602}"; // outputs 😂

Observe que isso interrompe o código existente com a seqüência \u porque altera o comportamento.

Geradores são atualizados

Os geradores em PHP também possuem alguns recursos adicionais agradáveis. Agora, os geradores têm uma declaração de retorno que pode ser usada para permitir a saída de um valor final após a iteração. Isso pode ser usado para verificar se o gerador foi executado sem erros e permite que o código que chamou o gerador para lidar com vários cenários adequadamente.

Além disso, os geradores podem retornar e produzir expressões de outros geradores. Isso permite que eles dividam operações complexas em unidades mais simples e modulares.

function genA() {
    yield 2;
    yield 3;
    yield 4;
}

function genB() {
    yield 1;
    yield from genA(); // 'genA' gets called here and iterated over
    yield 5;
    return 'success'; // This is a final result we can check later
}

foreach (genB() as $val) {
    echo "\n $val"; // This will output values 1 to 5 in order
}

$genB()->getReturn(); // This should return 'success' when there are no errors.


Expectativas

As expectativas são um aprimoramento da função assert(), mantendo a compatibilidade com versões anteriores. Eles permitem asserções de custo zero no código de produção e fornecem a capacidade de lançar exceções personalizadas quando a afirmação falhar, o que pode ser útil durante o desenvolvimento.

assert() torna-se uma construção de linguagem em PHP 7. As afirmações devem ser usadas apenas para fins de depuração no desenvolvimento e testes de ambientes. Para configurar o seu comportamento, recebemos duas novas diretivas.

zend.assertions
1: gerar e executar o código (modo de desenvolvimento) (valor padrão)
0: gera o código, mas salta em torno dele em tempo de execução
-1: não gera código tornando zero-custo (modo de produção)


assert.exception
1: lance quando a afirmação falhar, seja jogando o objeto fornecido como a exceção ou jogando um novo objeto AssertionError se a exceção não for fornecida
0: use ou gere um Throwable como descrito acima, mas apenas gere um aviso com base nesse objeto ao invés de jogá-lo (compatível com o comportamento do PHP 5)

Preparando para mover do PHP 5 para o PHP 7

A introdução de um lançamento importante traz a oportunidade de alterar/atualizar funcionalidades mais antigas ou até mesmo removê-las se elas forem consideradas antigas demais ou tenham sido obsoletas há algum tempo. Tais mudanças podem introduzir quebras na compatibilidade em aplicativos mais antigos.

Outra questão que surge de saltos dessa versão é que bibliotecas e frameworks importantes de que você depende podem ainda não terem sido atualizados para suportar a versão mais recente. A equipe do PHP tentou tornar as novas mudanças compatíveis com versões anteriores e permitir que a migração para a nova versão seja tão indolor quanto possível. Aplicativos mais recentes e atualizados devem achar mais fácil mudar para a nova versão, enquanto aplicativos mais antigos podem ter que decidir se os benefícios superam o custo, possivelmente escolhendo não atualizar.

A maioria das quebras são menores e podem ser mitigadas facilmente, enquanto outras podem exigir mais esforço e tempo. Basicamente, se você tivesse avisos de desaprovação em seu aplicativo antes de instalar o PHP 7, você provavelmente receberá erros que quebrarão o aplicativo até serem corrigidos. Você foi avisado, certo?

SAPIs e extensões antigas

Mais importante ainda, os SAPIs antigos e obsoletos foram removidos como a extensão mysql (mas você não deveria usar isso em primeiro lugar, certo?). Para obter uma lista completa de extensões, você pode verificar estas RFCs aqui e aqui.

Além disso, outros SAPIs estão sendo portados para o PHP 7.


Um monte de SAPIs e extensões antigas foram retirados do PHP 7. Estamos achando que não serão perdidas.

Sintaxe Variável Uniforme

Esta atualização fez algumas mudanças a favor da consistência para construções ​​variáveis. Isso permite expressões mais avançadas com variáveis, mas introduz mudanças no comportamento em alguns outros casos, como mostrado abaixo.

                        // old meaning            // new meaning
$$foo['bar']['baz']     ${$foo['bar']['baz']}     ($$foo)['bar']['baz']
$foo->$bar['baz']       $foo->{$bar['baz']}       ($foo->$bar)['baz']
$foo->$bar['baz']()     $foo->{$bar['baz']}()     ($foo->$bar)['baz']()
Foo::$bar['baz']()      Foo::{$bar['baz']}()      (Foo::$bar)['baz']()


Isso quebraria o comportamento de aplicativos que acessassem valores como este. Por outro lado, você pode fazer algumas coisas como esta:

// Nested ()
foo()(); // Calls the return of foo()
$foo->bar()();

// IIFE syntax like JavaScript
(function() {
    // Function body
})();

// Nested ::
$foo::$bar::$baz


Etiquetas de estilo antigo removidas

A abertura/fechamento de tags <% ... %>, <%= ... %>, são removidos e não mais válidos.

Nomes inválidos para Classes, Interfaces e Traits

Como resultado de adições como classes de parâmetros e tipos de retorno, as interfaces e os traits não podem mais ter os seguintes nomes:

bool
int
float
string
null
true
false

Estes causam quebras em aplicativos e bibliotecas existentes que os usam, mas devem ser fáceis de corrigir. Além disso, embora não provoquem nenhum erro e sejam válidos, o seguinte não deve ser usado porque eles são reservados para uso futuro:

resource
object
mixed
numeric

Para obter uma lista completa de alterações que quebrariam a compatibilidade, verifique este documento.

Você também pode usar o php7cc, que verifica seu código e pode detectar possíveis problemas que podem surgir se você mudar para o PHP 7. Mas, claro, não há melhor maneira do que instalar o PHP 7 e ver por si mesmo.

Possíveis problemas de compatibilidade do PHP 7

Compatibilidade de infraestrutura do PHP 7

Muitos serviços de hospedagem começaram a adicionar suporte para o PHP 7. Esta é uma boa notícia para os provedores de hospedagem compartilhada, pois os ganhos de desempenho permitirão aumentar o número de sites de clientes em seu hardware, reduzindo suas despesas operacionais e aumentando suas margens. Quanto aos próprios clientes, eles não devem esperar muito de um impulso nestas condições, mas para ser justo, a hospedagem compartilhada não é uma escolha orientada para o desempenho de qualquer maneira.

Por outro lado, os serviços que oferecem servidores privados virtuais ou servidores dedicados obterão os benefícios completos dessa colisão de desempenho. Alguns serviços PaaS, como o Heroku, apoiaram o PHP 7 no início, mas outros serviços, como a AWS Beanstalk e o OpenShift da Oracle, estão atrasados. Verifique o site do seu fornecedor PaaS para ver se o PHP 7 já é suportado, ou se o suporte virá no futuro próximo.

Claro, os provedores de IaaS permitem que você controle o hardware e instale o PHP 7 (ou compile se isso é mais do seu agrado). Os pacotes PHP 7 já estão disponíveis para os principais ambientes IaaS.

Compatibilidade de software PHP 7

Além da compatibilidade de infra-estrutura, você também precisa estar atento aos possíveis problemas de compatibilidade de software. Sistemas de gerenciamento de conteúdo populares como o WordPress, o Joomla e o Drupal adicionaram suporte para o PHP 7 com seus últimos lançamentos. Marcos importantes como Symfony e Laravel também gozam de suporte total.

No entanto, é hora de uma palavra de cautela. Este suporte não se estende ao código de terceiros na forma de complementos, plugins, pacotes ou o que seu CMS ou framework os chama. Eles podem sofrer problemas de compatibilidade e é sua responsabilidade garantir que tudo esteja pronto para o PHP 7.

Para repositórios ativos e mantidos, isso não deve ser um problema. No entanto, os repositórios mais antigos e não mantidos que não possuem suporte ao PHP 7 podem tornar sua aplicação inteira inutilizável.

O futuro do PHP

O lançamento do PHP 7 removeu o código antigo e obsoleto e preparou o caminho para novos recursos e atualizações de desempenho no futuro. Além disso, o PHP deverá obter otimizações de desempenho adicionais em breve. Apesar de ter algumas quebras de compatibilidade com lançamentos anteriores, a maioria dos problemas é fácil de resolver.

Bibliotecas e frameworks agora estão migrando seu código para o PHP 7, disponibilizando as versões mais recentes. Eu encorajo você a experimentá-lo e ver os resultados para você. Talvez o seu aplicativo já seja compatível e esteja à espera de usar, e se beneficiar do PHP 7.

POR VILSON DUKA - FREELANCE SOFTWARE ENGINEER @TOPTAL

ARTIGO ORIGINAL: Introducción A PHP 7: Qué Hay De Nuevo Y Qué Se Ha Ido

domingo, 15 de outubro de 2017

Design de UI do futuro sem botões

O que são botões e nós realmente precisamos deles?

Desde o início das interfaces de usuário gráficas usamos botões. Considere a GUI original da Xerox PARC, que tem 44 anos - e ainda assim nossas interfaces de usuário são muito parecidas.

Recentemente, tracei o histórico de estilos de botões criando o Dribbble Timeline. Embora os botões evoluíram em sintonia com as tendências atuais e ao lado da tecnologia, sua origem é, sem dúvida, inspirada em objetos reais do passado.


Canto inferior direito - Sears First Electric Buzzer 1897 Catálogo de outono

Por mais de uma década, criamos dispositivos sem uma interface física - que não dependem do toque humano, mas podem ser ativados por voz ou gesto. Por que persistimos na criação de formas com as quais interagir com base nos objetos familiares que nos cercam? A forma de um botão digital ainda é modelada em ferramentas e mecanismos que desenvolvemos no século XIX!

Nós criamos dispositivos eletrônicos inteligentes completamente novos, podemos manipulá-los de maneira quase exclusiva, mas por falta de preguiça ou força de hábito, continuamos a forçar nossos usuários a clicar em uma pequena área de apenas alguns pixels de largura.

É hora de fazer algo sobre isso - é hora de pensar sem botão.

UI "Sem-botão" - onde tudo se interage

Uma "utopia sem botões" é um conceito em que qualquer tentativa de manter mais de 130 anos de como as coisas sempre foram feitas está completamente destruída. O futuro é agora - devemos seguir em frente e alienar-nos das soluções ultrapassadas de nossos predecessores.

É possível para nós imaginar uma interface completamente livre de botões? Algo tão intuitivo, que, apenas olhando para ele, você saberá como agir? Não precisa mais ser apenas em nossa imaginação - essas interfaces já existem.


Microsoft HoloLens: HoloTour

Podemos, de uma vez por todas, eliminar o botão venerado? Microfones, câmeras, telas sensíveis ao toque, vibrações, acelerômetros, giroscópios, GPS, realidade estendida, realidade virtual - a lista continua - e tudo isso é gerenciado pelo seu smartphone ou PC. Não há mais nenhuma razão para continuar forçando seus usuários a pressionar aquele pequeno retângulo.

Vamos eliminar botões nas IIs

Você já leu Design de Tipo - Conceito do Tópico Designer Michael Abehsera em que as interfaces são desprovidas de qualquer elemento gráfico na qual o que importa é o conteúdo? Muitos de vocês perguntaram: "E os botões?" Nós não precisamos deles mais, vamos eliminá-los completamente.

Aqui estão algumas idéias interessantes:

O Facebook simplesmente perguntou: "Qual é o seu humor?" Não é necessário pressionar um botão para responder - use sua voz - diga-lhe que se sente bem e dirija-se à praia. Em seguida, arraste o texto que o Facebook reconhece da sua resposta para onde você deseja que ele apareça.


Pesquisa por voz Neel Raj

Parecido com um artigo sobre Medium? Um tempo atrás em Medium você poderia simplesmente "recomendar" um artigo. Hoje, clicamos em "palmas". Então, "aplauda". E se não houvesse necessidade de clicar nesse pequeno botão estranho, apenas literalmente aplaudir?


Conceito sem botões de Wojciech Dobry (... Não leve isso a sério demais.)

E quanto a algumas ações mais complexas? Como um check-out em uma loja online. Arraste e solte um item no carrinho de compras, deslize para proceder ao check-out e confirme com sua impressão digital. Mole-mole.


ASOS - Adicionar ao carrinho animação por Zachary Zhao

Conteúdo que pensa e tela inteira

Vamos começar com as superfícies que atualmente tocamos para executar uma ação. Dado que a grande maioria das interfaces de usuário gráficas dos dias modernos são exibições de tela sensíveis ao toque - muitas vezes manipuladas com nossos polegares - é lógico que tais interfaces estejam conosco por um longo período de tempo. E definitivamente esse impulso irresistível de toque-toque é a sensação mais importante de todas. Tocar na superfície nos dá uma sensação de ação real, de controle. Como seria, em vez de clicar em um ponto particular, ensinarmos nossos usuários a interagir com toda a superfície?

Vamos ver como o Instagram faz isso:


Navegação na história do Instagram

Você já viu um botão que permite navegar na História do Instagram e voltar para a história anterior? Provavelmente não, porque tudo que você precisa fazer é tocar a margem esquerda da tela para que isso aconteça.

Os usuários passaram a esperar novas formas de interagir com nossos produtos digitais e muitas vezes não envolve um botão. Os cartões são sensíveis em todas as suas superfícies; clicando em qualquer palavra, esperamos encontrar sua definição; Ao tocar imagens, esperamos alguma ação. Os usuários já estão acostumados com o fato de que áreas inteiras respondam ao toque.

Reconhecimento de Gesto

Nós entendemos os gestos muito bem porque eles são naturais para nós e refletidos em nossas ações físicas. Atualmente, quase todas as aplicações os usam para acelerar a navegação; podemos dobrar uma foto em vez de pressioná-la como um botão; podemos deslizar para navegar na galeria; ou tocar para ampliar qualquer conteúdo.


Virgil Pana


Interfaces de usuário por Ramotion & Jarek Berecki

Nós fazemos gestos não só em telas planas - gestos também são realizados em espaços AR e VR, onde podemos navegar com todo o nosso corpo.


Gestos em HoloLens pela Microsoft


Clay VR Gesture Recognition On iPhone

Interfaces de voz

Siri, Cortana, Alexa e o Assistente do Google estão funcionando. Muitos argumentam que as interfaces de voz são o futuro - é difícil discordar porque seus usos potenciais são além da conta. Podemos controlar veículos, edifícios inteligentes e máquinas simplesmente falando com eles - assim como conversamos com uma pessoa real. Com a inteligência artificial e a aprendizagem de máquinas, as máquinas agora são capazes de entender nosso idioma de forma cada vez mais precisa. Já não nos limitamos a pronunciar com cuidado "palavras mágicas"; podemos falar frases completas.


Ok, Google ...

Ao usar a fala, podemos nos mover livremente para o mundo das transferências bancárias. Atualmente, o Siri permite que você transfira dinheiro via PayPal para outra pessoa usando apenas um pedido rápido: "Siri, envie $200 para XYZ usando o PayPal." Sem botões - a única confirmação e verificação de segurança necessárias é o Touch ID.



Ações físicas, dispositivos conectados e reconhecimento de vídeo

Sorria para pagar? Está aqui! Alibaba e KFC lançaram em conjunto um sistema que permite que você pague simplesmente sorrindo para a câmera - sem botões para pressionar. O sistema funciona no reconhecimento facial e agora está disponível na China.



Este é apenas um dos potenciais usos. Nossos dispositivos já estão equipados com uma série de sensores e podem monitorar todo o seu corpo. Nada está impedindo que você use um dedo para executar qualquer ação em seu smartphone.



Outro exemplo de uma ação física que acelerará o desaparecimento dos botões é simplesmente estar perto do dispositivo. Vejamos o iWatch, por exemplo - apenas emparelhe com seu laptop para desbloquear o seu MacBook. Os dispositivos vestíveis ​​podem ser usados ​​para confirmar nossa identidade, bem como prever de maneira inteligente nossas necessidades com base na localização e nos dados dos sensores. Graças a eles, já podemos evitar o uso de botões em muitas interfaces.



Não esqueçamos sobre as tecnologias amplamente disponíveis há vários anos. Um deles é o giroscópio disponível em quase todos os smartphones, mas raramente usado em interfaces. Acima, você pode ver as interfaces experimentais criadas por Patryk Adaś.

UI Designers do futuro

Com tantas opções diferentes disponíveis para nós, agora é possível esquecer o uso de botões retangulares típicos para os usuários interagirem. Com a tecnologia de hoje, temos a capacidade de criar interfaces completamente novas que podem:

  • Economizar tempo do usuário
  • Impedir erros e
  • Compensar quaisquer ações acidentais



Interfaces conceituais de Cosmin Capitan e Ramotion

O tempo chegou - a tecnologia está nos ultrapassando. Nós, os designers, DEVEMOS perseguí-la... e certifique-se de recuperar o atraso!

POR WOJCIECH DOBRY - TOPTAL FREELANCE DESIGNER E DESIGN BLOG EDITOR @ TOPTAL
ARTIGO ORIGINAL: El Futuro del Diseño UI Sin Botones

terça-feira, 26 de setembro de 2017

BMS GT - Consumo de Web Api usando Javascript

Este artigo tem como objetivo documentar o uso de um importante recurso do meu sistema de gerenciamento com o cliente, o BMS GT. Este recurso se chama Web Api, e serve como um [conector] entre o sistema BMS e qualquer sistema legado.

Esta é a URL principal de consumo REST: /api/BMS/QueryXML

Com este conector, você pode efetuar todo CRUD de operações. Portanto, segue abaixo o uso prático deste recurso, usando Javascript/JQuery.

P.S.: Os exemplos são executados como se o serviço Web Api estivesse instalado no Endpoint [http://localhost:5000].

Para efetuar um SELECT

function selectRecords() {
    var xmlSELECT =
        "<select>" +
            "<organization name='BMS1' />" +
            "<user value='gtezini@hotmail.com' />" +
            "<password value='P9JyRi3HsoieLjpyVrr7rWn8uyCyR12RlARE+5yN2/Y=' />" +
            "<table name='account' alias='tb1' />" +
            "<fields>" +
            "    <field name='tb1.AccountId' label='ID' />" +
            "    <field name='tb1.Name' />" +
            "    <field name='tb1.AccountNumber' />" +
            "</fields>" +
            "<filter type='and'>" +
            "    <condition field='tb1.StatusCode' operator='=' value='1' />" +
            "</filter>" +
        "</select>";

    var param = {
        "Type": "GET",
        "xmlContent": xmlSELECT
    };

    $.ajax({
        type: 'POST',
        url: "http://localhost:5000/api/BMS/QueryXML",
        data: param,
        dataType: 'json',
        contentType: "application/x-www-form-urlencoded; charset=utf-8",
        success: function (data, textStatus, jqXHR) {
            if (textStatus == "success") {
        // JSON que representa os [registros] retornados
                var json = JSON.parse(data.Result);
            }

        },
        error: function (jqXHR, textStatus, errorThrown) {
            debugger;
        }
    });

}

Para efetuar um CREATE
function createRecord() {
 var xmlINSERT =
  "<insert>" +
   "<organization name='BMS1' />" +
   "<user value='gtezini@hotmail.com' />" +
   "<password value='P9JyRi3HsoieLjpyVrr7rWn8uyCyR12RlARE+5yN2/Y=' />" +
   "<table name='account' isNN='false' />" +
   "<records>" +
   "    <record>" +
   "        <fields>" +
   "            <field name='accountid' type='uniqueidentifier' value='NEWID' />" +
   "            <field name='name' type='string' value='Create sample 1...' />" +
   "            <field name='AccountNumber' type='string' value='1' />" +
   "        </fields>" +
   "    </record>" +
   "    <record>" +
   "        <fields>" +
   "            <field name='accountid' type='uniqueidentifier' value='NEWID' />" +
   "            <field name='name' type='string' value='Create sample 2...' />" +
   "            <field name='AccountNumber' type='string' value='2' />" +
   "        </fields>" +
   "    </record>" +
   "</records>" +
  "</insert>";

    var param = {
        "Type": "SET",
        "xmlContent": xmlINSERT
    };

    $.ajax({
        type: 'POST',
        url: "http://localhost:5000/api/BMS/QueryXML",
        data: param,
        dataType: 'json',
        contentType: "application/x-www-form-urlencoded; charset=utf-8",
        success: function (data, textStatus, jqXHR) {
        },
        error: function (jqXHR, textStatus, errorThrown) {
        }
    });

}

Para efetuar um UPDATE
function updateRecord() {

 var xmlUPDATE =
  "<update>" +
  " <organization name='BMS1' />" +
  " <user value='gtezini@hotmail.com' />" +
  " <password value='P9JyRi3HsoieLjpyVrr7rWn8uyCyR12RlARE+5yN2/Y=' />" +
  " <table name='account' />" +
  " <set>" +
  "  <field name='name' type='string' value='UPDATED - Create sample 1...' />" +
  " </set>" +
  " <filter type='and'>" +
  "  <condition field='name' operator='=' value='Create sample 1...' />" +
  " </filter>" +
  "</update>";

    var param = {
        "Type": "SET",
        "xmlContent": xmlUPDATE
    };

    $.ajax({
        type: 'POST',
        url: "http://localhost:5000/api/BMS/QueryXML",
        data: param,
        dataType: 'json',
        contentType: "application/x-www-form-urlencoded; charset=utf-8",
        success: function (data, textStatus, jqXHR) {
        },
        error: function (jqXHR, textStatus, errorThrown) {
        }
    });

}

Para efetuar um DELETE
function deleteRecord() {

 var xmlDELETE =
  "<delete>" +
  " <organization name='BMS1' />" +
  " <user value='gtezini@hotmail.com' />" +
  " <password value='P9JyRi3HsoieLjpyVrr7rWn8uyCyR12RlARE+5yN2/Y=' />" +
  " <table name='account' />" +
  " <filter type='and'>" +
  "  <condition field='name' operator='like' value='%Create sample%' />" +
  " </filter>" +
  "</delete>";

    var param = {
        "Type": "SET",
        "xmlContent": xmlDELETE
    };

    $.ajax({
        type: 'POST',
        url: "http://localhost:5000/api/BMS/QueryXML",
        data: param,
        dataType: 'json',
        contentType: "application/x-www-form-urlencoded; charset=utf-8",
        success: function (data, textStatus, jqXHR) {
        },
        error: function (jqXHR, textStatus, errorThrown) {
        }
    });

}

BMS GT - Consumo de Web Api usando C#

Este artigo tem como objetivo documentar o uso de um importante recurso do meu sistema de gerenciamento com o cliente, o BMS GT. Este recurso se chama Web Api, e serve como um [conector] entre o sistema BMS e qualquer sistema legado.

Esta é a URL principal de consumo REST: /api/BMS/QueryXML

Com este conector, você pode efetuar todo CRUD de operações. Portanto, segue abaixo o uso prático deste recurso, usando C#.

P.S.: Os exemplos são executados como se o serviço Web Api estivesse instalado no Endpoint [http://localhost:5000].

Para efetuar um SELECT

1 - Monte o "SELECT" através de uma instrução XML:

string xmlSELECT = @"
 <select>
  <organization name='{0}' />
  <user value='{1}' />
  <password value='{2}' />
  <table name='User' alias='tb1' />
  <fields>
   <field name='tb1.UserId' label='ID' />
   <field name='tb1.Name' />
   <field name='tb1.Email' />
   <field name='tb2.Name' />
   <field name='tb3.Name' />
  </fields>
  <join name='Status' alias='tb2' fieldFrom='statuscode' fieldTo='statuscode'>
   <filter type='and'>
    <condition field='tb2.name' operator='=' value='Ativo' />
   </filter>                     
  </join>
  <join name='salesorder' alias='tb3' fieldFrom='userid' fieldTo='ownerid'>
   <filter type='and'>
    <condition field='tb3.name' operator='like' value='%ped%' />
   </filter>                     
  </join>
  <filter type='and'>
   <condition field='tb1.name' operator='like' value='%usr%' />
  </filter>
 </select>";

O XML acima é a representação do SELECT-SQL abaixo:

SELECT tb1.UserId, tb1.Name, tb1.Email, tb2.Name, tb3.Name
FROM [User] tb1 JOIN [Status] tb2 ON tb1.statuscode = tb2.statuscode AND tb2.name = 'Ativo'
  JOIN [salesorder] tb3 ON TB1.UserId = tb3.ownerid AND tb3.name like '%ped%'
WHERE tb1.Name like '%usr%'

2 - Passe como parâmetro as informações de [Organização], [Usuário] e [Senha], conforme a sequência abaixo.

var xmlF = string.Format(xmlSELECT,
    "BMS1",
    "gtezini@hotmail.com",
    "P9JyRi3HsoieLjpyVrr7rWn8uyCyR12RlARE+5yN2/Y=");

3 - Execute a instrução, chamando a Web Api:
var result = GetStringByXMLPost("http://localhost:5000/api/BMS/QueryXML", "GET", xmlF, "application/x-www-form-urlencoded");

Segue abaixo o método [GetStringByXMLPost] que efetua chamadas HTTP:
public static string GetStringByXMLPost(string url, string type, string xml, string mediaType = "application/json")
{
 string urlContents = "";
 HttpResponseMessage result = null;
 var client = new HttpClient();

 try
 {
  Task.Run(async () =>
  {
   if (mediaType == "application/json")
   {
    var doc = new XMLDocumentModel()
    {
     Type = type,
     xmlContent = xml
    };

    var content = JsonConvert.SerializeObject(doc);
    var httpContent = new StringContent(content, Encoding.UTF8, mediaType);
    result = await client.PostAsync(new Uri(url), httpContent);
   }
   else {
    var keyValues = new List<KeyValuePair<string, string>>();
    keyValues.Add(new KeyValuePair<string, string>("Type", type));
    keyValues.Add(new KeyValuePair<string, string>("xmlContent", xml));
    var formContent = new FormUrlEncodedContent(keyValues);

    var request = new HttpRequestMessage(HttpMethod.Post, url);
    request.Content = formContent;
    result = await client.SendAsync(request);
   }

   urlContents = await result.Content.ReadAsStringAsync();
   client.Dispose();

  }).Wait();
 }
 catch (Exception ex)
 {
 }

 return urlContents;
}

P.S.: XMLDocumentModel é uma classe simples de apoio:
public class XMLDocumentModel
{
    public string Type { get; set; }
    public string xmlContent { get; set; }
}


Para efetuar um CREATE

1 - Monte o "CREATE" através de uma instrução XML:
string xmlINSERT = @"
    <insert>
        <organization name='{0}' />
        <user value='{1}' />
        <password value='{2}' />
        <table name='{3}' isNN='false' />
        <records>
            <record>
                <fields>
                    <field name='statuscode' type='int' value='998' />
                    <field name='name' type='string' value='test1...' />
                    <field name='ownerid' type='string' value='9EC0ABA8-2947-4857-9698-1C43BBBFAB5B' />
                </fields>
            </record>
            <record>
                <fields>
                    <field name='statuscode' type='int' value='999' />
                    <field name='name' type='string' value='test2...' />
                    <field name='ownerid' type='string' value='9EC0ABA8-2947-4857-9698-1C43BBBFAB5B' />
                </fields>
            </record>
        </records>
    </insert>";

2 - Passe como parâmetro as informações de [Organização], [Usuário], [Senha] e [tabela], conforme a sequência abaixo.
var xmlF = string.Format(xmlINSERT,
    "BMS1",
    "jack@gmail.com",
    "Rxkmrhz9ZFV7dFvA+k2wHIBO4yhOaULfqMTEzU7XlOw=",
    "lead");

3 - Execute a instrução, chamando a Web Api:
var result = GetStringByXMLPost("http://localhost:5000/api/BMS/QueryXML", "SET", xmlF, "application/x-www-form-urlencoded");


Para inserir dados em uma tabela N-N, adicione o atributo [isNN='true'] na tag xml [table], conforme exemplificado abaixo:
string xmlINSERT = @"
    <insert>
        <organization name='{0}' />
        <user value='{1}' />
        <password value='{2}' />
        <table name='{3}' isNN='true' />
        <records>
            <record>
                <fields>
                    <field name='pricelist_productid' type='uniqueidentifier' value='NEWID' />
                    <field name='pricelistid' type='uniqueidentifier' value='3BEF33F8-A159-4F77-964D-B820B8414E59' />
                    <field name='productid' type='uniqueidentifier' value='B091A317-4FF7-42BE-AD86-D77C22059E49' />
                </fields>
            </record>
            <record>
                <fields>
                    <field name='pricelist_productid' type='uniqueidentifier' value='NEWID' />
                    <field name='pricelistid' type='uniqueidentifier' value='3BEF33F8-A159-4F77-964D-B820B8414E59' />
                    <field name='productid' type='uniqueidentifier' value='6922B778-B4FF-4564-A992-E40A020C599F' />
                </fields>
            </record>
        </records>
    </insert>";


Para efetuar um UPDATE

1 - Monte o "UPDATE" através de uma instrução XML:
string xmlUPDATE = @"
    <update>
        <organization name='{0}' />
        <user value='{1}' />
        <password value='{2}' />
        <table name='{3}' />
        <set>
            <field name='name' type='string' value='TEST UPD...' />
        </set>
        <filter type='and'>
            <condition field='name' operator='=' value='TEST' />
        </filter>
    </update>";

2 - Passe como parâmetro as informações de [Organização], [Usuário], [Senha] e [tabela], conforme a sequência abaixo.
var xmlF = string.Format(xmlUPDATE,
    "BMS1",
    "jack@gmail.com",
    "Rxkmrhz9ZFV7dFvA+k2wHIBO4yhOaULfqMTEzU7XlOw=",
    "Status");

3 - Execute a instrução, chamando a Web Api:
var result = GetStringByXMLPost("http://localhost:5000/api/BMS/QueryXML", "SET", xmlF, "application/x-www-form-urlencoded");


Para efetuar um DELETE

1 - Monte o "DELETE" através de uma instrução XML:
string xmlDELETE = @"
    <delete>
        <organization name='{0}' />
        <user value='{1}' />
        <password value='{2}' />
        <table name='{3}' />
        <filter type='and'>
            <condition field='name' operator='=' value='TEST...' />
        </filter>
    </delete>";

2 - Passe como parâmetro as informações de [Organização], [Usuário], [Senha] e [tabela], conforme a sequência abaixo.
var xmlF = string.Format(xmlDELETE,
    "BMS1",
    "jack@gmail.com",
    "Rxkmrhz9ZFV7dFvA+k2wHIBO4yhOaULfqMTEzU7XlOw=",
    "Status");

3 - Execute a instrução, chamado a Web Api:
var result = GetStringByXMLPost("http://localhost:5000/api/BMS/QueryXML", "SET", xmlF, "application/x-www-form-urlencoded");


quarta-feira, 20 de setembro de 2017

Dynamics 365 Web Api - CRM REST Builder

Uma excelente ferramenta construtora de chamadas de Web Api está disponível no Github, chamada CRM REST Builder!

Com ela podemos acelerar e muito a construção das nossas queries, quais atributos usar (se são case sensitive, por exemplo), quais as actions e functions disponíveis, etc...

Baixe agora mesmo a [Solution] e importe no seu CRM - CRMRESTBuilder Solution - RELEASE


domingo, 10 de setembro de 2017

Um Tutorial para sua Primeira Aplicação AngularJS

O que é AngularJS?

AngularJS é um framework JavaScript MVC desenvolvido pelo Google, que permite que você construa aplicações front-end bem estruturadas, fácil de verificar e manter.

Por que eu deveria usá-lo?

Se ainda não experimentou o AngularJS, está perdendo. A estrutura consiste em um conjunto de ferramentas bem integradas que o ajudará a criar aplicativos bem estruturados e ricos do lado do cliente de forma modular, com menos código e mais flexibilidade.

O AngularJS amplia o HTML fornecendo diretrizes que adicionam funcionalidades à sua marcação e permitem que você crie modelos dinâmicos poderosos. Você também pode criar suas próprias diretrizes, criando componentes reutilizáveis ​​que preencham suas necessidades e abstraindo toda a lógica de manipulação de DOM.

Ele também implementa vinculação de dados bidirecionais, conectando seu HTML (visualizações) aos seus objetos JavaScript (modelos) de forma transparente. Em termos simples, isso significa que qualquer atualização em seu modelo será imediatamente refletida em sua visão sem a necessidade de manipulação de DOM ou manipulação de eventos (por exemplo, com jQuery).

Finalmente, adoro Angular devido à sua flexibilidade em relação à comunicação do servidor. Como a maioria dos frameworks MVC do JavaScript, ele permite que você trabalhe com qualquer tecnologia do lado do servidor, desde que possa atender seu aplicativo através de uma API da Web RESTful. Mas Angular também fornece serviços no topo do XHR que simplificam dramaticamente o seu código e permitem que você abstraia as chamadas da API em serviços reutilizáveis. Como resultado, você pode mover sua lógica de modelo e de negócios para o front-end e criar aplicativos web agnósticos back-end. Nesta publicação, faremos exatamente isso, um passo de cada vez.

Então, onde eu começo?

Primeiro, vamos decidir a natureza do aplicativo que queremos construir. Neste guia, preferimos não gastar muito tempo no back-end, então vamos escrever algo com base em dados facilmente acessíveis na Internet - como um aplicativo de alimentação esportiva!

Como eu sou um grande fã de automobilismo e Fórmula 1, usarei um serviço de API de autosport para atuar como nosso back-end. Felizmente, os caras da Ergast são gentis o suficiente para fornecer uma API gratuita de automobilismo que será perfeita para nós.

Para um pico furtivo no que vamos construir, dê uma olhada na demo ao vivo. Para embelezar a demonstração e mostrar alguns modelos angulares, apliquei um tema Bootstrap de WrapBootstrap, mas vendo que este artigo não é sobre CSS, vou resumi-lo dos exemplos e deixá-lo fora.

Tutorial de início

Vamos começar o nosso aplicativo de exemplo. Eu recomendo o projeto de sementes angulares, pois não só fornece um ótimo esqueleto para bootstrapping, mas também define o solo para teste de unidade com Karma e Jasmine (não vamos fazer nenhum teste nesta demo, então vamos apenas Deixar as coisas de lado por enquanto, consulte a Parte 2 deste tutorial para obter mais informações sobre a configuração do seu projeto para testes de unidade e de ponta a ponta).

EDIT (maio de 2014): desde que escrevi este tutorial, o projeto de sementes angulares passou por algumas mudanças pesadas (incluindo a adição de Bower como gerenciador de pacotes). Se você tiver dúvidas sobre como implantar o projeto, veja rapidamente a primeira seção de seu guia de referência. Na parte 2 do tutorial, o Bower , entre outras ferramentas, é coberto com maior detalhe.

OK, agora que clonamos o repositório e instalamos as dependências, o esqueleto do nosso aplicativo ficará assim:



Agora podemos começar a codificar. Como estamos tentando construir uma alimentação esportiva para um campeonato de corrida, vamos começar com a visão mais relevante: a tabela do campeonato.



Dado que já temos uma lista de drivers definida no nosso escopo (fique comigo - vamos chegar lá) e ignorando qualquer CSS (para legibilidade), nosso HTML pode parecer:

<body ng-app="F1FeederApp" ng-controller="driversController">
  <table>
    <thead>
      <tr><th colspan="4">Drivers Championship Standings</th></tr>
    </thead>
    <tbody>
      <tr ng-repeat="driver in driversList">
        <td>{{$index + 1}}</td>
        <td>
          <img src="img/flags/{{driver.Driver.nationality}}.png" />
          {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
        </td>
        <td>{{driver.Constructors[0].name}}</td>
        <td>{{driver.points}}</td>
      </tr>
    </tbody>
  </table>
</body>


A primeira coisa que você notará neste modelo é o uso de expressões ("{{" e "}}") para retornar valores variáveis. Em AngularJS, as expressões permitem que você execute alguma computação para retornar o valor desejado. Algumas expressões válidas seriam:

{{ 1 + 1 }}
{{ 946757880 | date }}
{{ user.name }}


Efetivamente, as expressões são snippets com JavaScript. Mas apesar de ser muito poderoso, você não deve usar expressões para implementar qualquer lógica de nível superior. Para isso, usamos diretrizes.

Compreender as Diretivas Básicas

A segunda coisa que você notará é a presença de ng-attributes, que você não veria na marcação típica. São diretrizes.

Em um nível alto, as diretivas são marcadores (como atributos, tags e nomes de classes) que indicam ao AngularJS para anexar um determinado comportamento a um elemento DOM (ou transformá-lo, substituí-lo, etc.). Vamos dar uma olhada nos que já vimos:

A ng-app diretiva é responsável por iniciar seu aplicativo definindo seu escopo. No AngularJS, você pode ter vários aplicativos na mesma página, então esta diretiva define onde cada aplicativo distinto começa e termina.
A ng-controller diretiva define qual controlador será responsável por sua visão. Nesse caso, denotamos o driversController, que fornecerá nossa lista de drivers ( driversList).
A ng-repeat diretriz é uma das mais usadas e serve para definir seu escopo de modelo ao fazer um loop pelas coleções. No exemplo acima, ele replica uma linha na tabela para cada driver em driversList.

Adicionando controladores

Claro, não há nenhum uso para nossa visão sem um controlador. Vamos adicionar driversController aos nossos controladores.js:

angular.module('F1FeederApp.controllers', []).
controller('driversController', function($scope) {
    $scope.driversList = [
      {
          Driver: {
              givenName: 'Sebastian',
              familyName: 'Vettel'
          },
          points: 322,
          nationality: "German",
          Constructors: [
              {name: "Red Bull"}
          ]
      },
      {
          Driver: {
          givenName: 'Fernando',
              familyName: 'Alonso'
          },
          points: 207,
          nationality: "Spanish",
          Constructors: [
              {name: "Ferrari"}
          ]
      }
    ];
});


Você pode ter notado a $scope variável que estamos passando como um parâmetro para o controlador. A $scope variável deve ligar o controlador e as visualizações. Em particular, ele contém todos os dados que serão usados ​​em seu modelo. Qualquer coisa que você adicionar a ele (como driversList no exemplo acima) será diretamente acessível em suas visualizações. Por enquanto, vamos trabalhar com uma matriz de dados estática, que nós vamos substituir mais tarde com nosso serviço API.

Agora, adicione isso ao app.js:

angular.module('F1FeederApp', [
  'F1FeederApp.controllers'
]);


Com esta linha de código, nós realmente inicializamos nosso aplicativo e registramos os módulos nos quais ele depende. Voltaremos a esse arquivo (app.js) mais tarde.

Agora, vamos colocar tudo em index.html:

<!DOCTYPE html>
<html>
<head>
  <title>F-1 Feeder</title>
</head>

<body ng-app="F1FeederApp" ng-controller="driversController">
  <table>
    <thead>
      <tr><th colspan="4">Drivers Championship Standings</th></tr>
    </thead>
    <tbody>
      <tr ng-repeat="driver in driversList">
        <td>{{$index + 1}}</td>
        <td>
          <img src="img/flags/{{driver.Driver.nationality}}.png" />
          {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
        </td>
        <td>{{driver.Constructors[0].name}}</td>
        <td>{{driver.points}}</td>
      </tr>
    </tbody>
  </table>
  <script src="bower_components/angular/angular.js"></script>
  <script src="bower_components/angular-route/angular-route.js"></script>
  <script src="js/app.js"></script>
  <script src="js/services.js"></script>
  <script src="js/controllers.js"></script>
</body>
</html>


Pequenos módulos, menores erros; agora você pode inicializar seu aplicativo e verificar sua lista (estática) de drivers.

Carregando dados do servidor

Como já sabemos exibir os dados do nosso controlador em nossa visão, é hora de buscar dados em tempo real de um servidor RESTful.

Para facilitar a comunicação com servidores HTTP, o AngularJS fornece os serviços $http e $resource. O primeiro é apenas uma camada em cima de XMLHttpRequest ou JSONP, enquanto o último fornece um nível de abstração mais alto. Nós usaremos $http.

Para abstrair as chamadas da API do servidor do controlador, vamos criar nosso próprio serviço personalizado que irá buscar nossos dados e atuar como um wrapper ao redor $http, adicionando isso ao nosso services.js:

angular.module('F1FeederApp.services', []).
  factory('ergastAPIservice', function($http) {

    var ergastAPI = {};

    ergastAPI.getDrivers = function() {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    return ergastAPI;
  });


Com as duas primeiras linhas, criamos um novo módulo (F1FeederApp.services) e registramos um serviço dentro desse módulo (ergastAPIservice). Observe que passamos $http como um parâmetro para esse serviço. Isso diz ao mecanismo de injeção de dependência da Angular que nosso novo serviço requer (ou depende) do serviço $http.

De forma semelhante, precisamos dizer ao Angular que inclua nosso novo módulo em nosso aplicativo. Vamos registrar com app.js, substituindo nosso código existente por:

angular.module('F1FeederApp', [
  'F1FeederApp.controllers',
  'F1FeederApp.services'
]);


Agora, tudo o que precisamos fazer é ajustar nosso controller.js um pouco, incluir ergastAPIservice como uma dependência e pronto:

angular.module('F1FeederApp.controllers', []).
  controller('driversController', function($scope, ergastAPIservice) {
    $scope.nameFilter = null;
    $scope.driversList = [];

    ergastAPIservice.getDrivers().success(function (response) {
        //Dig into the responde to get the relevant data
        $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings;
    });
  });


Agora recarregue o aplicativo e confira o resultado. Observe que não fizemos nenhuma alteração no nosso modelo, mas adicionamos uma nameFilter variável ao nosso escopo. Vamos colocar essa variável para usar.

Filtros

Ótimo! Temos um controlador funcional. Mas isso mostra apenas uma lista de drivers. Vamos adicionar algumas funcionalidades implementando uma simples entrada de pesquisa de texto que irá filtrar nossa lista. Vamos adicionar a seguinte linha ao nosso index.html, logo abaixo da tag:

<input type="text" ng-model="nameFilter" placeholder="Search..."/>


Agora estamos fazendo uso da diretiva ng-model. Esta diretiva vincula nosso campo de texto à $scope.nameFilter variável e garante que seu valor esteja sempre atualizado com o valor de entrada. Agora, vamos visitar index.html mais uma vez e fazer um pequeno ajuste na linha que contém a ng-repeat:

<tr ng-repeat="driver in driversList | filter: nameFilter">


Esta linha informa ao ng-repeat que, antes de enviar os dados, a driversList matriz deve ser filtrada pelo valor armazenado nameFilter.

Neste ponto, a ligação de dados bidirecionais é iniciada: sempre que um valor é inserido no campo de busca, Angular imediatamente garante que o $scope.nameFilter que associamos é atualizado com o novo valor. Uma vez que a ligação funciona de ambas as formas, no momento em que o nameFilter valor é atualizado, a segunda diretiva associada a ela (ou seja, a ng-repeat) também obtém o novo valor e a visualização é atualizada imediatamente.

Recarregue o aplicativo e confira a barra de pesquisa.



Observe que este filtro procurará a palavra-chave em todos os atributos do modelo, incluindo aqueles que não estamos usando. Vamos dizer que nós só desejamos filtrar Driver.givenName e Driver.familyName: Primeiro, adicione a driversController, logo abaixo da $scope.driversList = []; linha:

$scope.searchFilter = function (driver) {
    var keyword = new RegExp($scope.nameFilter, 'i');
    return !$scope.nameFilter || keyword.test(driver.Driver.givenName) || keyword.test(driver.Driver.familyName);
};


Agora, de volta index.html, atualizamos a linha que contém a diretiva ng-repeat:

<tr ng-repeat="driver in driversList | filter: searchFilter">


Recarregue o aplicativo mais uma vez e agora temos uma pesquisa por nome.

Rotas

Nosso próximo objetivo é criar uma página de detalhes do driver que nos permita clicar em cada driver e ver os detalhes de sua carreira.

Primeiro, vamos incluir o serviço $routeProvider (em app.js) que nos ajudará a lidar com essas variadas rotas de aplicação. Então, vamos adicionar duas dessas rotas: uma para a tabela do campeonato e outra para os detalhes do driver. Aqui está o nosso novo app.js:

angular.module('F1FeederApp', [
  'F1FeederApp.services',
  'F1FeederApp.controllers',
  'ngRoute'
]).
config(['$routeProvider', function($routeProvider) {
  $routeProvider.
 when("/drivers", {templateUrl: "partials/drivers.html", controller: "driversController"}).
 when("/drivers/:id", {templateUrl: "partials/driver.html", controller: "driverController"}).
 otherwise({redirectTo: '/drivers'});
}]);


Com essa mudança, navegue para http://domain/#/drivers, carregue o driversController e procure a visão parcial para renderizar partials/drivers.html. Mas espere! Ainda não temos visualizações parciais, certo? Nós também precisamos criar esses.

Visões parciais

O AngularJS permitirá que você vincule suas rotas a controladores e visualizações específicos.

Mas primeiro, precisamos dizer a Angular para onde renderizar essas visualizações parciais. Para isso, usaremos a diretiva ng-view, modificando nossa index.html para refletir o seguinte:

<!DOCTYPE html>
<html>
<head>
  <title>F-1 Feeder</title>
</head>

<body ng-app="F1FeederApp">
  <ng-view></ng-view>
  <script src="bower_components/angular/angular.js"></script>
  <script src="bower_components/angular-route/angular-route.js"></script>
  <script src="js/app.js"></script>
  <script src="js/services.js"></script>
  <script src="js/controllers.js"></script>
</body>
</html>


Agora, sempre que navegarmos pelas rotas do nosso aplicativo, Angular irá carregar a visão associada e renderizá-la no lugar da tag. Tudo o que precisamos fazer é criar um arquivo chamado partials/drivers.html e colocar nossa tabela de campeonato HTML lá. Nós também usaremos essa chance de vincular o nome do driver com a rota de detalhes do driver:

<input type="text" ng-model="nameFilter" placeholder="Search..."/>
<table>
<thead>
  <tr><th colspan="4">Drivers Championship Standings</th></tr>
</thead>
<tbody>
  <tr ng-repeat="driver in driversList | filter: searchFilter">
    <td>{{$index + 1}}</td>
    <td>
      <img src="img/flags/{{driver.Driver.nationality}}.png" />
      <a href="#/drivers/{{driver.Driver.driverId}}">
    {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
   </a>
 </td>
    <td>{{driver.Constructors[0].name}}</td>
    <td>{{driver.points}}</td>
  </tr>
</tbody>
</table>


Finalmente, vamos decidir o que queremos mostrar na página de detalhes. Que tal um resumo de todos os fatos relevantes sobre o piloto (por exemplo, nascimento, nacionalidade), juntamente com uma tabela contendo seus resultados recentes? Para fazer isso, adicionamos a services.js:

angular.module('F1FeederApp.services', [])
  .factory('ergastAPIservice', function($http) {

    var ergastAPI = {};

    ergastAPI.getDrivers = function() {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    ergastAPI.getDriverDetails = function(id) {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    ergastAPI.getDriverRaces = function(id) {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/results.json?callback=JSON_CALLBACK'
      });
    }

    return ergastAPI;
  });


Desta vez, fornecemos a identificação do driver para o serviço para que possamos recuperar as informações relevantes apenas para um driver específico. Agora, modificamos controllers.js:

angular.module('F1FeederApp.controllers', []).

  /* Drivers controller */
  controller('driversController', function($scope, ergastAPIservice) {
    $scope.nameFilter = null;
    $scope.driversList = [];
    $scope.searchFilter = function (driver) {
        var re = new RegExp($scope.nameFilter, 'i');
        return !$scope.nameFilter || re.test(driver.Driver.givenName) || re.test(driver.Driver.familyName);
    };

    ergastAPIservice.getDrivers().success(function (response) {
        //Digging into the response to get the relevant data
        $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings;
    });
  }).

  /* Driver controller */
  controller('driverController', function($scope, $routeParams, ergastAPIservice) {
    $scope.id = $routeParams.id;
    $scope.races = [];
    $scope.driver = null;

    ergastAPIservice.getDriverDetails($scope.id).success(function (response) {
        $scope.driver = response.MRData.StandingsTable.StandingsLists[0].DriverStandings[0]; 
    });

    ergastAPIservice.getDriverRaces($scope.id).success(function (response) {
        $scope.races = response.MRData.RaceTable.Races; 
    }); 
  });


O importante para notar aqui é que acabamos de injetar o serviço $routeParams no controlador do driver. Este serviço nos permitirá acessar nossos parâmetros de URL (para o id, neste caso) usando $routeParams.id.

Agora que temos nossos dados no escopo, precisamos apenas da visão parcial restante. Vamos criar um arquivo chamado partials/driver.html e adicionar:

<section id="main">
  <a href="./#/drivers"><- Back to drivers list</a>
  <nav id="secondary" class="main-nav">
    <div class="driver-picture">
      <div class="avatar">
        <img ng-show="driver" src="img/drivers/{{driver.Driver.driverId}}.png" />
        <img ng-show="driver" src="img/flags/{{driver.Driver.nationality}}.png" /><br/>
        {{driver.Driver.givenName}} {{driver.Driver.familyName}}
      </div>
    </div>
    <div class="driver-status">
      Country: {{driver.Driver.nationality}}   <br/>
      Team: {{driver.Constructors[0].name}}<br/>
      Birth: {{driver.Driver.dateOfBirth}}<br/>
      <a href="{{driver.Driver.url}}" target="_blank">Biography</a>
    </div>
  </nav>

  <div class="main-content">
    <table class="result-table">
      <thead>
        <tr><th colspan="5">Formula 1 2013 Results</th></tr>
      </thead>
      <tbody>
        <tr>
          <td>Round</td> <td>Grand Prix</td> <td>Team</td> <td>Grid</td> <td>Race</td>
        </tr>
        <tr ng-repeat="race in races">
          <td>{{race.round}}</td>
          <td><img  src="img/flags/{{race.Circuit.Location.country}}.png" />{{race.raceName}}</td>
          <td>{{race.Results[0].Constructor.name}}</td>
          <td>{{race.Results[0].grid}}</td>
          <td>{{race.Results[0].position}}</td>
        </tr>
      </tbody>
    </table>
  </div>

</section>


Note que estamos agora colocando a directiva ng-show em bom uso. Esta diretiva mostrará apenas o elemento HTML se a expressão fornecida for true (ou seja, nem false, nem null). Nesse caso, o avatar só será exibido uma vez que o objeto do driver tenha sido carregado no escopo pelo controlador.

Toques finais

Adicione um monte de CSS e renderize sua página. Você deve acabar com algo como isto:



Agora você está pronto para ativar seu aplicativo e certifique-se de que ambas as rotas funcionem conforme desejado. Você também pode adicionar um menu estático index.html para melhorar as capacidades de navegação do usuário. As possibilidades são infinitas.

EDIT (maio de 2014): recebi muitos pedidos para uma versão para download do código que criamos neste tutorial. Por isso, decidi liberá-lo AQUI (despojado de qualquer CSS). No entanto, eu realmente não recomendo baixá-lo, pois este guia contém todas as etapas que você precisa para construir o mesmo aplicativo com suas próprias mãos, o que será um exercício de aprendizagem muito mais útil e eficaz.

Conclusão

Neste ponto do tutorial, cobrimos tudo o que você precisaria para escrever um aplicativo simples (como um alimentador de Fórmula 1). Cada uma das páginas restantes da demonstração ao vivo (por exemplo, tabela de campeão do construtor, detalhes da equipe, calendário) compartilham a mesma estrutura e conceitos básicos que revisamos aqui.

Finalmente, tenha em mente que a Angular é uma estrutura muito poderosa e que mal conseguimos arranhar a superfície em termos de tudo o que tem para oferecer. Na Parte 2 deste tutorial, daremos exemplos de por que Angular se destaca entre seus frameworks MVC front-end: testabilidade. Analisaremos o processo de construção e execução de testes unitários com o Karma, conseguindo uma integração contínua com Yeomen, Grunt e Bower e outros pontos fortes desta fantástica estrutura de front-end.

POR RAONI BOAVENTURA - ENGENHEIRO JAVASCRIPT @ TOPTAL