Angular: Komponenty typu Smart i Dumb

Opublikowano: 08.07.2021 - tagi: Angular Komponent Architektura JavaScript

Nieważne jakiego frameworka używasz czy to Angulara, Reacta lub Vue, w każdym z nich istnieje coś takiego jak: komponent.

Komponenty służą nam do budowania naszej aplikacji. Komponent możemy porównać do klocka. Budujemy nasz program z klocków. Im mniejszy jest taki klocek tym łatwiej nim operować. Jeszcze lepiej jeśli dany klocek możemy użyć do budowy innych rzeczy, czyli mówimy, że taki klocek jest reużywalny.

Jak życie pokazuje nasze aplikacje się rozrastają, w szybkim tempie często kosztem jakości kodu.

Architektura komponentu typu Smart i Dumb może pomóc w łatwiejszym utrzymaniu kodu. W tym wpisie przykładowy kod napisany jest w Angularze, ale ten wzorzec może być używany także w innych frameworkach i nie tylko.

Poniżej znajdziesz przykład tego wzorca. Zanim zaczniemy stwórz komponent o nazwie: BookList. W katalogu tego komponentu stwórz plik o nazwie: book.model.ts:

export interface Book {
  id: number;
  title: string;
  author: string;
  isRead: boolean;
}

Komponent typu Dumb

Komponent typu Dumb nazywany też: komponentem prezentacji charakteryzuje się następującymi cechami:

1 Służy tylko do wyświetlania danych.

2 Nie ma logiki biznesowej. Tutaj chodzi oto, że ten komponent nie przetwarza danych. Po prostu pobiera dane i je wyświetla.

3 Nie posiada zależności na przykład z serwisami.

W Angluar dane do tego komponentu będziemy przekazywać za pomocą Input a wysyłać za pomocą Output.

Zawartość pliku: book-list.component.ts:

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { Book } from "./book.model";

@Component({
  selector: 'app-book-list',
  templateUrl: './book-list.component.html',
  styleUrls: ['./book-list.component.scss']
})
export class BookListComponent implements OnInit {

  @Input()
  books: Book[] | undefined;

  @Output()
  bookReadEvent = new EventEmitter<Book>();

  constructor() { }

  ngOnInit(): void {
  }

  bookReadAction(book: Book): void {
    this.bookReadEvent.emit(book);
  }
}

Teraz widok: book-list.component.html:

<div *ngFor="let book of books">
  <strong>{{ book.title }}</strong> - {{ book.author }} <button [hidden]="book.isRead" (click)="bookReadAction(book)">Przeczytane</button>
</div>

Komponent typu Smart

Komponent typu Smart z kolei posłuży nam do ogarnięcia logiki biznesowej i przekazaniu danych do komponentu prezentacji:

1 Obsługuje logikę biznesową.

2 Przekazuje dane do komponentu prezentacji.

3 Może posiadać zależności do serwisów itp.

Stwórz plik o nazwie: book-list.container.component.ts:

import { Component, OnInit } from '@angular/core';
import { Book } from './book.model';

@Component({
  selector: 'app-book-list-container',
  templateUrl: './book-list.container.component.html',
})
export class BookListContainerComponent implements OnInit {

  books: Book[] | undefined;

  constructor() { }

  ngOnInit(): void {
    this.books = [
      { id: 1, title: 'Wojna futbolowa', author: 'Ryszard Kapuściński', isRead: false },
      { id: 2, title: 'Mroczna wieża tom 1', author: 'Stephen King', isRead: false },
      { id: 3, title: 'Czarnobylska modlitwa. Kronika przyszłości', author: 'Swietłana Aleksiejewicz', isRead: false },
    ]
  }

  bookReadEvent(bookRead: Book): void {
    if (this.books) {
      this.books = this.books.map((book: Book) => {
        if (book.id === bookRead.id) {
          bookRead.isRead = true;
        }
        return book;
      })
    }
  }

}

Za pomocą metody: bookReadEvent odbieramy zdarzenie wysłane z komponentu typu Dumb i przetwarzamy dane tutaj.

Potrzebny będzie jeszcze widok dla: book-list.container.component.html:

<app-book-list
  [books]="books"
  (bookReadEvent)="bookReadEvent($event)">
</app-book-list>

Na koniec musimy jeszcze gdzieś dodać odwołanie do komponentu Smart żeby go wyświetlić na przykład w app.component.html:

<app-book-list-container></app-book-list-container>

Podsumowanie

Na tym prostym przykładzie przedstawiłem na czym polega idea komponentów typu Smart i Dumb. Dzięki takiemu podziałowi oddzielamy od siebie dwie odpowiedzialności: przetwarzanie danych(Smart), wyświetlanie danych(Dumb).

Dodatkowo takie podejście zapewnia nam reużywalność komponentów typu Dumb ponieważ nie tworzymy tam niepotrzebnych zależności.