Tag: html

  • API do Whatsapp: Como quebrar linhas e passar parâmetros em links

    API do Whatsapp: Como quebrar linhas e passar parâmetros em links

    Quase todo site, sistema, ou app, hoje, precisa ter um botão para compartilhamento via Whatsapp. Para facilitar todo esse processo, o Whatsapp disponibiliza uma API própria para facilitar o processo para desenvolvedores.

    Porém, para o trabalho mais básico, um simples link de interação com um início de conversa, a documentação não é muito clara quanto a caracteres especiais e links.

    Criando um link para iniciar a conversa

    Para você criar um link para iniciar uma conversa, basta usar a URL https://api.whatsapp.com/send e passar os parâmetros específicos.

    Na prática, você poderia passar algo como:

    <a href="https://api.whatsapp.com/send?phone=55XX9XXXXXXXX;text=TextoParaEnviar">Enviar Mensagem</a>

    Importante: O telefone deve conter DDI e DDD e ser apenas número. Por exemplo, você vai mandar uma mensagem para alguém de Recife/PE – Brasil, o telefone precisa ser: 5581numero_do_celular.

    Até aí é bem simples. O problema está quando você quer enviar um parâmetro em um link, ou quando você quer enviar caracteres especiais como quebra de texto. O que vai acontecer é que a API do Whatsapp simplesmente irá remover qualquer tipo de código que ele considerar inseguro, através de um processo chamado de Sanitização.

    O Whatsapp, entretanto, utiliza-se de uma versão própria de parâmetros simples para tratar richtexts. Ou seja, simplesmente basta você converter os valores que você quer para URL Encode. Isso fará com que o texto seja entregue da forma como você planejou e você possa aplicar caracteres especiais e formatação.

    Exemplo prático

    Digamos que você queira enviar a seguinte mensagem:

    A pia pinga, o pinto pia…
    Quanto mais a pia pinga, mais o pinto pia.

    Acesse https://ratimbum.com/?token=333 para saber mais.

    Perceba que o texto possui quebra de texto e seu código será algo como:

    A pia pinga, o pinto pia...\n
    Quanto mais a pia pinga, mais o pinto pia.\n\n
    Acesse https://ratimbum.com/?token=333 para saber mais.

    Sim, você deverá usar \n para quebrar o texto. Porém, a sanitização vai retirar o \n e o ?, o que vai criar um resultado não esperado.

    Para resolver o problema, basta você converter o texto para URL Encode. No javascript, por exemplo, ficará assim:

    var texto = "A pia pinga, o pinto pia...\n
    Quanto mais a pia pinga, mais o pinto pia.\n\n
    Acesse https://ratimbum.com/?token=333 para saber mais.";
    texto = window.encodeURIComponent(texto);
    
    /*
    O resultado será algo como:
    A%20pia%20pinga%2C%20o%20pinto%20pia...%0A%0AQuanto%20mais%20a%20pia%20pinga%2C%20mais%20o%20pinto%20pia.%0A%0AAcesse%20https%3A%2F%2Fratimbum.com%2F%3Ftoken%3D333%20para%20saber%20mais.
    */

    Dessa forma, você pode optar por colocar esse código dinamicamente em um <a> ou, o que recomendo, enviar através da função window.open, do Javascript.

    function enviarMensagem(){
    	var celular = "55DDDNUMERODOCELULAR";
      
      var texto = "Texto que eu vou enviar \n com quebras de \n texto.";
      texto = window.encodeURIComponent(texto);
      
      window.open("https://api.whatsapp.com/send?phone=" + celular + "&amp;text=" + texto, "_blank");
    	//Obs.. use "_system", no lugar de blank, caso você esteja usando Phonegap / Cordova / Ionic ou qualquer um baseado em webview;
    }

    Obs: Você não precisa converter para \n se for pegar o texto a partir de um <textarea>, como mostrado no exemplo mais abaixo.

    É importante salientar que se você estiver testando no Whatsapp web, a quebra de linha não é mostrada na pre-visualização, mas será mostrada no momento do envio real da imagem.

    Corrigindo possível erro ERR_BLOCKED_BY_RESPONSE

    As vezes, pode ocorrer o erro ERR_BLOCKED_BY_RESPONSE, em alguns navegadores ou condições específicas (principalmente no Firefox). Isso ocorreu devido a algumas atualizações não bem descritas da API do Whatsapp.

    Para corrigir esse problema, faremos uma pequena atualização no código acima, o qual será necessário verificar se é mobile ou não, e passar o prefixo web no lugar de api. Acredito que também exista uma correlação quanto a navegadores desatualizados, que estejam causando o problema.

    function enviarMensagem(){
    	var celular = "55DDDNUMERODOCELULAR";
      
      var texto = "Texto que eu vou enviar \n com quebras de \n texto.";
      texto = window.encodeURIComponent(texto);
    
      let urlApi = "https://web.whatsapp.com/send";
      if(mobileCheck()){
      	urlApi = "https://api.whatsapp.com/send";
      }
      
      window.open(urlApi + "?phone=" + celular + "&text=" + texto, "_blank");
    	//Obs.. use "_system", no lugar de blank, caso você esteja usando Phonegap / Cordova / Ionic ou qualquer um baseado em webview;
    }
    
    
    function mobileCheck(){
      let check = false;
      (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
      return check;
    }

    Ocorre também que o código questão modifica um comportamento. Ele não mais alertará o usuário de que uma mensagem será enviada. Ao contrário disso, ele já abre direto no Whatsapp web.

    Outras opções

    Se o intuito, porém, for utilizar também outras propriedades do richtext, como negrito e itálico, você deverá simplesmente passar _ (itálico), * (negrito), ~ (riscado) ou “` (tamanho fixo). Exemplo:

    var mensagemParaWpp = mensagem.replace(/<b>/g, "*").replace(/<\/b>/g,"*"); 
    alert(s);

    Código de Exemplo

  • Como adicionar e remover dinamicamente campos HTML em um form? (Javascript puro)

    Como adicionar e remover dinamicamente campos HTML em um form? (Javascript puro)

    O Javascript nos permite criar conteúdos dinâmicos e podemos usar isso para adicionar remover e adicionar elementos de acordo com as opções do usuário. Dados como informações sobre dependentes, links de mídias sociais, e-mails adicionais, etc. são curtos e não fazem sentido criarmos um formulário separado apenas para estes. Por isso, é interessante incluirmos a opção de adicionar diretamente esses campos em um subformulário dinâmico.

    Obs. Se quiser ir direto para o código final, procure o link do JsFiddle no final do post.

    HTML

    Para iniciarmos, basta criar um container no HTML onde você quer que os elementos sejam exibidos, além do botão simples de adição e um botão de captura de dados, para que, desta forma, possamos enviar o JSON resultante para o back-end.

    A organização dos containers é sempre muito importante quando trabalhamos com Javascript e nos dedicamos ao uso correto da web semântica.

    <div class="dependentes">
      <button id="btnAdicionarDependentes">
      📝Adicionar Dependentes
      </button>
      <div class="container" id="dependentesContainer">
      </div>
      <button class="green" id="btnCapturarDados">
      ✅ Capturar Dados
      </button>
    </div>
    <pre id="containerDados">  
    </pre>

    Javascript

    Como de praxe, usaremos o Javascript puro para realizar essa tarefa, dessa forma você poderá usar em qualquer lugar o que aprender aqui.

    Para poder capturar e devolver os dados, usaremos um objeto JSON, dessa forma fica fácil remontar, tanto no back-end, quando no front-end, os dados nas formatações e/ou elementos que precisamos.

    Vamos criar, como exemplo, o JSON abaixo, e vamos declará-lo em uma variável global chamada dependentes, seguindo a ideia de um cadastro de lista de dependentes, então temos:

    var dependentes = [{
      identificador: 13,
      nome: "Joana da Silva",
      idade: 12,
    }];

    Agora vamos nos focar nas funções. A primeira coisa que vamos fazer é criar um método que pegue os dados do JSON e o converta para elementos HTML renderizados na tela. Dessa forma, usaremos um laço para ler o JSON e aplicamos seus dados em uma template string e o adicionamos no container específico:

    function carregarDependentes() {
      let dependentes_container = document.querySelector("#dependentesContainer");
      dependentes_container.innerHTML = "";
      dependentes.forEach((el) =&gt; {
        let identificador = el.identificador;
        let nome = el.nome;
        let idade = el.idade;
        let dependente_container = `<div class="dependente" data-id="${identificador}">
        								<input class="nome" placeholder="Digite o nome" type="text" value="${nome}">
                                        <input class="idade" placeholder="Digite a idade" type="number" value="${idade}">
                                        <div class="action">
                                            <a href="#" class="salvar">salvar 💾</a>
                                            <a href="#" class="remover">❌</a>
    									</div>
                                    </div>`;
        dependentes_container.innerHTML += dependente_container;
      });
    }

    Agora vem o segredo que facilita o processo e o deixa mais organizado. Ao invés de remover e adicionar os elementos na tela, iremos nos focar em remover e adicionar do JSON e, em seguida, regenerar os elementos a partir desse objeto. Ficando, assim, com um código mais limpo. Outro motivo pelo qual usamos a regeneração dos elementos é para evitar criarmos IDs únicos temporários. Ao regenerar, podemos usar os índices do próprio array como identificador.

    Para adicionar um novo item, basta incluirmos um dado vazio no JSON, porém, seguindo nosso modelo, e mandamos gerar novamente os elementos:

    function adicionarDependentes() {
      dependentes.push({ identificador: "", nome: "", idade: "" });
      carregarDependentes();
    }

    Para remover, similar a criação de um novo, vamos usar um laço, porém para adicionar o evento aos botões de excluir. Usaremos então o método splice para remover o array. Depois, obviamente, vamos regenerar os elementos a partir da função carregarDependentes():

    function removerDependentes() {
      document
        .querySelectorAll("#dependentesContainer .remover")
        .forEach((el, i) =&gt; {
          el.addEventListener("click", () =&gt; {
            dependentes.splice(i, 1); // O splice vai remover um item do array no JSON
            carregarDependentes(); // E chamamos o método para regenerar os elementos
          });
        });
    }

    Uma vez que adicionamos uma nova linha em branco precisamos salvar seu preenchimento e aí iremos usar a mesma lógica de remoção, mas usaremos o splice para substituir e não para remover um dado do JSON. Porém, adicionaremos uma pequena validação para evitar entrar dados incompletos:

    unction salvarDependentes() {
      document
        .querySelectorAll("#dependentesContainer .salvar")
        .forEach((el, i) =&gt; {
          el.addEventListener("click", () =&gt; {
            let identificador = el.parentElement.parentElement.getAttribute(
              "data-id"
            );
            let nome = el.parentElement.parentElement.querySelector(".nome").value;
            let idade = el.parentElement.parentElement.querySelector(".idade")
              .value;
    
            if (!nome.length || !idade.length) { // Verifica se nome e idade foram preenchidos
              alert("Nome e idade precisam ser preenchidos para salvar.");
              return false;
            }
            dependentes.splice(i, 1, {
              identificador: identificador,
              nome: nome,
              idade: idade,
            }); // Substitui o dado no JSON
            carregarDependentes(); //  E chamamos o método para regenerar os elementos
          });
        });
    }

    Um outro método que precisamos adicionar é uma forma de bloquear para que um usuário consiga clicar em outros elementos ao redor, sem antes finalizar a edição do item. Para isso, iremos fazer um laço que adiciona uma classe de CSS, que vamos chamar de disabled, em todos os elementos, exceto o que está sendo editado. Essa classe possui um point-events: 0 e um opacity: 0.5. Para demonstrar que está desativado, você pode ainda adicionar outros efeitos, como filtros de baixo contraste ou escala de cinza.

    function travarOutros(element) {
      if (element == false) { // Passar false como parâmetro para que todos os elementos fiquem habilitados novamente, ao invés de apenas o elemento que queremos liberar
        document
          .querySelectorAll(".dependentes button, .dependentes .container &gt; div")
          .forEach((el) =&gt; {
            el.classList.remove("disabled");
          });
        document.querySelector("#containerDados").innerHTML = "";
        return false;
      }
      document
        .querySelectorAll(".dependentes button, .dependentes .container &gt; div")
        .forEach((el) =&gt; {
          if (el != element) {  // Verifica se o elemento no laço é o que está sendo editado
            el.classList.add("disabled");
          }
        });
    }

    Agora, antes de continuarmos, vamos revisitar as funções acima para chamar, quando necessário, uma função dentro da outra (leia os comentários no código para entender), ficando assim:

    function carregarDependentes() {
      let dependentes_container = document.querySelector("#dependentesContainer");
      dependentes_container.innerHTML = "";
      dependentes.forEach((el) =&gt; {
        let identificador = el.identificador;
        let nome = el.nome;
        let idade = el.idade;
        let dependente_container = `<div class="dependente" data-id="${identificador}">
        															<input class="nome" placeholder="Digite o nome" type="text" value="${nome}">
                                      <input class="idade" placeholder="Digite a idade" type="number" value="${idade}">
                                      <div class="action">
                                      	<a href="#" class="salvar">salvar 💾</a>
                                        <a href="#" class="remover">❌</a>
    																	</div>
    															  </div>`;
        dependentes_container.innerHTML += dependente_container;
      });
      salvarDependentes(); // Adicionamos o método aqui para que o laço seja aplicado nos novos items adicionados
      removerDependentes(); // Adicionamos o método aqui para que o laço seja aplicado nos novos items adicionados
      travarOutros(false); // Adicionamos para destravar tudo
    }
    
    function removerDependentes() {
      document
        .querySelectorAll("#dependentesContainer .remover")
        .forEach((el, i) =&gt; {
          el.addEventListener("click", () =&gt; {
            dependentes.splice(i, 1);
            carregarDependentes(); // Regenerar os elementos HTML após remover do JSON
          });
        });
    }
    
    function adicionarDependentes() {
      dependentes.push({ identificador: "", nome: "", idade: "" });
      carregarDependentes(); // Regenerar os elementos HTML após remover do JSON
      travarOutros(
        document.querySelector("#dependentesContainer &gt; div:last-child")
      ); // Desabilitar todos os outros elementos, exceto o que acabou de ser adicionado
    }
    
    function salvarDependentes() {
      document
        .querySelectorAll("#dependentesContainer .salvar")
        .forEach((el, i) =&gt; {
          el.addEventListener("click", () =&gt; {
            let identificador = el.parentElement.parentElement.getAttribute(
              "data-id"
            );
            let nome = el.parentElement.parentElement.querySelector(".nome").value;
            let idade = el.parentElement.parentElement.querySelector(".idade")
              .value;
    
            if (!nome.length || !idade.length) {
              alert("Nome e idade precisam ser preenchidos para salvar.");
              return false;
            }
            dependentes.splice(i, 1, {
              identificador: identificador,
              nome: nome,
              idade: idade,
            });
            carregarDependentes(); // Regenerar os elementos HTML após remover do JSON
            travarOutros(false); // Liberar todos os elementos novamente
          });
        });
    }
    
    function travarOutros(element) {
      if (element == false) {
        document
          .querySelectorAll(".dependentes button, .dependentes .container &gt; div")
          .forEach((el) =&gt; {
            el.classList.remove("disabled");
          });
        document.querySelector("#containerDados").innerHTML = "";
        return false;
      }
      document
        .querySelectorAll(".dependentes button, .dependentes .container &gt; div")
        .forEach((el) =&gt; {
          if (el != element) {
            el.classList.add("disabled");
          }
        });
    }

    Agora, tudo o que precisamos fazer é incluir os comandos de inicialização, onde aplicamos o método de adição ao evento de clique do botão e carregamos os dados iniciais do JSON:

    //init
    document.querySelector("#btnAdicionarDependentes").addEventListener("click", adicionarDependentes);
    carregarDependentes();

    Por fim, vamos criar uma função para o botão de capturar dados apenas para extrair e mostrar os dados em JSON. Você pode, eventualmente, usar esses dados e enviar via POST, por AJAX ou via campo oculto, e pegar no back-end para guardar ou processar os dados (como com um json_decoder, do PHP):

    //capturarDados
    document.querySelector("#btnCapturarDados").addEventListener("click", ()=&gt;{
    	document.querySelector("#containerDados").innerHTML = JSON.stringify(dependentes, undefined, 4);
    });

    CSS

    O único CSS que precisaremos usar é para aplicar a classe disabled. Se por acaso você está usando algum framework CSS, recomendo que você use o relativo a esta classe deste. Consulte a documentação, onde geralmente está relacionado aos helpers:

    .disabled{
      pointer-events: none;
      opacity: .5;
    }

    Se você quer usar o CSS mais elaborado que usei aqui, veja abaixo o link do JsFiddle.

    Finalizando

    Criar formulários dinâmicos auxilia a usabilidade à medida que permite que o usuário adicione dados de forma mais rápida e com respostas visuais imediatas. O uso aqui do Javascript puro visa a facilidade para que você possa implementar em quaisquer projetos, incluindo os com Typescript. Trabalhe um pouco no código para adequá-lo à sua necessidade. E, como sempre, você poderá puxar o código e testar direto do JsFiddle.

    Aproveite e entre para nosso grupo de discussão no Telegram.