Angular2: Làm thế nào để tải dữ liệu trước khi kết xuất thành phần?


143

Tôi đang cố tải một sự kiện từ API của mình trước khi thành phần được hiển thị. Hiện tại tôi đang sử dụng dịch vụ API mà tôi gọi từ chức năng ngOnInit của thành phần.

EventRegisterThành phần của tôi :

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
    }
}

EventRegisterMẫu của tôi

<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>

Dịch vụ API của tôi

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();
        }     
    }     
}

Thành phần được kết xuất trước khi lệnh gọi API trong ngOnInithàm được thực hiện tìm nạp dữ liệu. Vì vậy, tôi không bao giờ có thể nhìn thấy id sự kiện trong mẫu xem của tôi. Vì vậy, có vẻ như đây là một vấn đề ASYNC. Tôi dự kiến ​​ràng buộc của ev( EventRegisterthành phần) để thực hiện một số công việc sau khi thuộc evtính được thiết lập. Đáng buồn thay, nó không hiển thị divđược đánh dấu *ngIf="ev"khi tài sản được thiết lập.

Câu hỏi: Tôi có đang sử dụng một cách tiếp cận tốt? Nếu không; Cách tốt nhất để tải dữ liệu trước khi thành phần bắt đầu kết xuất là gì?

LƯU Ý: Cách ngOnInittiếp cận được sử dụng trong hướng dẫn angular2 này .

BIÊN TẬP:

Hai giải pháp có thể. Đầu tiên là bỏ qua fetchEventvà chỉ sử dụng dịch vụ API trong ngOnInithàm.

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

Giải pháp thứ hai. Giống như câu trả lời được đưa ra.

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

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

Câu trả lời:


127

cập nhật

nguyên

Khi console.log(this.ev)được thực hiện sau this.fetchEvent();, điều này không có nghĩa là fetchEvent()cuộc gọi được thực hiện, điều này chỉ có nghĩa là nó được lên lịch. Khi console.log(this.ev)được thực thi, cuộc gọi đến máy chủ thậm chí không được thực hiện và tất nhiên vẫn chưa trả về giá trị.

Thay đổi fetchEvent()để trả lại mộtPromise

 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
    });
 }

thay đổi ngOnInit()để chờ Promisehoàn thành

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

Điều này thực sự sẽ không mua cho bạn nhiều cho trường hợp sử dụng của bạn.

Đề xuất của tôi: Gói toàn bộ mẫu của bạn trong một <div *ngIf="isDataAvailable"> (template content) </div>

và trong ngOnInit()

isDataAvailable:boolean = false;

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

3
Trên thực tế, bình luận đầu tiên của bạn làm việc rất đẹp. Tôi chỉ loại bỏ toàn bộ fetchEventchức năng và chỉ đưa apiService.get.eventchức năng vào ngOnInitvà nó hoạt động như tôi dự định. Cảm ơn! Tôi sẽ chấp nhận bạn trả lời ngay khi nó cho phép tôi quá.
Tom Aalbers

Vì một số lý do, tôi đã sử dụng phương pháp này trên Angular 1.x. IMHO, điều này nên được coi là một hack thay vì một giải pháp thích hợp. Chúng ta nên làm tốt hơn trong góc 5.
Windmaomao

Chính xác thì bạn không thích điều gì? Tôi nghĩ cả hai cách tiếp cận đều hoàn toàn tốt.
Günter Zöchbauer

54

Một giải pháp hay mà tôi đã tìm thấy là thực hiện trên UI một cái gì đó như:

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

Chỉ khi: isDataLoaded là đúng thì trang mới được hiển thị.


3
Tôi nghĩ giải pháp này chỉ dành cho Angular 1 chứ không phải Angular> = 2 vì quy tắc luồng dữ liệu một chiều của Angular cấm cập nhật cho chế độ xem sau khi nó được soạn thảo. Cả hai móc này bắn sau khi chế độ xem của thành phần đã được sáng tác.
zt1983811

1
Các div không được rending tất cả. Chúng tôi không có ý định ngăn chặn nó hiển thị, chúng tôi muốn trì hoãn nó. Lý do nó không hoạt động là vì không có ràng buộc hai chiều trong angular2. @phil bạn có thể giải thích làm thế nào nó làm việc cho bạn?
ishandutta2007

1
ok Tôi đã sử dụng ChangeDetectionStrategy.Push, thay đổi nó để ChangeDetectionStrategy.Defaultlàm cho nó hai chiều được liên kết với mẫu.
ishandutta2007

góc 6. Công trình.
TDP

Thật là một hack tuyệt vời hoạt động trong góc 2x.
Nishil bhave

48

Bạn có thể tìm nạp trước dữ liệu của mình bằng cách sử dụng Bộ giải quyết trong Angular2 + , Bộ giải quyết xử lý dữ liệu của bạn trước khi Thành phần của bạn được tải đầy đủ.

Có nhiều trường hợp bạn chỉ muốn tải thành phần của mình nếu có điều gì đó xảy ra, ví dụ: chỉ điều hướng đến Bảng điều khiển nếu người đó đã đăng nhập, trong trường hợp này, Bộ giải quyết rất tiện dụng.

Nhìn vào sơ đồ đơn giản mà tôi đã tạo cho bạn để biết một trong những cách bạn có thể sử dụng trình phân giải để gửi dữ liệu đến thành phần của mình.

nhập mô tả hình ảnh ở đây

Áp dụng Resolver cho mã của bạn khá đơn giản, tôi đã tạo các đoạn mã cho bạn để xem cách Resolver có thể được tạo:

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;
      }
    });
  }
}

và trong mô-đun :

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

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

và bạn có thể truy cập nó trong Thành phần của bạn như thế này:

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

Và trong Tuyến đường một cái gì đó như thế này (thường là trong tệp app.routing.ts):

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

1
Tôi nghĩ rằng bạn đã bỏ lỡ mục cấu hình tuyến đường trong Routesmảng (thường là trong tệp app.routing.ts ):{path: 'yourpath/:id', component: YourComponent, resolve: { myData: MyData}}
Voicu

1
@Voicu, nó không phải là bao gồm tất cả các phần, nhưng tôi đồng ý, nó làm cho câu trả lời toàn diện hơn, vì vậy tôi đã thêm ... Cảm ơn
Alireza

5
Chỉ cần sửa phần cuối cùng, tham số giải quyết trong tuyến cần trỏ đến chính trình phân giải, chứ không phải kiểu dữ liệu, tức làresolve: { myData: MyResolver }
Chris Haines

MyData có phải là một đối tượng mô hình được xác định trong MyService không?
Isuru Madusanka

1
giải pháp này tốt cho việc tìm nạp dữ liệu trước khi tải trang nhưng không phải để kiểm tra người dùng đã đăng nhập cũng như vai trò người dùng. Vì vậy, bạn nên sử dụng Guards
Angel Q
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.