11

Ich bin neu in Angular 2 und ich habe ein Problem mit asynchronen http Anfrage und Bindung mit Interpolation.Angular 2 - Interpolation und Bindung mit asynchronen HTTP-Anfrage

Hier ist meine Komponente:

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

Wenn die Vorlage gemacht wird, ich erhalte eine Fehlermeldung, weil „Modell“ ist noch nicht festgelegt.

löste ich das Problem mit diesem sehr hässliche Hack:

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

Meine Frage ist: wie die Vorlage Rendering aufzuschieben, bis mein http Anruf beendet ist oder wie die „Modell“ Werte direkt in Vorlage einfügen, ohne Bindung zu einer anderen Komponente?

Danke!

Antwort

12

Wenn Sie ein Objekt vom Server zurückkommen, können Sie mit dem safe navigation (previously "Elvis") operator (?.in der Vorlage):

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

Siehe this answer für eine Arbeits Plunker.

+1

Danke! Der Elvis hat es geschafft! Aber ich verstehe noch nicht den Renderfluss. Die Vorlage wird jedes Mal gerendert, wenn sich die Komponenteneigenschaften ändern? –

+2

@TonyAlexanderHild, während der Erfassung der Winkeländerung (die nach jedem Ereignis ausgeführt wird), werden standardmäßig alle Ihre Ansichts-/Vorlagenbindungen fehlerhaft überprüft, dh sie werden auf Änderungen überprüft. Wenn Daten vom Server zurückgegeben werden, handelt es sich um ein Ereignis, sodass die Änderungserkennung ausgeführt wird. 'model.Name' ist dreckig markiert und hat sich geändert, also aktualisiert Angular das DOM. Nachdem Angular die Kontrolle an den Browser zurückgegeben hat, sieht es, dass sich das DOM ändert und aktualisiert, was wir auf dem Bildschirm sehen. –

+0

Danke @MarkRajcok! Es ist jetzt klar. –

0

Diese Diskussion listet einige Strategien https://github.com/angular/angular/issues/6674#issuecomment-174699245

Die Frage viele Auswirkungen hat. In einigen Fällen sollten die Observablen außerhalb des Rahmens verwaltet werden.

A. Sie können alle Observablen scannen, bevor Sie

B. Bootstrap Bootstrap dann alle Observablen scannen, bevor das Objekt der obersten Ebene Komponente übergeben.

C. Sie können die changeDetection auch innerhalb der Komponente ändern, um entweder auszulösen, wenn sich Eingänge ändern, oder sie manuell zu aktivieren.

D. Wenn Sie | async verwenden, sollte es nur auf oberster Ebene verwendet werden, wenn Sie keine .subscribe verwenden möchten, aber Sie sollten einfach .subscribe verwenden.

E. Wenn Sie überall | Async verwenden, dann invertieren Sie die Kontrolle über das Rendering auf die Observablen, was bedeutet, dass wir wieder in den Angular1 Tagen kaskadierender Änderungen sind, so dass Sie entweder C, D, B oder A

ChangeDetectionStrategy scheint im Moment nicht zu funktionieren. Sie würden die Komponente einfach als getrennt festlegen.

Wir können auch ngOnInit lifecycle hook verwenden, um die Komponente aus dem Change Detection Tree zu entfernen. Sie müssten dies ausführen.ref.detach(); wo ref über ChangeDetectorRef injiziert wird

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

können Sie auch nicht zone.js schließen und alle Änderungen manuell steuern. Sie können NgZone auch injizieren, um eine Operation außerhalb von zone.js auszuführen, sodass angular nicht angezeigt wird, um Channes auszulösen. Zum Beispiel

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