{{model.N'/> {{model.N'/> Angular 2 - 使用异步http请求进行插值和绑定 | 所有编程讨论 | zhouni.net

问题 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调用完成或如何直接在模板中插入“模型”值而不绑定到另一个组件?

谢谢!


9453
2018-01-26 16:33


起源



答案:


如果要从服务器返回对象,则可以使用 安全导航(以前称为“猫王”)操作员 (?。)在你的模板中:

@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。


12
2018-01-26 17:50



谢谢马克!猫王做了伎俩!但我还不了解渲染流程。每次组件属性发生任何变化时都会渲染模板? - Tony Alexander Hild
@TonyAlexanderHild,在角度变化检测期间(在每个事件之后运行),默认情况下,所有视图/模板绑定都被脏检查,这意味着它们会被检查是否有变化。当数据从服务器返回时,这是一个事件,因此更改检测运行。 model.Name 脏了检查并发现已更改,因此Angular更新DOM。在Angular将控制权返回给浏览器之后,它会看到DOM更改并更新我们在屏幕上看到的内容。 - Mark Rajcok
谢谢@MarkRajcok!现在很清楚了。 - Tony Alexander Hild


答案:


如果要从服务器返回对象,则可以使用 安全导航(以前称为“猫王”)操作员 (?。)在你的模板中:

@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。


12
2018-01-26 17:50



谢谢马克!猫王做了伎俩!但我还不了解渲染流程。每次组件属性发生任何变化时都会渲染模板? - Tony Alexander Hild
@TonyAlexanderHild,在角度变化检测期间(在每个事件之后运行),默认情况下,所有视图/模板绑定都被脏检查,这意味着它们会被检查是否有变化。当数据从服务器返回时,这是一个事件,因此更改检测运行。 model.Name 脏了检查并发现已更改,因此Angular更新DOM。在Angular将控制权返回给浏览器之后,它会看到DOM更改并更新我们在屏幕上看到的内容。 - Mark Rajcok
谢谢@MarkRajcok!现在很清楚了。 - Tony Alexander Hild


本讨论列出了一些策略 https://github.com/angular/angular/issues/6674#issuecomment-174699245

这个问题有很多含义。在某些情况下,可以在框架之外管理可观察量。

答:您可以在引导之前扫描所有可观察对象

B.然后在将对象传递给顶级组件之前扫描所有可观察对象。

C.您还可以将组件中的changeDetection更改为在输入更改时触发或手动触发更改检测器。

D.如果您正在使用| async,那么如果您不想使用.subscribe,它应该只在顶层使用,但您真的应该使用.subscribe。

E.如果你在任何地方使用| async,那么你真正在做的是将对渲染的控制权转换为可观察对象,这意味着我们回到了Angular1级联变化的日子,所以你要么做C,D,B,或者A.

ChangeDetectionStrategy 目前似乎没有工作。您可以将组件简单地设置为Detached。

我们还可以使用ngOnInit生命周期钩子从更改检测树中删除组件。你需要运行this.ref.detach();其中ref通过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之外运行操作,这样它就不会告诉angular触发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
  }
}

0
2018-01-26 16:40