11

Jestem nowy w Angular 2 i mam problem z asynchronicznym żądaniem http i powiązaniem z interpolacją.Angular 2 - Interpolacja i wiązanie z asynchronicznym żądaniem http

Oto mój komponent:

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

Gdy szablon jest renderowane pojawia się błąd, ponieważ „model” nie jest jeszcze ustalone.

I rozwiązać problem z tym bardzo brzydkim 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(); 
     }  
    } 
} 

Moje pytanie brzmi: jak odroczyć renderowanie szablonu aż moje wezwanie http została zakończona lub jak interpolować wartości „model” bezpośrednio w szablonie bez wiązania do innego komponentu?

Dzięki!

Odpowiedz

12

przypadku zwrotu przedmiotu z serwera, można użyć safe navigation (previously "Elvis") operator (?.) w szablonie:

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

Zobacz this answer dla działającego plunkera.

+1

Dzięki Mark! Elvis załatwił sprawę! Ale nie rozumiem jeszcze przepływu renderowania. Szablon jest renderowany za każdym razem, gdy następuje zmiana we właściwościach komponentu? –

+2

@ TonyAlexanderHild, podczas wykrywania zmiany kąta (która działa po każdym zdarzeniu), domyślnie wszystkie powiązania widoku/szablonu są brudne, co oznacza, że ​​są sprawdzane pod kątem zmian. Gdy dane zwracają się z serwera, jest to zdarzenie, więc przełącz się na wykrywanie zmian. 'model.Nazwa' jest brudny sprawdzony i stwierdzono, że został zmieniony, więc Angular aktualizuje DOM. Po Angular zwraca kontrolę do przeglądarki, widzi zmiany DOM i aktualizuje to, co widzimy na ekranie. –

+0

Dzięki @MarkRajcok! Teraz jest jasne. –

0

Ta dyskusja wymienia kilka strategii https://github.com/angular/angular/issues/6674#issuecomment-174699245

pytanie ma wiele konsekwencji. W niektórych przypadkach obserwowalne powinny być zarządzane poza ramami.

A. Można skanować wszystkie obserwable przed bootstrap

B. Bootstrap następnie zeskanować wszystkie obserwable przed przekazaniem obiektu do elementu górnego poziomu.

C. Możesz także zmienić metodę changeDetection w komponencie, aby wyzwalać zmiany wejść lub ręcznie wywoływać detektor zmian.

D. Jeśli używasz | async, to powinno być używane tylko na najwyższym poziomie, jeśli nie masz ochoty używać .subscribe, ale naprawdę powinieneś po prostu użyć .subscribe.

E. Jeśli używasz | async wszędzie, to co naprawdę robisz, odwraca kontrolę nad renderingiem do obserwowalnych, co oznacza, że ​​wróciliśmy do Angular1 dni kaskadowych zmian, więc albo musisz zrobić C, D, B lub A

ChangeDetectionStrategy nie wydaje się działać w tej chwili. Najprościej ustawić komponent jako Wolnostojący.

Możemy również użyć haka do trybu życia ngOnInit w celu usunięcia komponentu z drzewa detekcji zmian. Będziesz musiał uruchomić this.ref.detach(); gdzie ref jest wstrzykiwany poprzez 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

Można również nie należą zone.js i ręcznie kontrolować wszystkie zmiany. Możesz także wstrzyknąć NgZone, aby uruchomić operację poza strefą.js, więc nie będzie ona określać kątowo, aby wyzwalać chane. Na przykład,

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