Two-way data binding com JavaScript
Tags: javascript
Published: 2021-09-07 15:04:16

Vou mostrar como fazer um **two-way data binding** sem usar nenhum **framework** só com Javascript puro.

**Two-way data binding**, é o termo dado a reatividade dos elementos na sua interface. Ou seja, o que acontece no **DOM (View)**, reflete no **JavaScript(Model)**, e o que acontece no **JavaScript(Model)**, reflete no **DOM(View)**.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b99bgeiolbkssfi10g0l.png)

No exemplo que vou disponibilizar dois botões um para incrementar o valor e outra para decrementar, qualquer interação com os botões temos a atualização do **DOM** que é exibida no **input** e também atualiza o **Model** no **Javascript**.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m9lta21by1ma2lmihovw.gif)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ihtib1bk3y9g8s1201ho.png)

**1 - Criar o projeto**

Crie uma pasta e nela adicione os arquivos **index.js** e **index.html**.

**2 - HTML**

No **HTML** temos o seguinte código abaixo:

```javascript
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <!-- CSS only -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
  <title>Two way data binding</title>
</head>

<body>

  <div class="container py-5">
    <form class="row g-3">
      <div class="col-auto">
        <input min="0" type="number" class="form-control" id="ipt-number" placeholder="Número" disabled>
      </div>
      <div class="col-auto">
        <button type="button" class="btn btn-success mb-3" onclick="increment()">(+)</button>
        <button type="button" class="btn btn-danger mb-3" onclick="decrement()">(-)</button>
      </div>
    </form>
  </div>

  <script src="index.js"></script>
  <!-- JavaScript Bundle with Popper -->
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js"
    integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous">
  </script>
</body>

</html>
```

* Para a estilização optei por usar o [**Bootstrap**](https://getbootstrap.com/docs/5.1/getting-started/introduction/), por isso foi adicionado o **CSS** e **Javascript**.

**CSS**: 

```javascript 
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
```

**Javascript**: 

```javascript 
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js"
    integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous">
```

* O código é bem simples pois temos um **input** **disabled** que será o alvo das alterações do valor no **DOM** e dois botões **(+)** que lança a ação de incrementar e o **(-)** que lança a ação de decrementar.

* As ações são disparadas pelo evento **onclick** de clique em qualquer botão que chama as funções correspondentes;

**3 - Javascript**

A interação com o **DOM** encontra-se no Javascript, e nela que vamos usar a propriedade [**Object.defineProperty**](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) para manipular nosso objeto que será atualizado de acordo com o valor do **input** e por ações podemos atualizar o **DOM**.

O código pode ser visto abaixo: 

```javascript 
const model = {
  value: ""
};

load = () => {
  const number = document.getElementById("ipt-number");
  number.value = "0";

  Object.defineProperty(model, "prop", {
    get: function () {
      console.log("Getter called");
      return this.value;
    },
    set: function (value) {
      console.log("Setter called", value);
      this.value = value;
      printVal();
    }
  });
};

increment = () => {
  let val = parseInt(model.prop || 0) + 1;
  model.prop = val;
};

decrement = () => {
  let val = parseInt(model.prop || 0) - 1;
  if (val < 0) val = 0;
  model.prop = val;
};

printVal = () => {
  const el = document.getElementById("ipt-number");
  el.value = model.prop;
};

load();
```

* **load**: Nessa função temos a inicialização do **input** com o valor zero e a criação do manipulador do nosso objeto através da propriedade **Object.defineProperty**;

* **Object.defineProperty**: Como primeiro parâmetro definimos o objeto que será definida as propriedades, depois a propriedade que será definida ou alterada e por ultimo uma função com os métodos **get** e **set**. Quando queremos atualizar o objeto **model** usamos o **set** e para obter o valor atual usamos o **get**;

* increment: Nessa função é a ação de incrementa o valor do objeto **model**, pegamos o valor atual **model.prop** e depois de executar alguma regra no caso uma soma com 1 atualizamos o nosso objeto **model** **model.prop = val** com o novo valor; 

* decrement: Nessa função é a ação que decrementa o valor do objeto **model**, então pegamos o valor atual do **model**, pegamos o valor atual **model.prop** e depois de executar alguma regra no caso o valor atual menos 1 atualizamos o nosso objeto **model** **model.prop = val** com o novo valor;

* printVal: Nessa função para cada ação de incrementar ou decrementar o método **set** é disparado para atualizar o **DOM**; 

O código pode ser obtido no [**Github**](https://github.com/wandealves/two-way-data-binding-Javascript).