Add Loading Spinner Component with Declarative Reactive Programming

נראה איך להוסיף ספינר טעינה שירוץ בזמן שהמידע שלנו נטען. על מנת לעשות את זה ניצור קומפוננטה ונקרא לה loading.

אני משתמשת בקוד של bootstrap לספינר שלי, את הקוד אני אשים בקובץ ה-html של הקומפוננטה.

יצירת ספינר

קובץ loading.component.html

<div class="d-flex justify-content-center align-items-center min-vh-100" id="loadingoverlay">
  <div class="spinner-border text-light" role="status">
  </div>
</div>

כדי שהספינר יופיע מעל לכל מה שיש על המסך, הוספתי קצת CSS.

קובץ loading.component.scss

#loadingoverlay {
  position: fixed;
  top: 0;
  z-index: 9999;
  width: 100%;
  height: 100%;
  display: none;
  background: rgba(0,0,0,0.6);
}

את הקומפוננטה נכנסים ל-app.component כדי שכולם יוכלו להשתמש בה.

קובץ app.component.html

<app-header></app-header>

<app-loading></app-loading>

<div class="container pt-5 pb-5">
  <div class="row">
    <div class="col-md-12">
      <router-outlet></router-outlet>
    </div>
  </div>
</div>

הספינר מופיע עכשיו באופן קבוע, אנחנו רוצים שהוא יופיע רק כשהמידע נטען.

פעולות הספינר

ניצור service שיטפל בספינר.

קובץ loader.service.ts

export class LoaderService {
  private loadingSubject = new Subject<boolean>();
  loadingAction$ = this.loadingSubject.asObservable();

  showLoader(){
    this.loadingSubject.next(true);
  }

  hideLoader(){
    this.loadingSubject.next(false);
  }
}

בקובץ אנחנו מייצרים subject שיעקוב אחרי ערכי הספינר ופונקציות להפעלה ולכיבוי של הספינר.

הפעלת הספינר

את ההפעלה של הספינר נעשה מתוך הקומפוננטה שבה אנחנו טוענים את המידע. נייבא את הספינר ל-constructor, נפעיל אותו כשהדף עולה ב-ngOnInit ואז כשמקבלים את ה-result אפשר להשתמש ב-tap על מנת לעצור את הספינר.

קובץ declarative-posts.component.ts

filteredPosts$ = combineLatest([this.posts$, this.selectedCategoryAction$]).
    pipe(
      tap(data => { this.loaderService.hideLoader(); }),
      map(([posts, selectedCategoryId]) => {
      return posts.filter(
        (post) => selectedCategoryId ? post.categoryId == selectedCategoryId : true
      )
}));

constructor(private postService: DeclarativePostsService,
    private categoryService: DeclarativeCategoriesService,
    private loaderService: LoaderService){}

ngOnInit(){
    this.loaderService.showLoader();
}

הדבר האחרון שנשאר זה להוסיף תנאי בקובץ ה-app שאומר להציג את הקומפוננטה רק אם נשלח true ל-subject.

קובץ app.component.ts

export class AppComponent {
  title = 'declarative-prog';
  showLoader$ = this.loaderService.loadingAction$;

  constructor(private loaderService: LoaderService){}
}

קובץ app.component.html

<div *ngIf="showLoader$ | async as showLoader">
  <app-loading *ngIf="showLoader"></app-loading>
</div>

עכשיו הכל עובד, אבל זה רץ די מהר. אם רוצים לראות את הפעולה אפשר להוסיף זמנית השהייה לטעינת הפוסטים.

קובץ declarative-posts.service.ts

posts$ = this.http.get<Post[]>(`${this.baseUrl}/posts`)
  .pipe(
    delay(2000),
    catchError(this.handleError)
);

אפשר להשתמש בספינר בכל קומפוננטה שנרצה.

ניווט במאמר

מאמרים אחרונים

Weekly Tutorial