【问题标题】:Angular 2 - Interpolation and binding with async http requestAngular 2 - 使用异步 http 请求进行插值和绑定
【发布时间】:2016-05-03 07:34:39
【问题描述】:

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

渲染模板时出现错误,因为“模型”尚未设置。

我用这个非常丑陋的 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();
        }     
    }
}

我的问题是:如何推迟模板渲染直到我的 http 调用完成,或者如何直接在模板中插入“模型”值而不绑定到另一个组件?

谢谢!

【问题讨论】:

    标签: asynchronous binding httprequest angular string-interpolation


    【解决方案1】:

    如果您从服务器返回一个对象,您可以在模板中使用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 以了解工作中的笨拙者。

    【讨论】:

    • 谢谢马克!猫王成功了!但我还不明白渲染流程。每次组件属性发生变化时都会渲染模板?
    • @TonyAlexanderHild,在 Angular 更改检测期间(在每个事件之后运行),默认情况下,您的所有视图/模板绑定都经过脏检查,这意味着检查它们是否有更改。当数据从服务器返回时,这是一个事件,因此运行更改检测。 model.Name 被脏检查并发现已更改,因此 Angular 更新了 DOM。 Angular 将控制权返回给浏览器后,它会看到 DOM 发生变化并更新我们在屏幕上看到的内容。
    【解决方案2】:

    本次讨论列出了一些策略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();其中 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
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-24
      • 1970-01-01
      • 2017-01-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多