11

我是新來的Angular 2,我面臨異步HTTP請求和與插值綁定的問題。Angular 2 - 內插和異步http請求綁定

這裏是我的組件:

@Component({ 
    selector: 'info', 
    template: `<h1>{{model.Name}}</h1>` 
}) 
export class InfoComponent implements OnInit { 

    model: any; 

    constructor(
     private _service: BackendService 
    ) { } 

    ngOnInit() { 
     if (this.model == null) { 
      this._service.observableModel$.subscribe(m => this.model = m); 
      this._service.get(); 
     }  
    } 
} 

當模板被渲染,我得到一個錯誤,因爲「模式」尚未設定。

我解決了這個問題,這很醜陋的黑客:

@Component({ 
    selector: 'info', 
    template: ` 
    <template ngFor #model="$implicit" [ngForOf]="models | async"> 
    <h1>{{model.Name}}</h1> 
    </template> 
    ` 
}) 
export class NeadInfoComponent implements OnInit { 

    models: Observable<any>; 

    constructor(
     private _service: BackendService 
    ) { } 

    ngOnInit() { 
     if (this.models == null) { 
      this._service.observableModel$.subscribe(m => this.models = Observable.of([m])); 
      this._service.get(); 
     }  
    } 
} 

我的問題是:如何到我的HTTP調用完成推遲模板渲染或如何在模板而不直接結合插值「模型」值到另一個組件?

謝謝!

回答

12

如果你是從你的服務器返回一個對象,你可以使用safe navigation (previously "Elvis") operator(?。)在你的模板:

@Component({ 
    selector: 'info', 
    template: `<h1>{{model?.Name}}</h1>` 
}) 
export class InfoComponent implements OnInit { 
    model: any; 
    constructor(private _service: BackendService) { } 

    ngOnInit() { 
     this._service.getData().subscribe(m => this.model = m); 
     // getData() looks like the following: 
     // return this._http.get('....') // gets JSON document 
     //  .map(data => data.json()); 
    } 
} 

的工作plunker見this answer

+1

謝謝馬克!貓王做到了!但我還不明白渲染流程。每當組件屬性發生任何變化時,都會呈現模板? –

+2

@TonyAlexanderHild,在角度變化檢測期間(在每個事件之後運行),默認情況下,所有視圖/模板綁定都會被檢查髒,這意味着它們會被檢查是否有更改。當數據從服務器返回時,這是一個事件,所以更改檢測運行。 'model.Name'髒檢查發現已經改變,所以Angular更新了DOM。在Angular向瀏覽器返回控制權之後,它會看到DOM更改並更新我們在屏幕上看到的內容。 –

+0

謝謝@MarkRajcok!現在很清楚。 –

0

此討論列出了一些策略https://github.com/angular/angular/issues/6674#issuecomment-174699245

的問題產生了許多影響。在某些情況下,觀察者應該在框架之外進行管理。

A.你引導然後

B.引導傳遞對象的頂級組件之前,掃描所有的觀測之前,您可以掃描所有觀測。

C.您還可以更改組件中的changeDetection,以便在輸入發生變化時觸發,或手動觸發更改來檢測它們。

D.如果你使用|異步那麼它應該只在頂層使用的,如果你不喜歡使用.subscribe但你真的應該只使用.subscribe。

E.如果您在任何地方使用async,那麼您真正在做的是將控制渲染轉換爲observables,這意味着我們回到了Angular1級聯變化的日子,因此您需要執行C, D,B或A

ChangeDetectionStrategy目前似乎沒有工作。你會簡單地將組件設置爲Detached。

我們還可以使用ngOnInit生命週期掛鉤從更改檢測樹中刪除組件。你需要運行this.ref.detach();其中,裁判通過ChangeDetectorRef

ngOnInit() { 
    this.ref.detach(); 
    } 
    makeYourChanges() { 
    this.ref.reattach(); // attach back to change detector tree 

    this.data.value = Math.random() + ''; // make changes 

    this.ref.detectChanges(); // check as dirty 

    this.ref.detach(); // remove from tree 
    // zone.js triggers changes 
    } 

ChangeDetectorRef

注入也不能包括zone.js和手動控制所有的變化。你也可以注入NgZone來在zone.js之外運行一個操作,所以它不會觸發角度來觸發chanes。例如,

// this example might need a refactor to work with rxjs 5 
export class Timeflies { 
    pos = 'absolute'; 
    color = 'red'; 
    letters: LetterConfig[]; 
    constructor(
    private service: Message, 
    private el: ElementRef, 
    private zone: NgZone) { 

    } 
    ngOnInit() { 
    // initial mapping (before mouse moves) 
    this.letters = this.service.message.map(
     (val, idx) => ({ 
     text: val, 
     top: 100, 
     left: (idx * 20 + 50), 
     index: idx 
     }) 
    ); 
    this.zone.runOutsideAngular(() => { 
     Observable 
     .fromEvent(this.el.nativeElement, 'mousemove') 
     .map((e: MouseEvent) => { 
      //var offset = getOffset(this.el); 

      // subtract offset of the element 
      var o = this.el.nativeElement.getBoundingClientRect(); 

      return { 
      offsetX: e.clientX - o.left, 
      offsetY: e.clientY - o.top 
      }; 
     }) 
     .flatMap(delta => { 
      return Observable 
      .fromArray(this.letters 
       .map((val, index) => ({ 
       letter: val.text, 
       delta, 
       index 
       }))); 
     }) 
     .flatMap(letterConfig => { 
      return Observable 
      .timer((letterConfig.index + 1) * 100) 
      .map(() => ({ 
       text: letterConfig.letter, 
       top: letterConfig.delta.offsetY, 
       left: letterConfig.delta.offsetX + letterConfig.index * 20 + 20, 
       index: letterConfig.index 
      })); 
     }) 
     .subscribe(letterConfig => { 
      // to render the letters, put them back into app zone 
      this.zone.run(() => this.letters[letterConfig.index] = letterConfig); 
     }); 

    });//zone 
    } 
}