Angular: Komponenty, dyrektywy i potoki – jak prawidłowo je tworzyć i testować

Łukasz Flak

Wprowadzenie

Angular to popularny framework do tworzenia aplikacji internetowych. Zapewnia solidny zestaw narzędzi i funkcji do tworzenia dynamicznych i skalowalnych aplikacji. Jedną z kluczowych koncepcji Angular jest wykorzystanie komponentów, dyrektyw i potoków. W tym artykule szczegółowo zbadamy te koncepcje i dowiemy się, jak skutecznie je tworzyć i testować.

Komponenty

Komponenty są elementami składowymi aplikacji Angular. Są one odpowiedzialne za renderowanie interfejsu użytkownika i obsługę interakcji z użytkownikiem. Komponent składa się z trzech głównych części: szablonu, klasy i metadanych.

Szablon definiuje strukturę i układ widoku komponentu. Zazwyczaj jest napisany w HTML i może zawierać specyficzną dla Angular składnię i dyrektywy. Klasa zawiera logikę i dane komponentu. Jest napisana w języku TypeScript i definiuje właściwości i metody używane w szablonie.

Oto przykład prostego komponentu Angular:

import { Component } from '@angular/core';

@Component({
  selector: 'app-example',
  templateUrl: './example.component.html',
  styleUrls: ['./example.component.css']
})
export class ExampleComponent {
  name: string = 'John Doe';
}

W tym przykładzie zdefiniowaliśmy komponent o nazwie ExampleComponent z selektorem app-example. Szablon i style dla tego komponentu są zdefiniowane w osobnych plikach.

Testowanie komponentów

Testowanie komponentów jest ważną częścią zapewniania jakości i niezawodności aplikacji Angular. Angular zapewnia framework testowy o nazwie Jasmine, wraz z narzędziem testowym o nazwie TestBed, aby ułatwić testowanie komponentów.

Aby przetestować komponent, musimy utworzyć plik testowy i skonfigurować TestBed do utworzenia instancji komponentu. Następnie możemy wchodzić w interakcje z komponentem i tworzyć asercje dotyczące jego zachowania.

Oto przykład testu dla ExampleComponent:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ExampleComponent } from './example.component';

describe('ExampleComponent', () => {
  let component: ExampleComponent;
  let fixture: ComponentFixture<ExampleComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ExampleComponent]
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(ExampleComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('powinien utworzyć komponent', () => {
    expect(component).toBeTruthy();
  });

  it('powinien wyświetlać poprawną nazwę', () => {
    const element: HTMLElement = fixture.nativeElement;
    const nameElement = element.querySelector('.name');
    expect(nameElement.textContent).toContain(component.name);
  });
});

W tym teście najpierw konfigurujemy TestBed, aby zawierał ExampleComponent w module testowym. Następnie tworzymy instancję komponentu i uzyskujemy odniesienie do jego fixture. Hook beforeEach jest używany do skonfigurowania środowiska testowego przed każdym przypadkiem testowym. Pierwszy przypadek testowy sprawdza, czy komponent został pomyślnie utworzony. Drugi przypadek testowy weryfikuje, czy nazwa wyświetlana w szablonie jest zgodna z właściwością name komponentu.

Dyrektywy

Dyrektywy są sposobem na rozszerzenie HTML o niestandardowe zachowania. Pozwalają nam manipulować DOM, stosować style i dodawać interaktywność do elementów. W Angular istnieją dwa rodzaje dyrektyw: dyrektywy strukturalne i dyrektywy atrybutów.

Dyrektywy strukturalne modyfikują strukturę DOM poprzez dodawanie lub usuwanie elementów. Są one oznaczone gwiazdką (*) przed nazwą dyrektywy. Dyrektywy atrybutów modyfikują zachowanie lub wygląd elementów.

Oto przykład niestandardowej dyrektywy, która podświetla kolor tła elementu:

import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  constructor(private elementRef: ElementRef) { }

  @HostListener('mouseenter')
  onMouseEnter() {
    this.highlight('yellow');
  }

  @HostListener('mouseleave')
  onMouseLeave() {
    this.highlight(null);
  }

  private highlight(color: string | null) {
    this.elementRef.nativeElement.style.backgroundColor = color;
  }
}

W tym przykładzie zdefiniowaliśmy dyrektywę o nazwie HighlightDirective z selektorem appHighlight. Dyrektywa nasłuchuje zdarzeń mouseenter i mouseleave i odpowiednio zmienia kolor tła elementu.

Testowanie dyrektyw

Testowanie dyrektyw polega na utworzeniu komponentu testowego, który używa dyrektywy i tworzeniu twierdzeń o jej zachowaniu. Możemy użyć tego samego frameworka testowego i narzędzi wspomnianych wcześniej do testowania komponentów.

Oto przykład testu dla HighlightDirective:

import { Component } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HighlightDirective } from './highlight.directive';

@Component({
  template: `
    <div appHighlight></div>
  `
})
class TestComponent { }

describe('HighlightDirective', () => {
  let fixture: ComponentFixture<TestComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [HighlightDirective, TestComponent]
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(TestComponent);
    fixture.detectChanges();
  });

  it('powinien dodawać podświetlenie po najechaniu myszy', () => {
    const element: HTMLElement = fixture.nativeElement.querySelector('div');
    element.dispatchEvent(new Event('mouseenter'));
    fixture.detectChanges();
    expect(element.style.backgroundColor).toBe('yellow');
  });

  it('powinien usuwać podświetlenie po opuszczeniu myszy', () => {
    const element: HTMLElement = fixture.nativeElement.querySelector('div');
    element.dispatchEvent(new Event('mouseleave'));
    fixture.detectChanges();
    expect(element.style.backgroundColor).toBe('');
  });
});

W tym teście stworzyliśmy komponent testowy, który używa HighlightDirective poprzez dodanie go jako atrybutu do pliku element. Następnie zasymulowaliśmy zdarzenia mouseenter i mouseleave na elemencie i stwierdziliśmy, że kolor tła jest odpowiednio stosowany i usuwany.

Potoki

Potoki to sposób na przekształcanie danych w szablonach Angular. Umożliwiają one wykonywanie operacji takich jak filtrowanie, sortowanie i formatowanie danych przed ich wyświetleniem. Angular udostępnia kilka wbudowanych potoków, a także możemy tworzyć niestandardowe potoki odpowiadające naszym konkretnym potrzebom.

Oto przykład niestandardowego potoku, który przekształca ciąg znaków na wielkie litery:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'uppercase'
})
export class UppercasePipe implements PipeTransform {
  transform(value: string): string {
    return value.toUpperCase();
  }
}

W tym przykładzie zdefiniowaliśmy pipe o nazwie UppercasePipe z nazwą uppercase. Metoda transform przyjmuje ciąg znaków jako dane wejściowe i zwraca jego wersję pisaną wielkimi literami.

Testowanie potoków

Testowanie potoków polega na utworzeniu środowiska testowego i wywołaniu metody transform potoku z różnymi wartościami wejściowymi. Następnie możemy potwierdzić, że przekształcone dane wyjściowe są zgodne z naszymi oczekiwaniami.

Oto przykład testu dla UppercasePipe:

import { TestBed } from '@angular/core/testing';
import { UppercasePipe } from './uppercase.pipe';

describe('UppercasePipe', () => {
  let pipe: UppercasePipe;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [UppercasePipe]
    });
    pipe = TestBed.inject(UppercasePipe);
  });

  it('powinien przekształcić dane wejściowe na wielkie litery', () => {
    const input = 'hello world';
    const transformed = pipe.transform(input);
    expect(transformed).toBe('HELLO WORLD');
  });
});

W tym teście skonfigurowaliśmy TestBed, aby zapewnić instancję UppercasePipe. Następnie użyliśmy TestBed.inject(), aby uzyskać odniesienie do potoku. Wywołaliśmy metodę transform z wejściem testowym i potwierdziliśmy, że przekształcone dane wyjściowe są zgodne z naszą oczekiwaną wartością.

Wnioski

Komponenty, dyrektywy i potoki to podstawowe elementy składowe aplikacji Angular. Komponenty zapewniają strukturę i logikę interfejsu użytkownika, dyrektywy rozszerzają HTML o niestandardowe zachowania, a potoki przekształcają dane przed ich wyświetleniem. Testowanie tych elementów zapewnia ich właściwą funkcjonalność i pomaga utrzymać jakość aplikacji. Postępując zgodnie z przykładami i wskazówkami zawartymi w tym artykule, będziesz dobrze przygotowany do skutecznego tworzenia i testowania komponentów, dyrektyw i potoków w swoich projektach Angular. Miłego kodowania!

Bibliografia

Poznaj mageek of j‑labs i daj się zadziwić, jak może wyglądać praca z j‑People!

Skontaktuj się z nami