Angular2: วิธีโหลดข้อมูลก่อนที่จะแสดงองค์ประกอบหรือไม่


143

ฉันกำลังพยายามโหลดกิจกรรมจาก API ของฉันก่อนที่ส่วนประกอบจะแสดงผล ขณะนี้ฉันกำลังใช้บริการ API ของฉันซึ่งฉันเรียกจากฟังก์ชัน ngOnInit ของส่วนประกอบ

EventRegisterองค์ประกอบของฉัน:

import {Component, OnInit, ElementRef} from "angular2/core";
import {ApiService} from "../../services/api.service";
import {EventModel} from '../../models/EventModel';
import {Router, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig, RouteParams, RouterLink} from 'angular2/router';
import {FORM_PROVIDERS, FORM_DIRECTIVES, Control} from 'angular2/common';

@Component({
    selector: "register",
    templateUrl: "/events/register"
    // provider array is added in parent component
})

export class EventRegister implements OnInit {
    eventId: string;
    ev: EventModel;

    constructor(private _apiService: ApiService, 
                        params: RouteParams) {
        this.eventId = params.get('id');
    }

    fetchEvent(): void {
        this._apiService.get.event(this.eventId).then(event => {
            this.ev = event;
            console.log(event); // Has a value
            console.log(this.ev); // Has a value
        });
    }

    ngOnInit() {
        this.fetchEvent();
        console.log(this.ev); // Has NO value
    }
}

EventRegisterแม่แบบของฉัน

<register>
    Hi this sentence is always visible, even if `ev property` is not loaded yet    
    <div *ngIf="ev">
        I should be visible as soon the `ev property` is loaded. Currently I am never shown.
        <span>{{event.id }}</span>
    </div>
</register>

บริการ API ของฉัน

import "rxjs/Rx"
import {Http} from "angular2/http";
import {Injectable} from "angular2/core";
import {EventModel} from '../models/EventModel';

@Injectable()
export class ApiService {
    constructor(private http: Http) { }
    get = {
        event: (eventId: string): Promise<EventModel> => {
            return this.http.get("api/events/" + eventId).map(response => {
                return response.json(); // Has a value
            }).toPromise();
        }     
    }     
}

ส่วนประกอบได้รับการแสดงผลก่อนที่การเรียก API ในngOnInitฟังก์ชั่นจะทำการดึงข้อมูล ดังนั้นฉันไม่เคยเห็นรหัสเหตุการณ์ในเทมเพลตมุมมองของฉัน ดูเหมือนว่านี่เป็นปัญหาของ ASYNC ฉันคาดว่าการรวมev( EventRegisterองค์ประกอบ) เพื่อทำงานบางอย่างหลังจากevตั้งค่าคุณสมบัติ น่าเศร้าที่มันไม่ได้แสดงdivเครื่องหมายด้วย*ngIf="ev"เมื่อคุณสมบัติได้รับการตั้งค่า

คำถาม:ฉันใช้วิธีการที่ดีหรือไม่? ถ้าไม่; วิธีที่ดีที่สุดในการโหลดข้อมูลก่อนที่ส่วนประกอบจะเริ่มแสดงผลคืออะไร

หมายเหตุ:ngOnInitวิธีการที่ใช้ในการนี้กวดวิชา angular2

แก้ไข:

สองวิธีที่เป็นไปได้ สิ่งแรกคือการทิ้งfetchEventและเพียงใช้บริการ API ในngOnInitฟังก์ชัน

ngOnInit() {
    this._apiService.get.event(this.eventId).then(event => this.ev = event);
}

ทางออกที่สอง ชอบคำตอบที่ได้รับ

fetchEvent(): Promise<EventModel> {
    return this._apiService.get.event(this.eventId);
}

ngOnInit() {
    this.fetchEvent().then(event => this.ev = event);
}

คำตอบ:


127

ปรับปรุง

เป็นต้นฉบับ

เมื่อconsole.log(this.ev)มีการดำเนินการหลังจากthis.fetchEvent();นี้ไม่ได้หมายความว่าการfetchEvent()โทรจะเสร็จสิ้นซึ่งหมายความว่ามีการกำหนดเวลาไว้เท่านั้น เมื่อconsole.log(this.ev)มีการดำเนินการเรียกไปยังเซิร์ฟเวอร์ไม่ได้ทำและแน่นอนยังไม่ได้ส่งกลับค่า

เปลี่ยนfetchEvent()เพื่อส่งคืนPromise

 fetchEvent(){
    return  this._apiService.get.event(this.eventId).then(event => {
        this.ev = event;
        console.log(event); // Has a value
        console.log(this.ev); // Has a value
    });
 }

เปลี่ยนngOnInit()เป็นรอPromiseให้เสร็จสมบูรณ์

ngOnInit() {
    this.fetchEvent().then(() =>
    console.log(this.ev)); // Now has value;
}

นี่จะไม่ซื้อคุณมากสำหรับกรณีการใช้งานของคุณ

คำแนะนำของฉัน: ห่อเทมเพลตทั้งหมดของคุณใน <div *ngIf="isDataAvailable"> (template content) </div>

และใน ngOnInit()

isDataAvailable:boolean = false;

ngOnInit() {
    this.fetchEvent().then(() =>
    this.isDataAvailable = true); // Now has value;
}

3
ที่จริงแล้วความคิดเห็นแรกของคุณทำงานได้อย่างสวยงาม ฉันเพิ่งลบfetchEventฟังก์ชั่นทั้งหมดและใส่apiService.get.eventฟังก์ชั่นในngOnInitและมันทำงานตามที่ฉันตั้งใจ ขอบคุณ! ฉันจะยอมรับคำตอบของคุณทันทีที่ฉันอนุญาต
Tom Aalbers

ด้วยเหตุผลบางอย่างฉันใช้วิธีนี้กับ Angular 1.x IMHO นี่ควรเป็นแฮ็คแทนที่จะเป็นทางออกที่เหมาะสม เราควรทำให้ดีขึ้นในเชิงมุม 5
windmaomao

อะไรที่คุณไม่ชอบเกี่ยวกับเรื่องนี้? ฉันคิดว่าวิธีการทั้งสองนั้นดีมาก
GünterZöchbauer

54

ทางออกที่ดีที่ฉันได้พบคือทำใน UI เช่น:

<div *ngIf="isDataLoaded">
 ...Your page...
</div

เฉพาะเมื่อ: isDataLoaded เป็นจริงหน้าจะแสดงผล


3
ฉันคิดว่าโซลูชันนี้ใช้สำหรับ Angular 1 ไม่ใช่ Angular> = 2 เนื่องจากกฎการไหลของข้อมูลทางเดียวของ Angular ห้ามมิให้อัปเดตมุมมองหลังจากที่ได้สร้างขึ้นแล้ว ตะขอทั้งสองนี้ใช้งานได้หลังจากมุมมองของส่วนประกอบถูกสร้างขึ้น
zt1983811

1
div ไม่ได้รับการแก้ไขเลย เราไม่ต้องการหยุดยั้งการเรนเดอร์เราต้องการหน่วงเวลา เหตุผลที่มันไม่ทำงานเป็นเพราะไม่มีสองผูกพันใน angular2 @phil คุณสามารถอธิบายวิธีการทำงานให้คุณได้อย่างไร
ishandutta2007

1
ตกลงฉันใช้ChangeDetectionStrategy.Pushเปลี่ยนไปChangeDetectionStrategy.Defaultทำให้มันผูกกับแม่แบบได้สองทาง
ishandutta2007

เชิงมุม 6. งาน
TDP

แฮ็คที่ยอดเยี่ยมนั้นทำงานได้ในมุม 2x
nishil Bhave

48

คุณสามารถดึงข้อมูลของคุณล่วงหน้าโดยใช้ Resolvers ในAngular2 + , Resolvers ประมวลผลข้อมูลของคุณก่อนที่ชิ้นส่วนของคุณจะโหลดเต็ม

มีหลายกรณีที่คุณต้องการโหลดส่วนประกอบของคุณเฉพาะในกรณีที่มีบางอย่างเกิดขึ้นตัวอย่างเช่นนำทางไปยังแดชบอร์ดเฉพาะในกรณีที่บุคคลนั้นเข้าสู่ระบบแล้วในกรณีนี้ผู้แก้ปัญหามีประโยชน์มาก

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

ป้อนคำอธิบายรูปภาพที่นี่

การใช้Resolverกับโค้ดของคุณนั้นง่ายมากฉันสร้างตัวอย่างเพื่อให้คุณดูว่า Resolver สามารถสร้างได้อย่างไร:

import { Injectable } from '@angular/core';
import { Router, Resolve, RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router';
import { MyData, MyService } from './my.service';

@Injectable()
export class MyResolver implements Resolve<MyData> {
  constructor(private ms: MyService, private router: Router) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<MyData> {
    let id = route.params['id'];

    return this.ms.getId(id).then(data => {
      if (data) {
        return data;
      } else {
        this.router.navigate(['/login']);
        return;
      }
    });
  }
}

และในโมดูล :

import { MyResolver } from './my-resolver.service';

@NgModule({
  imports: [
    RouterModule.forChild(myRoutes)
  ],
  exports: [
    RouterModule
  ],
  providers: [
    MyResolver
  ]
})
export class MyModule { }

และคุณสามารถเข้าถึงได้ในคอมโพเนนต์ของคุณเช่นนี้:

/////
 ngOnInit() {
    this.route.data
      .subscribe((data: { mydata: myData }) => {
        this.id = data.mydata.id;
      });
  }
/////

และในเส้นทางบางอย่างเช่นนี้ (มักอยู่ในไฟล์ app.routing.ts):

////
{path: 'yourpath/:id', component: YourComponent, resolve: { myData: MyResolver}}
////

1
ฉันคิดว่าคุณพลาดรายการการตั้งค่าเส้นทางภายใต้Routesอาเรย์ (โดยปกติจะอยู่ในไฟล์app.routing.ts ):{path: 'yourpath/:id', component: YourComponent, resolve: { myData: MyData}}
Voicu

1
@Voicu มันไม่ควรจะครอบคลุมทุกส่วน แต่ฉันเห็นด้วยกับมันทำให้คำตอบที่ครอบคลุมมากขึ้นดังนั้นฉันจึงเพิ่ม ... ขอบคุณ
Alireza

5
เพียงเพื่อแก้ไขส่วนสุดท้ายพารามิเตอร์การแก้ไขในเส้นทางต้องชี้ไปที่ตัวแก้ไขไม่ใช่ประเภทข้อมูลเช่นresolve: { myData: MyResolver }
Chris Haines

MyData นั้นเป็นโมเดลวัตถุที่กำหนดใน MyService หรือไม่
Isuru Madusanka

1
วิธีนี้เป็นวิธีที่ดีสำหรับการดึงข้อมูลก่อนที่จะโหลดหน้าเว็บ แต่ไม่ใช่สำหรับการตรวจสอบผู้ใช้ที่เข้าสู่ระบบหรือบทบาทผู้ใช้ เพื่อที่คุณควรใช้ Guards
Angel Q
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.