Reutilizare. Un cuvânt care mi-a trecut prin minte de mai multe ori recent, în timp ce lucram la un proiect Angular. Am decis să îmi creez propriile mele reutilizabile angulare și blog despre experiență.

Cum se creeaza un indicator de incarcare reutilizabil pentru proiecte
Fotografie de Luca Bravo pe Unsplash

Deci, ce este mai exact un indicator de încărcare? De obicei, este un fel de spinner cu o suprapunere, care previne interacțiunile utilizatorului. Interfața de utilizare nu poate fi făcută clic și focalizarea este blocată. Prin urmare, utilizatorul nu poate muta accidental datele sau starea aplicației prin interacțiunea cu intrările din spatele suprapunerii.

După oprirea încărcării, suprapunerea cu filatorul este îndepărtată din DOM și elementul focalizat anterior este focalizat din nou.

Am început cu logica care ar declanșa spinnerul. Pentru asta am folosit un simplu BehaviorSubject și două funcții de decorare:

import {BehaviorSubject} from 'rxjs';
import {distinctUntilChanged} from 'rxjs/operators';

const indicatorSubject = new BehaviorSubject<boolean>(false);

export const isLoading$ = indicatorSubject.asObservable().pipe(distinctUntilChanged());

export function startLoadingIndicator(target: any, propertyKey: string | symbol, propertyDescriptor: PropertyDescriptor): any {
  const original = propertyDescriptor.value;
  propertyDescriptor.value = (...args) => {
    indicatorSubject.next(true);
    const result = original.call(target, ...args);
    return result;
  };
  return propertyDescriptor;
}

export function stopLoadingIndicator(target: any, propertyKey: string, propertyDescriptor: PropertyDescriptor): any {
  const original = propertyDescriptor.value;
  propertyDescriptor.value = (...args) => {
    indicatorSubject.next(false);
    const result = original.call(target, ...args);
    return result;
  };
  return propertyDescriptor;
}

În acest fel, nu avem nevoie de un serviciu injectabil pentru declanșarea sau oprirea spinnerului. Cele două metode simple de decorare apelează doar .next () pe BehaviorSubject. Variabila isLoading $ este exportată ca observabilă.

Să-l folosim în componenta noastră indicator de încărcare.

get isLoading$(): Observable<boolean> {
  return isLoading$;
}

Acum, în șablonul dvs., puteți utiliza isLoading $ getter cu conducta asincronizată pentru a afișa / ascunde întreaga suprapunere.

<div class="btp-overlay" *ngIf="isLoading$ | async">
  <div class="btp-loading-indicator__container" [style.width]="indicatorSize" [style.height]="indicatorSize">
    <btp-spinner></btp-spinner>
  </div>
</div>

După cum puteți vedea, am extras filatorul în propria componentă și am făcut alte câteva lucruri. Am adăugat unele logici pentru captarea focalizării și posibilitatea de a configura dimensiunea și culoarea spinnerului folosind un InjectionToken.

import {LoadingIndicatorConfig} from './interfaces/loading-indicator.interfaces';
import {InjectionToken} from '@angular/core';

export const DEFAULT_CONFIG: LoadingIndicatorConfig = {
  size: 160,
  color: '#7B1FA2'
};

export const LOADING_INDICATOR_CONFIG: InjectionToken<string> = new InjectionToken('btp-li-conf');

Furnizarea de obiecte de configurare folosind InjectionToken este o modalitate bună de a furniza proprietăți configurabile în constructor.

  constructor(@Inject(LOADING_INDICATOR_CONFIG)
              private config: LoadingIndicatorConfig) {
  }

Acum trebuie să grupăm totul într-un NgModule:

import {ModuleWithProviders, NgModule} from '@angular/core';
import {LoadingIndicatorComponent} from './loading-indicator/loading-indicator.component';
import {CommonModule} from '@angular/common';
import {SpinnerComponent} from './spinner/spinner.component';
import {DEFAULT_CONFIG, LOADING_INDICATOR_CONFIG} from './loading-indicator.config';

@NgModule({
  declarations: [LoadingIndicatorComponent, SpinnerComponent],
  imports: [
    CommonModule
  ],
  exports: [LoadingIndicatorComponent]
})
export class LoadingIndicatorModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: LoadingIndicatorModule,
      providers: [{provide: LOADING_INDICATOR_CONFIG, useValue: DEFAULT_CONFIG}]
    };
  }
}

După construirea bibliotecii și instalarea acesteia într-o aplicație angulară, declanșarea filatorului devine extrem de ușoară folosind cele două metode de decorare.

În primul rând, trebuie să adăugăm componenta la locul potrivit din DOM. De obicei, îl pun în componenta de introducere a aplicației, în partea de jos a șablonului.

<h1>Loading indicator</h1>


<button data-test-id="cy-trigger-indicator" (click)="triggerLoadingIndicator()">START LOADING</button>

<btp-loading-indicator></btp-loading-indicator>

După cum puteți vedea, metoda triggerLoadingIndicator este apelată când se face clic pe buton. Această metodă este o metodă decorată:

  @startLoadingIndicator
  triggerLoadingIndicator() {
    setTimeout(this.triggerLoadingIndicatorStop.bind(this), 500);
  }

  @stopLoadingIndicator
  triggerLoadingIndicatorStop() {
    console.log('stopped');
  }

Și asta este. Desigur, într-o aplicație reală, s-ar putea folosi pentru a decora cererile și gestionarele de răspuns respective. Un sfat rapid: decorați și gestionatorii de erori. 🙂

Vă mulțumesc foarte mult pentru că ați citit această postare pe blog. Dacă doriți să încercați lib-ul menționat mai sus, puteți găsi pachetul și instrucțiunile de instalare aici.

Poți să mă urmărești și mai departe Stare de nervozitate sau GitHub.