Angular2 : 구성 요소를 렌더링하기 전에 데이터를로드하는 방법은 무엇입니까?


143

구성 요소가 렌더링되기 전에 API에서 이벤트를로드하려고합니다. 현재 구성 요소의 ngOnInit 함수에서 호출하는 API 서비스를 사용하고 있습니다.

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

ngOnInit함수 에서 API 호출 이 데이터 반입을 완료 하기 전에 구성 요소가 렌더링됩니다 . 따라서 뷰 템플릿에서 이벤트 ID를 볼 수 없습니다. 이것이 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내가 intendend로 일했다. 감사! 그것이 나를 허용하자마자 당신이 대답을 받아 들일 것입니다.
Tom Aalbers

어떤 이유로 든 Angular 1.x 에서이 접근법을 사용했습니다. IMHO, 이것은 적절한 솔루션 대신 해킹으로 간주해야합니다. 우리는 각 5에서 더 나은해야
windmaomao

그 점에 대해 정확히 무엇을 좋아하지 않습니까? 두 가지 접근 방식이 완벽하다고 생각합니다.
Günter Zöchbauer

54

내가 찾은 좋은 해결책은 다음과 같은 UI를 수행하는 것입니다.

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

isDataLoaded가 true 인 경우에만 페이지가 렌더링됩니다.


3
Angular의 단방향 데이터 흐름 규칙은 뷰가 구성된 후 뷰의 업데이트를 금지하기 때문에이 솔루션은 Angular> = 2가 아닌 Angular 1에만 있다고 생각합니다. 구성 요소의보기가 구성된 후에이 두 후크가 모두 실행됩니다.
zt1983811

1
div는 전혀 변환되지 않습니다. 렌더링을 멈추지 않고 지연시키고 싶습니다. 작동하지 않는 이유는 angular2에 양방향 바인딩이 없기 때문입니다. @phil 어떻게 작동했는지 설명해 주실 수 있습니까?
ishandutta2007

1
좋아 ChangeDetectionStrategy.Push, ChangeDetectionStrategy.Default템플릿 을 사용하여 양방향 바인딩 되도록 변경했습니다 .
ishandutta2007

각도 6. 작동합니다.
TDP

각도 2x에서 훌륭한 해킹이 작동합니다.
nishil bhave

48

Angular2 +의 리졸버를 사용하여 데이터를 프리 페치 할 수 있습니다. 리졸버는 컴포넌트가 완전히로드되기 전에 데이터를 처리합니다.

특정 상황이 발생하는 경우에만 구성 요소를로드하려는 경우가 많습니다. 예를 들어 이미 로그인 한 사람 만 대시 보드로 이동하는 경우에는이 경우 해결 프로그램이 매우 편리합니다.

리졸버를 사용하여 데이터를 컴포넌트로 보낼 수있는 방법 중 하나를 위해 내가 작성한 간단한 다이어그램을 살펴보십시오.

여기에 이미지 설명을 입력하십시오

코드에 리졸버 를 적용하는 것은 매우 간단합니다. 리졸버를 만드는 방법을 알 수 있도록 스 니펫을 만들었습니다.

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

그리고 Route에서 다음과 같이 (보통 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 }
크리스 헤인즈

MyData가 MyService에 정의 된 모델 객체입니까?
Isuru Madusanka

1
이 솔루션은 페이지가로드되기 전에 데이터를 가져 오는 데 좋지만 로그 된 사용자 또는 사용자 역할을 확인하는 데는 적합하지 않습니다. 당신은 감시 사용해야 들어
천사 Q
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.