11

Я новичок в Angular 2 и у меня возникла проблема с асинхронным HTTP-запросом и привязкой к интерполяции.Угловая 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()); 
    } 
} 

См this answer для рабочего plunker.

+1

Thanks Mark! Элвис сделал трюк! Но я пока не понимаю поток рендеринга. Шаблон визуализируется каждый раз, когда есть какие-либо изменения в свойствах компонента? –

+2

@TonyAlexanderHild, при обнаружении углового изменения (который запускается после каждого события) по умолчанию все ваши привязки к просмотру/шаблону грязно проверены, то есть они проверяются на изменения. Когда данные возвращаются с сервера, это событие, поэтому выполняется проверка обнаружения изменений. 'model.Name' грязно проверено и обнаружено, что оно изменилось, поэтому Angular обновляет DOM. После того, как Angular возвращает управление браузеру, он видит изменения DOM и обновляет то, что мы видим на экране. –

+0

Спасибо @MarkRajcok! Теперь ясно. –

0

Это обсуждение перечисляет несколько стратегий https://github.com/angular/angular/issues/6674#issuecomment-174699245

вопрос имеет много последствий. В некоторых случаях наблюдаемые должны управляться вне рамок.

A. Вы можете сканировать все наблюдаемые, прежде чем самонастройки

B. Bootstrap отсканировать все наблюдаемые перед передачей объекта в компонент верхнего уровня.

C. Вы также можете изменить changeDetection в компоненте, чтобы либо запускать, когда входы меняются, либо вручную запускают их.

D. Если вы используете | async, то его следует использовать только на верхнем уровне, если вам не нравится использовать .subscribe, но вы действительно должны просто использовать .subscribe.

E. Если вы используете | async везде, то то, что вы действительно делаете, - это инвертирование контроля над визуализацией на наблюдаемые, что означает, что мы вернулись в Angular1 дней каскадных изменений, так что вам либо нужно делать C, D, B или A

ChangeDetectionStrategy похоже не работает в данный момент. Вы бы просто установили компонент как отдельный.

Мы также можем использовать крючок жизненного цикла 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 для запуска операции вне зоны.js, чтобы он не указывал угловое значение для запуска шайб. Например,

// 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 
    } 
} 
Смежные вопросы