Przejdź do głównej treści
Grafika przedstawia ukryty obrazek

Jak zrealizować dwukierunkowe wiązanie danych MVVM w Vanilla JS?

Ilustracja tematu Jak zrealizowa dwukierunkowe wizanie danych MVVM w Vanilla JS

Wzorzec projektowy MVVM (Model-View-ViewModel) umożliwia rozdzielenie logiki aplikacji od jej warstwy prezentacji. W kontekście aplikacji webowych pomaga zarządzać interakcjami użytkownika i aktualizacjami danych w sposób czytelny oraz utrzymywany. W odróżnieniu od popularnych frameworków takich jak Vue.js czy Angular, możemy wdrożyć ten wzorzec również za pomocą czystego JavaScriptu (Vanilla JS).

Główne elementy MVVM

  1. Model — Reprezentuje dane oraz logikę biznesową aplikacji.
  2. View — Odpowiada za interfejs użytkownika (HTML).
  3. ViewModel — Pośrednik między Modelem a Widokiem, zarządzający synchronizacją danych i zdarzeniami.

Poniżej przedstawię krok po kroku implementację tego wzorca w Vanilla JS.

1. Definicja modelu

Model będzie prostym obiektem przechowującym dane aplikacji. Dodamy do niego funkcjonalność subskrypcji i powiadamiania obserwatorów o zmianach danych:

class Model {
  constructor(initialData = {}) {
    this.data = initialData;
    this.listeners = [];
  }

  subscribe(listener) {
    this.listeners.push(listener);
  }

  notify() {
    this.listeners.forEach(listener => listener(this.data));
  }

  set(property, value) {
    if (this.data[property] !== value) {
      this.data[property] = value;
      this.notify();
    }
  }

  get(property) {
    return this.data[property];
  }
}

2. Tworzenie ViewModelu

ViewModel zarządza komunikacją między Modelem a Widokiem. Będzie on subskrybował zmiany Modelu i aktualizował Widok.

class ViewModel {
  constructor(model) {
    this.model = model;
    this.bindings = {};
  }

  bind(property, element, eventType = null) {
    // Aktualizacja View na zmiany Modelu
    element.value = this.model.get(property) || "";
    element.textContent = this.model.get(property) || "";

    this.model.subscribe(data => {
      if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
        element.value = data[property];
      } else {
        element.textContent = data[property];
      }
    });

    // Aktualizacja Modelu na zmiany View (dwukierunkowe wiązanie)
    if (eventType) {
      element.addEventListener(eventType, e => {
        this.model.set(property, e.target.value);
      });
    }
  }
}

3. Implementacja Widoku

HTML może zawierać elementy, które będziemy wiązać z Modelem:

<div>
  <label for="name">Name:</label>
  <input type="text" id="name">
</div>
<p>Name: <span id="displayName"></span></p>

4. Integracja komponentów

// Inicjalizacja Modelu
const model = new Model({ name: 'John Doe' });

// Inicjalizacja ViewModelu
const viewModel = new ViewModel(model);

// Powiązanie danych
const nameInput = document.getElementById('name');
const displayName = document.getElementById('displayName');

viewModel.bind('name', nameInput, 'input');
viewModel.bind('name', displayName);

5. Gotowy HTML z JavaScript

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vanilla MVVM</title>
  </head>
  <body>
    <div>
      <label for="name">Name:</label>
      <input type="text" id="name">
    </div>
    <p>Name: <span id="displayName"></span>
    </p>
    <script>
      class Model {
        constructor(initialData = {}) {
          this.data = initialData;
          this.listeners = [];
        }
        subscribe(listener) {
          this.listeners.push(listener);
        }
        notify() {
          this.listeners.forEach(listener => listener(this.data));
        }
        set(property, value) {
          if (this.data[property] !== value) {
            this.data[property] = value;
            this.notify();
          }
        }
        get(property) {
          return this.data[property];
        }
      }
      class ViewModel {
        constructor(model) {
          this.model = model;
          this.bindings = {};
        }
        bind(property, element, eventType = null) {
          // Aktualizacja View na zmiany Modelu
          element.value = this.model.get(property) || "";
          element.textContent = this.model.get(property) || "";
          this.model.subscribe(data => {
            if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
              element.value = data[property];
            } else {
              element.textContent = data[property];
            }
          });
          // Aktualizacja Modelu na zmiany View (dwukierunkowe wiązanie)
          if (eventType) {
            element.addEventListener(eventType, e => {
              this.model.set(property, e.target.value);
            });
          }
        }
      }
      // Inicjalizacja Modelu
      const model = new Model({
        name: 'John Doe'
      });
      // Inicjalizacja ViewModelu
      const viewModel = new ViewModel(model);
      // Powiązanie danych
      const nameInput = document.getElementById('name');
      const displayName = document.getElementById('displayName');
      viewModel.bind('name', nameInput, 'input');
      viewModel.bind('name', displayName);
    </script>
  </body>
</html>

6. Działający przykład

Name:

7. Rozszerzenia

Możesz rozbudować powyższą implementację o:

  • Obsługę walidacji danych w ViewModelu.
  • Większą liczbę dwukierunkowych wiązań.
  • Obsługę dynamicznego dodawania elementów do DOM.
  • Lepsze zarządzanie wydajnością przez debouncing przy wiązaniu zdarzeń.

Podsumowanie

Implementacja wzorca MVVM w Vanilla JS wymaga ręcznego zarządzania subskrypcjami i synchronizacją danych, ale daje pełną kontrolę nad logiką aplikacji. Wdrożenie tego wzorca może być świetnym ćwiczeniem pozwalającym zrozumieć, jak działają popularne frameworki frontendowe.

2 lutego 2025 18

Kategorie

programowanie

Tagi

javascript

Dziękujemy!
()

Powiązane wpisy

Zdjecie zwiazane z Long Polling vs Short Polling Porwnanie
21 stycznia 2025 5 min 26

Long Polling vs. Short Polling: Porównanie

Czytaj więcej
Ilustracja tematu Wyraenia regularne i ich obsuga w PHP oraz JavaScript
1 lutego 2025 5 min 19

Wyrażenia regularne i ich obsługa w PHP oraz JavaScript

Czytaj więcej
Obraz ilustrujacy Biblioteka MathJax  obsuga rwna matematycznych w internecie
2 lutego 2025 2 min 17

Biblioteka MathJax - obsługa równań matematycznych w internecie

Czytaj więcej
Wymiana doświadczeń

Masz podobne doświadczenia?

Chętnie poznam Twoją perspektywę i porozmawiam o tym temacie szerzej.

Napisz do mnie

Każda perspektywa może wnieść coś wartościowego do dyskusji.

Twoja prywatność i pliki cookies

  1. Ta strona internetowa wykorzystuje wyłącznie niezbędne pliki cookies, które są wymagane do jej prawidłowego działania – m.in. do poprawnego wyświetlania treści, zapamiętania podstawowych ustawień przeglądarki oraz zapewnienia stabilności serwisu.
  2. Nie stosuję plików cookies w celach marketingowych, reklamowych ani analitycznych.
  3. Strona ma charakter wyłącznie informacyjny i nie zawiera formularzy kontaktowych, rejestracyjnych ani zakupowych, przez które dane mogłyby być przesyłane na serwer.
  4. Nie zbieram danych osobowych podczas zwykłego korzystania z witryny.
  5. Serwis nie korzysta z certyfikatu SSL, jednak ze względu na informacyjny charakter strony nie jest wymagane przesyłanie poufnych danych. Zalecam jednak, aby nigdy nie wpisywać haseł ani danych osobowych na stronach bez szyfrowanego połączenia.
  6. Korzystając z tej strony, wyrażasz zgodę na używanie wyłącznie niezbędnych plików cookies.

Więcej informacji znajdziesz w mojej polityce prywatności.