ค้นหาเว็บไซต์

typescript - รอข้อมูลก่อนการเรนเดอร์มุมมองใน Angular 5


ฉันเป็นมือใหม่ใน Angular 5 และสามารถสร้างวิวที่เข้าถึงเมธอดของเซอร์วิสได้โดยใช้ subscribe แต่พบปัญหาเรื่องความล่าช้าในการเรนเดอร์ ผู้ใช้จะเห็นสตริง "undefined" ก่อน และหลังจาก 0.5 วินาทีมันจะเปลี่ยนเป็นค่าที่ถูกต้อง

นี่คือไฟล์ .ts สำหรับคอมโพเนนต์:

 public trips: Trip[];
  public numberOfUsers : Map<string, number> = new Map<string, number>();

  constructor(private tripService: TripService) { }

  ngOnInit() {
   this.getTrips();
  }

  getTrips() {
    this.tripService.getTripForMe().subscribe(
      data => { this.trips = data;},
      err => console.error(err),
      () => this.initNumberOfUsers()
    );
  }

  initNumberOfUsers() {
    for (let i = 0; i < this.trips.length; i++) {
      let count;
      this.tripService.getNumberOfUsers().subscribe(
        data => { count = data},
        err => console.error(err),
        () => this.numberOfUsers.set(this.trips[i].id, count)
      );


      ;
    }
  }

  getNumberOfUsers(data) {
    return this.numberOfUsers.get(data.id);
  }

เมธอด initNumberOfUsers ถูกเรียกหลังจากการสมัครครั้งแรกเสร็จสิ้นซึ่งเป็นเรื่องที่ดี แต่ getNumberOfUsers(data) น่าจะถูกเรียกก่อนและได้ค่า "undefined"

นี่คือส่วนที่เกี่ยวข้องจากเทมเพลต ("data" คือค่าปัจจุบันของลูป ngFor)

<span [innerText]="getNumberOfUsers(data)"></span>

มีวิธีใดที่จะทำให้แน่ใจว่าทั้งสองวิธีการสมัครจะเสร็จสิ้นตามลำดับก่อนการแสดงผลหรือไม่?

อัปเดต

ฉันสามารถสร้างตัวแก้ปัญหาได้และคอนโซลแสดงวัตถุ แต่ฉันมีปัญหาในการส่งข้อมูลไปยังคอมโพเนนต์

รหัสรีโวลเวอร์:

import { Injectable } from '@angular/core';
import { Router, Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Trip} from ....
import { TripService } from .....
import { Observable } from 'rxjs/Rx';

@Injectable()
export class TripsResolve implements Resolve<Trip[]> {

   public trips: Trip[];

   constructor(private service: TripService) {}

   resolve(route: ActivatedRouteSnapshot, 
           state: RouterStateSnapshot) {
     this.service.getTrips().subscribe(
        data => { this.trips = data },
        err => console.error(err),
        () => console.log(this.trips)
      );
      return this.trips;
   }
}

จากเส้นทาง

resolve: {
         trips: TripsResolve
      }

จากโมดูล

providers: [
        ...
        TripsResolve,
        ...

ข้อมูลไม่ถูกส่ง/ไม่พร้อมใช้งานในคอมโพเนนต์:

import { Component, OnInit } from '@angular/core';
import { Trip } from ...
import { Router, ActivatedRoute } from '@angular/router';

@Component({
  selector: ...
  templateUrl: ...
  styleUrls: ...
})
export class MyComponent implements OnInit {

    public trips: Trip[];

    constructor(private route: ActivatedRoute,
                private router: Router) {
    }

    ngOnInit() {
        this.route.data.forEach(data => {
            this.trips = data.trips;
            console.log(this.trips);  //can't see it
       });

    } 
}

มีไอเดียไหมว่าฉันจะเข้าถึง/ดึงข้อมูลในคอมโพเนนต์ได้อย่างไร?

คำตอบ #1

ฉันเคยเจอปัญหาแบบเดียวกันเมื่อฉันจัดการกับข้อมูลผ่านการเรียก API

ฉันได้ลองใช้สองวิธีแก้ปัญหา

มีการใช้ *ngIf

DOM จะถูกเติมเต็มก็ต่อเมื่อข้อมูลพร้อมแล้ว

<div *ngIf="isLoaded">Text to show</div>

อีกหนึ่งวิธีคือการใช้ resolver ซึ่งก่อนที่จะแสดงมุมมองจริง ๆ มันจะเตรียมข้อมูลก่อน แต่บางครั้ง resolver จะทำให้หน้าจอของคุณว่างเปล่า หากการโหลดข้อมูลใช้เวลานานเกินไป

@Injectable()
export class HeroDetailResolve implements Resolve {
    constructor(private heroService: HeroService, private router: Router) { }

    resolve(route: ActivatedRouteSnapshot): Promise | boolean {
        let id = +route.params['id'];
        return this.heroService.getHero(id).then(hero => {
            if (hero) {
                return hero;
            } else { // id not found
                this.router.navigate(['/dashboard']);
                return false;
            }
        });
    }
}

ดังนั้นตอนนี้คุณมีตัวเลือกสองทาง

ฉันขอแนะนำ *ngIf. ขณะกำลังโหลดข้อมูล แสดง GIF โหลดหรือข้อความบางอย่างเพื่อให้ผู้ใช้ทราบว่ามีบางอย่างเกิดขึ้นเบื้องหลัง

คำตอบ #2

ตรวจสอบ resolvers ใน Angular

นี่คือลิงก์: https://angular.io/api/router/Resolve

คล้ายกับผู้รักษา พวกมันทำงานก่อนที่คอมโพเนนต์จะถูกสร้างขึ้นอย่างสมบูรณ์ และป้องกันไม่ให้คุณต้องมีตรรกะ if/else ในเทมเพลตของคุณสำหรับการรอขณะโหลด

ถ้าคุณไม่ต้องการใช้ตัวแก้ปัญหา คุณก็สามารถทำบางอย่างเช่น:

<span *ngIf="myVariable; else stillLoading" [innerText]="getNumberOfUsers(data)"></span>

<ng-template #stillLoading><p>Loading...</p></ng-template>

myVariable จะเป็นอะไรก็ตามที่คุณต้องการกำหนดก่อนที่คุณจะแสดงแท็ก <span> ฉันแค่ไม่อยากมองลึกลงไปในแผนที่ของคุณ :)

การเลือกว่าจะใช้ตัวแก้ไขปัญหาหรือ if/else ในเทมเพลตก็ขึ้นอยู่กับว่าคุณต้องการให้ประสบการณ์ผู้ใช้ของคุณเป็นอย่างไร อาจจะมีเหตุผลอื่นๆ ด้วย แต่เหตุผลนี้น่าจะเป็นสิ่งแรกที่นึกออก

บทความที่เกี่ยวข้อง