Tagjquery

AngularJs – Como começar se já sei jQuery

1. Não projete a sua página, para depois alterá-la com manipulações DOM

Em jQuery, você cria uma página, e então você começa a torná-la dinâmica. Isso ocorre porque o jQuery foi projetado para o acréscimo e tem crescido incrivelmente partir dessa premissa simples.

Por outro lado, em AngularJS, você deve começar a partir do zero, com sua arquitetura em mente. Em vez de começar por pensar “eu tenho esse pedaço do DOM e eu quero fazê-lo fazer X”, você tem que começar com o que você quer realizar, em seguida, ir sobre o projeto de sua aplicação, e, finalmente, ir sobre o projeto de seu ponto de vista.

2. Não aumente jQuery com AngularJS

Da mesma forma, não comece com a idéia de que o jQuery faz X, Y e Z, por isso vou adicionar AngularJS em cima disso para os models e controllers. Isso é muito tentador quando você está apenas começando, e é por isso que sempre é recomendo que os novos desenvolvedores AngularJS não utilizem jQuery em tudo, pelo menos até que se acostumarem a fazer as coisas da “Jeito Angular”.

É muito mais comum do que se pensa ver desenvolvedores criando soluções elaboradas com plugins jQuery de 150 ou 200 linhas de código que, depois, colá-las em AngularJS com uma coleção de callbacks e $applys que são confusas e complicadas, mas, eles eventualmente vão conseguir fazer isso funcionar! O problema é que na maioria dos casos esse plugin jQuery poderia ser reescrito em AngularJS em uma fração do código, onde de repente tudo se torna compreensível e simples.

A questão de fundo é esta: quando você está projetando uma solução, primeiro “pense em AngularJS”, se você não pode pensar em uma solução, faça uma pesquisa no google, vá em busca de ajuda na comunidade, se depois de tudo isso, você não conseguir encontrar uma solução, então sinta-se livre para utilizar o jQuery. Mas não deixe que jQuery tornar-se uma muleta ou você nunca vai dominar AngularJS.

3. Sempre pense em termos de arquitetura

Em primeiro lugar saber que as aplicações de uma única página são aplicações. Eles não são páginas da web. Então, precisamos pensar como um desenvolvedor do lado do servidor(server-side), além de pensar como um desenvolvedor do lado do cliente(client-side). Temos que pensar em como dividir a nossa aplicação, em, componentes testáveis, extensíveis e individuais.

Então, como você faz isso? Como você “pensa em AngularJS”? Aqui estão alguns princípios gerais, em contraste com jQuery.

3.1 O ponto de vista é o “official record

Em jQuery, nós programaticamente alteramos nossa view. Poderíamos ter um menu dropdown definido como um ul assim:

<ul class="main-menu">  
    <li class="active">
        <a href="#">Home</a>
    </li>
    <li>
        <a href="#">Menu 1</a>
        <ul>
            <li><a href="#">Submenu 1</a></li>
            <li><a href="#">Submenu 2</a></li>
            <li><a href="#">Submenu 3</a></li>
        </ul>
    </li>
    <li>
        <a href="#">Menu 2</a>
    </li>
</ul>  

Em jQuery, em nossa lógica de aplicação, gostaríamos de ativá-lo com algo como:

$('.main-menu').dropdownMenu();

Quando nós basta olhar para o ponto de vista, não é imediatamente óbvio que há alguma funcionalidade aqui. Para pequenas aplicações, isso é bom. Mas para aplicações não-triviais, as coisas rapidamente se confuso e difícil de manter.

Em AngularJS, porém, a vista é o official record de funcionalidade view-based. Nossa declaração ul ficaria assim:

<ul class="main-menu" dropdown-menu>  
    ...
</ul>  

Estes dois fazem a mesma coisa, mas na versão AngularJS alguém olhando para o model sabe o que é deverá acontecer. Sempre que um novo membro da equipe de desenvolvimento vem a bordo, ela pode olhar para isso e, em seguida, saber que existe uma diretiva chamado DropDownMenu operando nela, ela não precisa intuir a resposta certa ou vasculhar todo o código. A visão nos disse o que era suposto acontecer. Muito mais limpo.

Desenvolvedores novos em AngularJS muitas vezes perguntam algo como: como faço para encontrar todos os links de um tipo específico e adicionar uma diretiva sobre eles. O desenvolvedor é sempre espantado quando têm sua pergunta respondida: Você não… Mas a razão pela qual você não deve fazer isso é que isto é como meia jQuery, meio-AngularJS, e não é bom. O problema aqui é que o desenvolvedor está tentando “fazer jQuery” no contexto da AngularJS. Isso nunca vai funcionar bem. A vista é o official record. Fora de uma diretiva (mais sobre isso abaixo), você nunca, nunca, nunca muda o DOM. E as diretrizes são aplicadas na visão, por isso a intenção é clara.

Lembre-se: não se projetar, e, em seguida, marcar. Você deve arquiteto e design.

3.2 Data binding

Esta é, de longe, uma das características mais impressionantes do AngularJS e diminui muito a necessidade de fazer o tipo de manipulações DOM que foram mencionadas na seção anterior. AngularJS irá atualizar automaticamente a sua visão, logo, você não precisa fazer isso! Em jQuery, nós devemos esperar a resposta de um evento e, em seguida atualizar o conteúdo. Algo como:

$.ajax({
  url: '/myEndpoint.json',
  success: function ( data, status ) {
    $('ul#log').append('<li>Data Received!</li>');
  }
});

Para a view teríamos algo parecido com:

<ul class="messages" id="log">  
</ul>  

Além das interesses de mistura, também temos os mesmos problemas de significar a intenção que foram mencionadas anteriormente. Mas o mais importante, tivemos a referência e atualizar um nó DOM manualmente. E se quisermos eliminar uma entrada de log, temos de código contra o DOM para isso também. Como é que podemos testar a lógica além do DOM? E se nós queremos mudar a apresentação?

Este pouco confuso e um pouco frágil. Mas, em AngularJS, podemos fazer isso dessa maneira:

$http( '/myEndpoint.json' ).then( function ( response ) {
    $scope.log.push( { msg: 'Data Received!' } );
});

Para a view teríamos algo parecido com:

<ul class="messages">  
    <li ng-repeat="entry in log">{{ entry.msg }}</li>
</ul>  

Mas, também poderíamos ter a view dessa maneira:

<div class="messages">  
    <div class="alert" ng-repeat="entry in log">
        {{ entry.msg }}
    </div>
</div>  

E agora, em vez de utilizarmos uma lista não ordenada, estamos usando caixas de alerta do Twitter Bootstrap. E nunca se fez necessária qualquer alteração no código do controlador! Mas o mais importante, não importa onde ou como o registro é atualizado, a visão vai mudar também. Automaticamente.

Embora não tenha exibido o mesmo aqui, a ligação de dados é bidirecional. Então, essas mensagens de log também podem ser editadas na exibição apenas fazendo isso:

<input ng-model="entry.msg" />  

3.3 Camada modelo distinto

Em jQuery, o DOM é uma espécie de model. Mas, em AngularJS, temos uma camada de modelo separada que nós podemos controlar de qualquer forma que for necessária, de forma completamente independente do ponto de vista. Isso ajuda para a ligação de dados acima, mantém a separação de interesses, e apresenta muito maior testabilidade.

3.4 Separação de interesses

Você deve manter as seus interesses em separado. Sua visão funciona como o official record do que é deve acontecer (pelo menos, na maioria das vezes), o model representa os dados, você tem uma camada de serviço para executar tarefas reutilizáveis; você faz manipulação DOM e acrescenta a sua view com as diretivas, e você junta tudo isso com os controladores.

A única coisa que se faz necessário acrescentar pertence à testabilidade, que será discutida em outro trecho mais abaixo.

3.5 Injeção de Dependência

Para nos ajudar com separação de interesses é a injeção de dependência(DI). Se você vem de uma linguagem server-side (de Java à PHP), você provavelmente está familiarizado com este conceito já, mas se você é um cara do lado do cliente vindo de jQuery, este conceito pode parecer bobo. Mas não é.

A partir de uma perspectiva ampla, DI significa que você pode declarar componentes muito livremente e, em seguida, a partir de qualquer outro componente, basta pedir uma instância dele e ele vai será concedido. Você não tem que saber sobre a ordem de carregamento, ou locais de arquivo, ou qualquer coisa assim. O poder não pode ser imediatamente visível, um exemplo comum seria teste.

Vamos dizer que em sua aplicação, precisamos de um serviço que implementa o armazenamento do lado do servidor por meio de uma API REST e, dependendo do estado do aplicativo, armazenamento local também. Ao executar testes em nossos controladores, não quero ter que comunicar com o servidor – estamos testando o controlador, depois de tudo. Nós podemos apenas adicionar um serviço de simulação do mesmo nome do nosso componente original, e, o injector irá garantir que o nosso controlador recebe um falso automaticamente – o nosso controlador não precisa e não saberá a diferença.

4 Desenvolvimento dirigido a testes (TDD) – Sempre

É impossível negar que isso é realmente parte da seção 3, na arquitetura, mas é tão importante que é melhor ser colocado em sua própria seção.

Fora de todos os muitos plugins jQuery que você já viu, utilizou, ou escreveu, quantos deles tinham um conjunto de testes que o acompanha? Não muitos, porque jQuery não é muito favorável a isso. Mas AngularJS é.

Em jQuery, a única maneira de testar é muitas vezes para criar o componente de forma independente com uma página de amostra / demo contra o qual nossos testes pode executar a manipulação de DOM. Então nós temos que desenvolver um componente separadamente e depois integrá-lo em nossa aplicação. Assim, grande parte do tempo, ao desenvolver com jQuery, optamos por uma maneira de desenvolvimento iterativa, em vez de dirigida a testes(tdd).

Mas porque temos separação de interesses, podemos fazer o desenvolvimento orientado a testes de forma iterativa em AngularJS! Por exemplo, vamos dizer que queremos uma diretiva super-simples para indicar nosso menu que nossa rota atual. Podemos declarar o que queremos em nossa view:

<a href="#" when-active>Hello</a>  

Agora você pode escrever o seu teste:

it( 'should add "active" when the route changes', inject(function() {  
    var elm = $compile( '<a href="/hello" when-active>Hello</a>' )( $scope );

    $location.path('/not-matching');
    expect( elm.hasClass('active') ).toBeFalsey();

    $location.path( '/hello' );
    expect( elm.hasClass('active') ).toBeTruthy();
}));

Rodando o teste podemos confirmar que ele falha. Então agora podemos escrever a nossa diretiva:

.directive( 'whenActive', function ( $location ) {
    return {
        scope: true,
        link: function ( scope, element, attrs ) {
            scope.$on( '$routeChangeSuccess', function () {
                if ( $location.path() == element.attr( 'href' ) ) {
                    element.addClass( 'active' );
                }
                else {
                    element.removeClass( 'active' );
                }
            });
        }
    };
});

Agora o teste passa e nosso menu executa conforme solicitado. Nosso desenvolvimento é iterativo e dirigido a testes.

5. Conceitualmente, as diretivas não são “pacotes” jQuery

Você vai ouvir muitas vezes “só fazem manipulação DOM em uma diretiva”. Isso é uma necessidade. Então, trate com o devido respeito!

Algumas diretivas apenas decoram o que já está na view (pense em ngClass) e, portanto, às vezes, fazer manipulação DOM imediatamente e, em seguida, são basicamente feito. Mas, se a diretiva é como um “widget” e tem um model, ele também deve respeitar a separação de interesses. Ou seja, o model também deve permanecer em grande parte independente da sua implementação nos links e controllers.

AngularJS vem com um conjunto completo de ferramentas para fazer isto muito facilmente, com ngClass podemos atualizar dinamicamente a classe; ngBind permite a ligação de dados bidirecional; ngShow e ngHide programaticamente mostrar ou ocultar um elemento, e muitos mais – inclusive os que escrevemos nós mesmos. Em outras palavras, nós podemos fazer todos os tipos de grandiosidade sem manipulação DOM. Quanto menos manipulação DOM, será mais fácil testar as suas diretivas, será mais fácil estilizá-las, será mais fácil de modificá-las no futuro, e mais re-utilizável e distribuível eles são.

É fácil perceber que muitos desenvolvedores novos em AngularJS utilizando diretivas como o lugar para jogar um monte de jQuery. Em outras palavras, eles pensam “já que eu não posso fazer manipulação DOM no controlador, eu vou levar esse código colocá-lo numa diretiva”. Enquanto que, certamente, é muito melhor, muitas vezes é ainda errado.

Pense no logger da seção 3. Mesmo que você coloque isso em uma diretiva, você ainda pode fazê-lo do “jeito Angular”. Ele ainda não requer qualquer manipulação DOM! Há muitas ocasiões em que a manipulação DOM é necessária, mas é muito mais raro do que você pensa! Antes de fazer a manipulação DOM em qualquer lugar em seu aplicativo, pergunte-se se você realmente precisa. Pode haver uma maneira melhor.

Aqui está um exemplo que mostra o padrão que possivelmente será encontrado com mais freqüência. Queremos um botão alternável. Em tempo, este exemplo é um pouco artificial para representar os casos mais complicados, que são resolvidos da mesma maneira.

.directive( 'myDirective', function () {
    return {
        template: '<a class="btn">Toggle me!</a>',
        link: function ( scope, element, attrs ) {
            var on = false;

            $(element).click( function () {
                if ( on ) {
                    $(element).removeClass( 'active' );
                }
                else {
                    $(element).addClass( 'active' );
                }

                on = !on;
            });
        }
    };
});

Existem algumas coisas de errado com isso. Primeiro, jQuery nunca foi necessário. Não há nada que fiz aqui que precisava jQuery! Segundo, mesmo que já utilizássemos jQuery na nossa página, não há nenhuma razão para usá-lo aqui, podemos simplesmente utilizar o angular.element e nosso componente continuará funcionando mesmo em um projeto que não tem jQuery. Em terceiro lugar, mesmo assumindo jQuery foi necessário para a presente directiva para o trabalho, jqLite(angular.element) sempre utilizará jQuery se ele foi carregado! Então, não precisamos usar o $ – podemos apenas usar angular.element. O quarto lugar, está intimamente relacionado com a terceiro, é que os elementos jqLite não necessitam estar envolvidos com $ – o elemento que é passado para a função de ligação já constituiria um elemento jQuery! E quinto, que já foi mencionado em seções anteriores, porque estamos misturando coisas do modelo em nossa lógica?

Esta diretiva poderia ser reescrita de maneira mais simples, assim:

.directive( 'myDirective', function () {
    return {
        scope: true,
        template: '<a class="btn" ng-class="{active: on}" ng-click="toggle()">Toggle me!</a>',
        link: function ( scope, element, attrs ) {
            scope.on = false;

            scope.toggle = function () {
                scope.on = !$scope.on;
            };
        }
    };
});

Mais uma vez, tudo que for modelo deve ficar no modelo, de modo que você (ou seus usuários) pode facilmente trocá-lo por um que atenda a qualquer estilo necessário, e a lógica nunca teve de ser tocado. Reutilização.

E ainda há todos os outros benefícios, como o teste – é fácil! Não importa o que está no modelo, API interna da diretiva nunca é tocada, dessa maneira a refatoração é fácil. Você pode alterar o modelo, tanto quanto você quiser, sem tocar na directiva. E não importa o que você mude, seus testes ainda passar.

Então, se diretivas não são apenas coleções de funções jQuery, quais são eles? Diretivas são extensões de HTML. Se o HTML não fizer algo que você precisa fazer, você escreve uma diretiva para fazer isso por você e, em seguida, passa a utilizá-la como se fosse parte de HTML.

Colocando isso de outra maneira, se AngularJS não fazer algo, pense em como a equipe iria realizá-lo para combinar perfeitamente com ngClick, ngClass…

JQuery – Como verificar se um elemento existe

Este é um post muito pequeno, mas apenas algo que eu queria apontar. O método is() do jQuery irá retornar verdadeiro se qualquer um dos elementos do conjunto atual corresponde a qualquer um dos elementos do conjunto é baseada (como definido pelo seletor dado). Normalmente, se você está trabalhando com apenas um único elemento, não há problema, mas, se sua coleção jQuery atual contém vários elementos, você pode obter um resultado não intencional.

$(selector).is('*')

JQuery – Como desativar botão de submit depois de enviado

No desenvolvimento Web, existe um problema muito comum, que é quando o formulário é enviado duplicado. Muitas vezes, os usuários gostam de pressionar botão de enviar mais de uma vez para garantir que o botão foi clicado com certeza, e causando o problema submissão dupla do formulário.

A solução mais comum é desativadar o botão de enviar após o usuário clicar sobre ele, podendo também, exibir uma mensagem de carregamento, de forma que o usuário saiba que o formulário já foi submetido essa mensagem também pode sobrepor a tela, dessa forma o usuário não conseguirá clicar novamente no submit.

Desativando o botão enviar
Para desativar o botão de enviar com o jQuery, você só precisa adicionar um atributo de “disabled” para o botão de envio.

$('input[type=submit]').click(function() {
    $(this).attr('disabled', 'disabled');
    $(this).parents('form').submit()
});

Ativando o botão de enviar
Para ativar de volta o botão enviar desativado, defina o atributo “disabled” para falso, ou remova o atributo “disabled”.

$('input[type=submit]').attr("disabled", false);    
// ou
$('input[type=submit]').removeAttr("disabled");

© 2017 Adler Dias

Theme by Anders NorénUp ↑