11

Sono nuovo in Angular 2 e sto affrontando un problema con richiesta HTTP asincrona e associazione con interpolazione.Angolare 2 - Interpolazione e associazione con richiesta http asincrona

Ecco la mia componente:

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

Quando il modello è reso ottengo un errore perché "modello" non è ancora impostato.

ho risolto il problema con questo molto brutto 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(); 
     }  
    } 
} 

La mia domanda è: come rinviare il rendering modello fino a quando la mia chiamata http è completato o come interpolare i valori di "modello" direttamente nel modello senza vincolante a un altro componente?

Grazie!

risposta

12

Se si restituisce un oggetto dal server, è possibile utilizzare il safe navigation (previously "Elvis") operator (?.) Nel modello:

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

Vedi this answer per un plunker lavoro.

+1

Grazie Mark! L'Elvis ha fatto il trucco! Ma non capisco ancora il flusso di rendering. Il modello viene sottoposto a rendering ogni volta che vengono apportate modifiche alle proprietà del componente? –

+2

@TonyAlexanderHild, durante il rilevamento delle modifiche angolari (che viene eseguito dopo ogni evento), per impostazione predefinita, tutte le associazioni di visualizzazione/modello sono sporche, il che significa che vengono controllate le modifiche. Quando i dati vengono restituiti dal server, si tratta di un evento, quindi viene eseguito il rilevamento delle modifiche. 'model.Name' è sporco controllato e trovato modificato, quindi Angular aggiorna il DOM. Dopo che Angular restituisce il controllo al browser, vede il DOM cambiare e aggiorna ciò che vediamo sullo schermo. –

+0

Grazie @MarkRajcok! Adesso è chiaro. –

0

Questa discussione elenca alcune strategie https://github.com/angular/angular/issues/6674#issuecomment-174699245

La questione ha molte implicazioni. In alcuni casi, gli osservabili dovrebbero essere gestiti al di fuori del quadro.

A. È possibile eseguire la scansione di tutte le osservabili prima di bootstrap

B. Bootstrap quindi la scansione di tutti osservabili prima di passare l'oggetto al componente di livello superiore.

C. È inoltre possibile modificare la modifica del rilevamento all'interno del componente per attivare quando gli ingressi cambiano o attivare manualmente il rilevatore di modifiche.

D. Se si utilizza | async, dovrebbe essere utilizzato solo al livello superiore se non si ha voglia di usare .subscribe ma si dovrebbe semplicemente usare .subscribe.

E. Se si utilizza Async dappertutto, ciò che si sta facendo in realtà è invertendo il controllo del rendering verso l'osservabile, il che significa che siamo di nuovo nei tempi Angular1 dei cambiamenti a cascata in modo che sia necessario fare C, D, B, o A

ChangeDetectionStrategy non sembra funzionare al momento. È semplice impostare il componente come Staccato.

Possiamo inoltre utilizzare il hook del ciclo di vita ngOnInit per rimuovere il componente dall'albero di rilevamento delle modifiche. Dovresti eseguire this.ref.detach(); dove ref viene iniettato attraverso 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

È possibile anche includere non zone.js e controllare manualmente tutte le modifiche. È inoltre possibile iniettare NgZone per eseguire un'operazione al di fuori di zone.js in modo che non sia detto angolare per attivare i canali. Ad esempio,

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