Tag: front-end

  • 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

  • Sistemas de Design: Padronização de Sistemas

    Sistemas de Design: Padronização de Sistemas

    Design é projeto. E, como todo projeto, ele deve ser documentado, possuindo descrições, informações de uso e instruções em geral. É exatamente isso que são Sistemas de Design.

    Sistemas de Design são documentações, online ou físicas, que descrevem a padronização e uso de elementos de um projeto, com o objetivo de que ele possa ser usado em diferentes aplicações, mantendo uma identidade compartilhada. Essa identidade pode ser referenciada a um projeto, linha, segmento ou empresa. São usados elementos como formas, grades, tipografia, assets, iconografia, recomendações de uso, dentre outros. Algo muito semelhante ao que o design gráfico já está acostumado ao criar um Manual de Identidade Visual.

    Um exemplo comum, do dia a dia dos usuários de computador, é o Microsoft Office. Trata-se de um conjunto de aplicativos que, por serem da mesma linha, precisam seguir a mesma identidade. Para que os desenvolvedores possam trabalhar com esse padrão, um sistema de design certamente foi desenvolvido para que a equipe de desenvolvimento possa trabalhar de forma coerente.

    O Sistema de Design garante ao usuário uma fluidez no uso das aplicações e reconhecimento das propriedades do software. Facilitando, dessa forma, não só a identificação, como ajudando o usuário a pressupor determinados recursos ou localizações de ferramentas.

    Experiência Compartilhada

    Grandes empresas de tecnologia desenvolvem seus próprios sistemas de design para que desenvolvedores terceiros possa criar experiências mais coesas e confortáveis para o usuário. Seguir esses sistemas pode ser requerimento ou uma simples recomendação, dependendo da loja e sistema operacional que precisar aprovar.

    Aplicativos, mobile e desktop, são os mais comuns a receberem influencias direta dos sistemas de design de sistemas operacionais que, recomenda-se, devem ser mesclados aos sistemas da própria empresa. Para esses casos temos como exemplo:

    Como dito anteriormente, seguir um sistema de design de sistema operacional é fundamental para manter uma experiência fluida para o usuário. Por isso que, por exemplo, é possível sentir diferenças entre uma mesma aplicação desenvolvida para Windows, Android e dispositivos Apple, porque cada desenvolvedora, apesar de manter sua própria identidade, adapta-se para o ambiente em que o usuário estará.

    Screenshot do WhatsApp vs iOS
    O Whatsapp adapta quase que completamente sua UI de acordo com o sistema de design de cada Sistema Operacional
    Captura de tela do Visual Studio no Windows 10 x MacOSX
    Visual Studio no Windows 10 vs Visual Studio no MacOSX

    Não necessariamente o designer ou desenvolvedor são obrigados a seguir um determinado sistema de design de terceiros. Às vezes, a melhor opção é justamente criar o seu próprio sistema ou criar uma barreira para ter uma identidade única para sua aplicação.

    Algumas empresas, como a Google, estimulam para que os designers utilizem seus sistemas também em sites ou aplicativos web. Para isso, dão descrições específicas de usabilidade e ferramentas que possam agilizar o desenvolvimento dessas aplicações.

    Frameworks e Bibliotecas

    Para facilitar o desenvolvimento, muitas empresas oferecem frameworks e bibliotecas, tanto web quanto para desktop e mobile, afim de estimular o desenvolvedor a seguir aquele determinado padrão.

    É interessante para as grandes desenvolvedoras que outras empresas sigam seus padrões de design, pois, dessa forma, ditam tendências e criam assimilações dos usuários com suas próprias plataformas, principalmente quando essas outras empresas utilizam ferramentas e APIs das grandes.

    A exemplo de frameworks e bibliotecas temos:

    Porém, nem sempre essas opções suprem a demanda, pois não são compatíveis com outras bibliotecas e tecnologias. Por isso, muitos grupos de desenvolvedores trabalham para criar frameworks e bibliotecas abertas que seguem os padrões estipulados pelos sistemas de design. Como por exemplo:

    Utilizar Sistemas de Design, além de facilitar o processo de desenvolvimento, ajuda a manter o usuário mais confortável no ambiente em que ele está acostumado. Criar ou adaptar o seu próprio sistema também ajuda a criar uma identidade única para seu conjunto de aplicativos e serviços.

    Que outros sistemas de design e frameworks vocês podem recomendar? Deixem nos comentários.

  • Como centralizar verticalmente elementos HTML com CSS

    Como centralizar verticalmente elementos HTML com CSS

    Uma das coisas mais comuns, mas ao mesmo tempo mais chatas de se fazer no CSS é alinhar verticalmente elementos em tela. Existem várias formas de fazer isso. Neste post, vou elencar as minhas formas favoritas de fazer isso.

    Alinhamento com Flexbox

    Flex é uma propriedade incrível do CSS que permite organizar elementos. Se outrora tínhamos que fazer inúmeras gambiarras com float, o flex nos permite controlar o comportamentos dos filhos de um container. Para centralizar verticalmente um objeto, podemos usar a direção de coluna, em seu container pai.

    Alinhamento com position

    Com um pouco de matemática, conseguimos fazer um alinhamento vertical com o position absolute. Porém, é importante lembrar que o objeto será flutuante e que seu pai, necessariamente, precisa ser um position relative. O problema de usar esse tipo de alinhamento é que o conteúdo da posição precisa ser fixo. Felizmente, hoje, conseguimos fazer cálculos com variáveis de CSS, o que facilita a forma de implementarmos essa técnica. A vantagem dessa técnica é poder usar, justamente, em ambientes flutuantes que se sobreponham. Porém, caso você não precise que ele se alinhe ao pai, mas à viewport, você pode usar também o position como fixed.

    Alinhamento com Grid

    Outra forma moderna de alinhar verticalmente é através do uso de grids. A vantagem de usar grids é que o tamanho do conteúdo do elemento alinhado corresponderá ao tamanho da grid que se deseja utilizar. Ou seja, é adaptável de acordo com a viewport, e não referente ao seu conteúdo. Para isso, basta definirmos a quantidade de colunas e linhas que desejamos e estabelecemos onde o objeto alinhado vai iniciar e terminar.

    Exemplo de uso (Modal Alinhado ao Centro)

    Para mostrar como pode ser usado os alinhamentos, que tal criarmos um simples modal alinhado ao centro da tela?

    Um modal é composto por uma cortina que reveste o conteúdo original, seguido de um painel com alguma informação dentro. É convenção de que os modals carreguem essas informações no centro da tela, afim de que a informação fique direcionada e encapsulada, levando, assim, o usuário à uma atenção maior àquela informação. Ou seja, isolar e destacar. O exemplo abaixo foi feito usando a primeira estratégia de alinhamento vertical aqui apresentada, pois, desta forma, o tamanho do modal que vai ser a referência para a centralização. Alguns efeitos foram adicionados para ilustrar melhor.

  • 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.