segunda-feira, 21 de outubro de 2013

Curso de Criação de Apps Móveis com PhoneGap e jQuery Mobile - Licão 3


Você já viu o PhoneGap / Cordova e o que ele pode fazer por você. Agora, é hora de dar uma "melhorada" na cara dessas páginas com o jQuery Mobile!

Baixe os arquivos correspondentes a esta aula. A pasta completa está em: 






O trabalho "Criação de aplicações Móveis com PhoneGap e jQuery Mobile" de Cleuton Sampaio de Melo Jr está licenciado com uma Licença Creative Commons - Atribuição-CompartilhaIgual 4.0 Internacional. Isso inclui: Textos, páginas, gráficos e código-fonte.

jQuery Mobile


O jQuery já é conhecido da maioria dos desenvolvedores, sendo um framework para lidar com o DOM da página e para fazer requests assíncronos.

O jQuery Mobile é um framework de UI HTML 5, otimizado para dispositivos de toque na tela, e criado para desenvolver web sites e aplicações móveis responsivas, que são acessadas por qualquer smartphone, tablet ou mesmo desktop.


E por que eu o utilizaria?

Bem, vamos deixar as evidências falarem por si só. Vamos pegar o último exercício que fizemos. Eis a tela do emulador:

Agora, vamos ver a tela do emulador do mesmo exercício, feito com o auxílio do jQuery Mobile:



Agora, vamos ver a tela do emulador do mesmo exercício, feito com o auxílio do jQuery Mobile:


Tem uma grande diferença, não? Veja como o layout se adaptou bem ao tamanho da tela do dispositivo... Isso é responsividade, e sem o menor esforço meu.

Para rodar esse exercício, substitua a página “index.html”, que está dentro de “assets/www” no projeto “exercício”, pelo arquivo “index.html” que está na pasta “sessao3”.

Eis o código da página “index.html” nova:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="format-detection" content="telephone=no" />
<meta name="viewport" content="width=device-width, initial-scale=1">        
        <meta name="msapplication-tap-highlight" content="no" />
<link rel="stylesheet" 
href="http://code.jquery.com/mobile/1.4.4/jquery.mobile-1.4.4.min.css" />
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script 
src="http://code.jquery.com/mobile/1.4.4/jquery.mobile-1.4.4.min.js"></script>
        <script type="text/javascript" src="cordova.js"></script>
        <script type="text/javascript" src="js/index.js"></script>
        <script type="text/javascript">
            app.initialize();
        </script>
        <title>Hello World</title>
    </head>
    <body>
    <div data-role="page">
<div data-role="header">...</div>
<div role="main" class="ui-content">
              <h1>Insira registros</h1>
<br/>ID: <input type="number" id="id" min="1" />
<br/>Texto: <input type="text" id="data" />
<br/><input type="button" id="btnincluir" value="Incluir"  
onclick="inserir()" />
<div id="saida">
</div>
</div>
<div data-role="footer">...</div>
</div>

    </body>    
</html>

Aqui, só “arranhamos” a superfície do jQuery Mobile...

Para usar o jQuery mobile, temos que incluir 3 elementos em nosso HTML:
  1. Um CSS compatível com o jQuery mobile, que pode ser o distribuído via CDN:
<link rel="stylesheet"
href="http://code.jquery.com/mobile/1.4.4/jquery.mobile-1.4.4.min.css" />
  1. O Script do jQuery, que pode ser também o distribuído via CDN:
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
  1. O Script do jQuery Mobile, idem:
</script>

Você também pode baixar os arquivos e instalar em sua aplicação. Copie os arquivos que estão dentro de “jquery-mobile” para dentro de: “assets/www/js”, e altere os links na página “index.html” para o caminho relativo: “js/...”.

Uma página jQuery Mobile

Uma página no jQuery Mobile consiste de um elemento com o atributo: data-role="page". Dentro de um container “page”, qualquer código HTML válido pode ser utilizado. Por exemplo:


<body>
<div data-role="page">
<h3>Bom dia</h3>
</div>
</body>

O jQuery Mobile inclui um sistema de navegação, que “sequestra” os cliques em links e submissão de formulários, transformando-os em requests Ajax.

Sempre que o usuário clica em um link ou submete um formulário, isso é imediatamente interceptado pelo sistema de navegação Ajax, e é usado para criar um request assíncrono, baseado na URL do formulário ou do link, ao invés de carregar a página toda, como seria o comportamento normal.


Quando o request termina e a resposta é recebida, o jQuery Mobile busca no documento um elemento com o atributo: data-role="page", inserido o código da resposta no DOM da página original. Em seguida, quaisquer “widgets” contidos na resposta recebem o comportamento e os estilos da página. O resto da resposta é descartado, incluindo: Scripts e estilos. É uma maneira simples e elegante de implementar a arquitetura SPA – Single Page Application.

Depois que a resposta foi incorporada no DOM da página original, é apresentada através de uma animação, controlada por uma “transição”. Por default, é aplicada a “fade transition”, na qual a página esvanece e a outra aparece.

Vamos ver um exemplo no projeto “jq1”, dentro da sessão 3.





Examinando o código fonte da página “index.html”, temos o seguinte trecho:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="format-detection" content="telephone=no" />
<meta name="viewport" content="width=device-width, initial-scale=1">      
        <meta name="msapplication-tap-highlight" content="no" />
<link rel="stylesheet" href="js/jquery.mobile-1.4.4.min.css" />
<script src="js/jquery-2.1.1.min.js"></script>
<script src="js/jquery.mobile-1.4.4.min.js"></script>
        <script type="text/javascript" src="cordova.js"></script>
        <script type="text/javascript" src="js/index.js"></script>
        <script type="text/javascript">
            app.initialize();
        </script>
        <title>Pg 1</title>
    </head>
    <body>
    <div data-role="page" id="inicial">
<div data-role="header">...</div>
<div role="main" class="ui-content">
           <h1>Página original</h1>
           <ul>
            <li><a href="pagina2.html" data-transition="flip">
Página 2 - outro arquivo</a></li>
            <li><a href="#pg2segunda" data-transition="pop">
segunda página interna</a></li>
            <li><a href="#pg2popup" data-transition="pop"
data-rel="dialog">Popup interno</a></li>
           </ul>          
</div>
<div data-role="footer">...</div>
</div>

Temos uma página, com id=”incial”, e nela, temos um link para outro arquivo (“pagina2.html”). Ao navegar, o que acontece?
  • É feito um request Ajax para a página2.html;
  • Quando a resposta chegar, o jQuery Mobile analisará o conteúdo, buscando por uma “page”. Ao encontrar, ele sobreporá o seu conteúdo dentro do conteúdo da “page” com id “inicial”;
  • Uma transição será iniciada (animação), neste caso é um “flip” (data-transition="flip");
Veja mais sobre transições em: http://demos.jquerymobile.com/1.4.4/transitions/;

Páginas internas

Se você não quiser criar páginas separadas, pode colocar tudo dentro de “index.html”, separando as várias “pages”. Veja a continuação do arquivo “index.html”:

     <div data-role="page" id="inicial">
<div data-role="header">...</div>
<div role="main" class="ui-content">
           <h1>Página original</h1>
           <ul>
            <li><a href="pagina2.html" data-transition="flip">
Página 2 - outro arquivo</a></li>
            <li><a href="#pg2segunda" data-transition="pop">
segunda página interna</a></li>
            <li><a href="#pg2popup" data-transition="pop" 
data-rel="dialog">Popup interno</a></li>
           </ul>            
</div>
<div data-role="footer">...</div>
</div>
<div data-role="page" id="pg2segunda">
<div data-role="header">...</div>
<div role="main" class="ui-content">
           <h1>Segunda página interna</h1>
           <a href="#inicial" data-transition="slidefade">
voltar a primeira</a>
</div>
<div data-role="footer">...</div>
</div>
<div data-role="page" id="pg2popup">
<div data-role="header"><h1>Dialog</h1></div>
<div role="main" class="ui-content">
           <h1>Popup interno</h1>
           <a href="#inicial" data-transition="slidefade">Fechar</a>
</div>
<div data-role="footer"><hr/></div>
</div>



Note que temos várias “pages” dentro de “index.html”. Ao clicarmos em um link com href iniciando por “#”, o jQuery Mobile vai procurar a “page” dentro do mesmo arquivo.


Note que temos uma “page” que funciona como um “popup” (data-rel="dialog").


Experimente com os links.



O que temos dentro de uma “page”?

Temos 3 elementos:
  • Um cabeçalho: div com “data-role='header'”;
  • Um miolo de conteúdo: div com role="main" e class="ui-content";
  • Um rodapé: div com “data-role='footer'”;



Widgets

Atenção: Os widgets nem sempre funcionam da mesma forma no navegador do desktop e no do Emulador! Teste sempre no Emulador do Android, no Simulador do iOS e nos dispositivos móveis!


O jQuery Mobile inclui uma série de plugins que melhoram os elementos normais do HTML 5. Eles tentam dar uma “cara de mobile” para uma aplicação Web, imitando os Widgets presentes nas plataformas nativas (Android, iOS etc).


Para usar um Widget, geralmente basta acrescentar um atributo “data-role”. Para ver um exemplo, substitua os arquivos “index.html” e “pagina2.html”, pelos que estão dentro da sessão 3, na pasta “widgets”:



Temos alguns Widgets sendo utilizados nos dois arquivos. Vamos começar pela página “index.html”:

<ul data-role="listview">
<li><a href="pagina2.html" data-transition="flip">
Página 2 - outro arquivo</a></li>
<li><a href="#pg2segunda" data-transition="pop">
segunda página interna</a></li>
<li><a href="#pg2popup" data-transition="pop" data-rel="dialog">
Popup interno</a></li>
</ul>

Ao acrescentarmos o atributo ' data-role=“listview” ', transformamos a renderização do tag <ul> em um Widget. E, como ele tem links internos, serão exibidos ícones com setas no canto direito, para indicar ao usuário que aquela opção é clicável e representa um link.



No arquivo “pagina2.html”, temos um botão com o texto “Voltar index.html”. Note como ele foi renderizado: um retângulo, quase da largura da tela, com cantos arredondados. Isso é o Widget “button”. O jQuery Mobile transforma automaticamente os tags “input”, com type “button”, “submit” e “reset” em widgets do tipo button, sem necessidade de colocar o atribito “data-role='button'”.

Porém, é possível alterar os tags <a> e <button> em widgets do tipo button, mudando sua classe. Por exemplo, dentro do arquivo “index.html”, se invocarmos o link para a “segunda página interna”, eis o que aparecerá:


Note que o link foi substituído por um Widget button. Para isso, eu só tive que acrescentar a classe “ui-btn” no tag <a>:

<div data-role="page" id="pg2segunda">
<div data-role="header">...Header pg segunda interna...</div>
<div role="main" class="ui-content">
          <h1>Segunda página interna</h1>
          <a href="#inicial" data-transition="slidefade" class="ui-btn">
          voltar a primeira</a>
</div>
<div data-role="footer">...Footer pg segunda interna...</div>
</div>

Container colapsível

Um widget muito interessante é o container colapsível, que permite ocultar e mostrar os elementos de acordo com a seleção do usuário. Veja no arquivo “index.html” como modificamos isso:


Eis o código:

<div data-role="collapsible"
   data-iconpos="right"
   data-inset="false"
   data-content-theme="b">
   <h2>Opções</h2>
   <ul  data-role="listview">
    <li><a href="#">Avião</a></li>
    <li><a href="#">Trem</a></li>
    <li><a href="#">Carro</a></li>
   </ul>
</div>

Vamos explicar as mudanças... Para começar, estamos usando o widget “collapsible”, que oculta tudo o que estiver dentro do container (neste caso, uma <div>). Para que possa ser expandido novamente, é necessário que o primeiro elemento do container seja um header (<h1> até <h6>), pois esse será o elemento no qual o usuário terá que clicar para expandir o container.


Note que temos outro item dentro do container colapsível: “Outros”. Ao expandi-lo, você verá que existe um formulário inteiro dentro dele:


Ou seja, qualquer conteúdo que estiver dentro do container será oculto.

Forms

Acrescente o arquivo “jquery.validate.min.js”, que está dentro da sessão 3, pasta “jquery-mobile” à pasta “js” dentro de “assets/www”. Lembre-se de substituir os arquivos “index.html” e “pagina2.html”, pelos que estão dentro da sessão 3, na pasta “widgets”!

Um elemento importante de interação com o usuário são os “forms”. Em aplicações móveis, nós não temos esse conceito. Simplesmente, posicionamos “views” no layout de nossa “activity” e pronto! Em HTML, é melhor utilizar forms para isto.

Dentro do arquivo “index.html” novo, do projeto jq1 (aquele que você substituiu no tópico anterior), tem uma “page” popup. Ela contém uma <div> colapsível, cujo segundo item é um form.



O form é maior que a tela, logo, para ver a parte inferior, você tem que arrastar para cima (clique com o mouse sobre a área livre do popup e arraste para cima).

Temos vários elementos nesse form. Vamos ver como os configuramos.



Caixa de texto

No HTML 5, as caixas de texto podem ter tipos de acordo com seu conteúdo. Para ver todos esses tipos, eu recomendo o tutorial de HTML 5 do W3Schools:

No form, nós criamos duas caixas de texto:

<label for="nome">Nome:</label>
<input type="text" name="nome" id="nome" value="" required
  title="Informe seu nome!" />

<label for="email">Email:</label>
<input type="email" name="email" id="email" value="" required
title="Informe um email válido!" />

Antes de mais nada, estamos utilizando vários recursos do HTML 5. Se você não está familiarizado com esta versão, sugiro consultar o tutorial do W3Schools:



Estamos usando o tag <label> para acrescentar um rótulo para os campos. Ele existe desde o HTML 4.01. Seu atributo “for” tem o “id” do elemento ao qual se refere. Também estamos usando algumas coisinhas para poder validar os campos:
  • required: Indica que o campo é obrigatório e deve ser preenchido pelo usuário;
  • title: Indica a mensagem de erro que deve ser exibida, caso o campo seja rejeitado na validação;
Em um navegador compatível com HTML 5, a validação é feita quando o formulário é submetido. Para que isto aconteça na Webview, temos que carregar um plugin de validação, que pode ser o jquery-validate.js. Por isto, a página “index.html” nova faz referência a esse plugin:


<script src="js/jquery-2.1.1.min.js"></script>
<script src="js/jquery.mobile-1.4.4.min.js"></script>
<script src="js/jquery.validate.min.js"></script>

A novidade aqui é o “type=email”, que no HTML 5 significa validar o conteúdo de um campo de acordo com as regras de um e-mail.

O atributo “title” indica a mensagem de erro que deve ser mostrada, caso o campo não passe na validação.


Validação

É possível validar os campos do form sem escrever código Javascript para isto. Com alguns atributos HTML 5 e um plugin de validação, podemos validar quase tudo, de forma simples e prática.

Para ativar a validação do form, temos que executar um script logo após o formulário ter sido carregado:

...
<input type="submit" value="Enviar" />
</form>
<script>
$("#meuform").validate();
</script>

Select

Uma <select> é uma lista de escolha, cujo padrão é permitir escolher apenas um elemento. Ela é implementada desta forma:

<label for="cidade" class="select">Cidade:</label>
<select name="cidade" id="cidade" required
title="Selecione uma cidade!">
<option value="" selected>[selecione]</option>
   <option value="rio" >Rio de Janeiro</option>
   <option value="sp">São Paulo</option>
   <option value="bh">Belo Horizonte</option>
</select>

Fizemos um pequeno “truque” para forçar o usuário a selecionar uma cidade. Poderíamos ter simplesmente escolhido uma das cidades e colocado o atributo “selected” dentro do seu tag <option>, mas isto poderia gerar erros, pois o usuário não seria obrigado a escolher qual a cidade desejada. Então, criamos uma opção com valor vazio (value=“”) e, se ele não escolher uma cidade, a validação vai pegar (temos o atributo “required” no <select>).


Checkbox


Nem dá para reconhecer como uma Checkbox, não? Mas é. Você pode selecionar os dois turnos (que ficarão com fundo azul). Isto é devido à formatação do Widget Checkbox, do jQuery Mobile. Eis o HTML:

<fieldset data-role="controlgroup"
data-type="horizontal">
   <legend>Turnos:</legend>
   <input type="checkbox" name="turnos" id="turno-manha" checked>
   <label for="turno-manha">Manhã</label>
   <input type="checkbox" name="turnos" id="turno-tarde" >
   <label for="turno-tarde">Tarde</label>
</fieldset>

A opção ' data-type=”horizontal” ' faz com que as várias Checkboxes apareçam em uma mesma linha na tela. Se mudarmos para “vertical”, será uma coluna, com os rótulos à esquerda.

Pergunta: E se eu quiser forçar o usuário a selecionar pelo menos uma caixa? Neste caso, vamos ter que criar uma regra de validação:

 </form>
 <script>
  $("#meuform").validate(
  {
         rules: {
             'turnos': {
                 required: true,
             }
         }
         messages: {
             'turnos': {
                 required: "Selecione pelo menos 1 turno"
             }
         }
     }
  );
 </script>

Criamos uma regra para validar o vetor “turnos”. O usuário terá que selecionar pelo menos 1 turno.

Radio

Criamos um “radio group”, com botões que se excluem mutuamente:

Eis o código HTML:

<fieldset data-role="controlgroup">
    <legend>Escolaridade</legend>
    <input type="radio" name="escolaridade" id="escolaridade1" value="em" >
    <label for="escolaridade1">Ensino médio</label>
    <input type="radio" name="escolaridade" id="escolaridade2" value="sp">
    <label for="escolaridade2">Superior completo</label>
    <input type="radio" name="escolaridade" id="escolaridade3" value="m">
    <label for="escolaridade3">Mestrado</label>
    <input type="radio" name="escolaridade" id="escolaridade4" value="d">
    <label for="escolaridade4">Doutorado</label>
</fieldset>

Como não informamos o atributo ' data-type ', o comportamento default é mostrar como vertical, em uma coluna.

Para validar elementos repetitivos, como Checkboxes e radio buttons, geralmente usamos regras de validação, criadas e passadas para o validador dentro do script:

</form>
<script>
$("#meuform").validate(
{
       rules: {
           'turnos': {
               required: true
           },
           escolaridade: {
            required: true
           }
       },
       messages: {
           'turnos': {
               required: "Selecione pelo menos 1 turno"
           },
           escolaridade: {
            required: "Selecione uma escolaridade"
           }
       }
   }
);

Criamos duas regras de validação para os campos “turnos” e “escolaridade”, obrigando que o usuário selecione pelo menos uma opção de cada um deles.



Slider

Slider é uma novidade do HTML 5 e permite selecionar um valor deslocando um botão.


Eis o HTML:

<label for="slider">Renda</label>
<input type="range" name="renda" id="renda" value="1000" min="1000" max="20000">


Como pegar os campos de um Form

Se você estiver submetendo o formulário para fora da aplicação, por exemplo, para um servidor externo, então é tudo resolvido por HTTP Request.

Porém, se está usando o form como uma “activity”, é possível interceptar o evento “submit” e pegar os dados que o usuário digitou. Para isso, usamos o jQuery:

$( "#meuform" ).submit(function( event ) {
 if($(“#meuform”).valid()) {
  var campos = "Nome: "
  + $("#nome").val()
  + "\r\nEmail: "
  + $("#email").val()
  + "\r\nCidade: "
  + $("#cidade").val()
  + "\r\nTurnos: ";
  $('input[name="turnos"]:checked').each(function() {
 campos += " " + $(this).val();
  });
  campos += "\r\nEscolaridade: ";
  campos += $('input[name="escolaridade"]').val();
  campos += "\r\nRenda: ";
  campos += $("#renda").val();
  alert(campos);
        event.preventDefault();
  $("#meuform").get(0).reset();
  $.mobile.navigate( "#inicial" );

 }

});
</script>

Estamos “pendurando” um “callback” no evento “submit” do form. Quando o usuário clicar no botão “Enviar” (input type=”submit”), este “callback” será invocado. Note que estamos verificando se o form é válido! Os campos Simples, como caixas de texto, select e slider, podemos pegar apenas usando o seletor de “id” do jQuery, e o método “val()”:

$("#nome").val()

Os campos de seleção múltipla, podemos pegar através do atributo “name”, de seus tags:

$('input[name="escolaridade"]').val();

No caso dos “turnos”, precisamos pegar apenas o valor dos turnos que foram efetivamente selecionados. Então, usamos o atributo “checked” no seletor do jQuery:

$('input[name="turnos"]:checked').each(function() {
campos += " " + $(this).val();
});

Neste caso, estamos pegando um vetor com todos os Checkboxes (com name=”turnos”) que estão selecionados. O método “each()” permite passar um “callback” que será invocado para cada elemento. 

Finalmente, precisamos resetar o form, limpando os campos e navegar para uma outra página:


event.preventDefault();
$("#meuform").get(0).reset();
$.mobile.navigate( "#inicial" );

O primeiro comando previne o comportamento normal do submit. O segundo, invoca o reset do form, e. o terceiro, faz o jQuery Mobile navegar para a “page” com id “inicial”.




Exercício

Vamos pegar o exercício da aula anterior e modificá-lo para usar o jQuery Mobile. Haverá duas páginas, ambas internas no arquivo “index.html”. Uma será a principal, a outra será um popup com formulário para inclusão de novo registro:



Ela conterá um botão para adicionar novos registros e apresentará uma <ul> listview, com os registros que já estão no banco de dados.

Ao clicar no botão “adicionar”, deverá navegar para a página popup, que mostrará o form de inclusão de registros. Os campos deverão ser:

  • “id” : numérico e obrigatório, com mensagem de validação;
  • “data” : texto e obrigatório, com mensagem de validação;

Dicas

1) Mova os scripts de validação do form e do evento submit, para o arquivo “index.js”, deixando a página html mais limpa.

2) Tente usar um widget listview (<ul>) como o local onde serão acrescentados os registros pré-existentes:

var registro = "";
for (var i=0; i<len; i++) {
registro += "<li>"
+ results.rows.item(i).id
+ " "
+ results.rows.item(i).data
+ "</li>";

}
$("#saida").html(registro);
$("#saida").listview("refresh");

Não entre em pânico! Procure no zip desta aula, dentro da Pasta do Curso. A resposta já está lá...