简体   繁体   English

Angular 2异步管道不会自动呈现/更新Observable数据

[英]Angular 2 async pipe not rendering/updating Observable data automatically

I am facing an issue with the Angular2 router and an async pipe. 我面临着Angular2路由器和异步管道的问题。

I am trying to render an RxJs Observable and the data does not render automatically. 我试图渲染一个RxJs Observable,数据不会自动渲染。

One has to click on the link for the route for the data to render. 必须单击链接以获取要呈现的数据的路径。

Here is the root app: 这是根应用程序:

import {bootstrap}    from 'angular2/platform/browser';
import {HTTP_PROVIDERS} from 'angular2/http';
import {ROUTER_PROVIDERS} from 'angular2/router';
import {AppComponent} from './app.component.ts';

bootstrap(AppComponent, [HTTP_PROVIDERS, ROUTER_PROVIDERS]);

Here is the root component: 这是根组件:

import {Component} from 'angular2/core';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {FirstComponent} from './app.first-component.ts';
import {SecondComponent} from './app.second-component.ts';
import {AppService} from "./app.services.ts";


@Component({
    selector: 'my-app',
    providers: [AppService, FirstComponent, SecondComponent],
    directives: [FirstComponent, SecondComponent, ROUTER_DIRECTIVES],
    template: `<h1>An Angular 2 App</h1>
               <a [routerLink]="['First']">first-default</a> 
               <a [routerLink]="['Second']">second</a> 
               <router-outlet></router-outlet>`
})
@RouteConfig([
    {path: '/', name: 'First', component: FirstComponent, useAsDefault: true},
    {path: '/second', name: 'Second', component: SecondComponent}
])
export class AppComponent {
}

Here is First component: 这是第一个组成部分:

import {Component} from "angular2/core";
import {AppService} from "./app.services.ts";
import "rxjs/Rx";


@Component({
    selector: 'my-first',
    template: `
<div>
    <ul>
        <li *ngFor="#s of appService.someObservable$ | async">
           a string: {{ s }}
        </li>
    </ul>
 </div>`
})
export class FirstComponent {

    constructor(private appService:AppService) {
        console.log('constructor', 'first');
    }
}

and finally the service (where the data resides): 最后是服务(数据所在的位置):

import {Injectable} from "angular2/core";
import {Observable} from "rxjs/Rx";

@Injectable()
export class AppService {

    constructor() {
        console.log('constructor', 'appService');
        this.constructSomeObservable();
    }

    someObservable$:Observable <string[]>;

    constructSomeObservable() {
        this.someObservable$ = Observable.create(observer => {
                const eventSource = new EventSource('/interval-sse-observable');
                eventSource.onmessage = x => observer.next(JSON.parse(x.data));
                eventSource.onerror = x => observer.error(console.log('EventSource failed'));
                return () => {
                    eventSource.close();
                };
            })
            .startWith([])
            .scan((acc, value) => acc.concat(value));
    }
}

What I am getting wrong with the router or the pipe? 我的路由器或管道出了什么问题?

See sample project on github here . 这里查看github上的示例项目。

edit : Here is the modified version of the component: 编辑 :这是组件的修改版本:

import {Component} from "angular2/core";
import {AppService} from "./app.services.ts";
import {Observable} from "rxjs/Rx";


@Component({
    selector: 'my-first',
    template: `
<div>
    <ul>
        <li *ngFor="#s of someObservable$ | async">
           a string: {{ s }}
        </li>
    </ul>
 </div>`
})
export class FirstComponent {

    someObservable$:Observable <string[]>;

    constructor(private appService:AppService) {
        console.log('constructor', 'first');
        this.someObservable$ = appService.someObservable$;
    }
}

The data is not updated in the template. 数据未在模板中更新。 Is it to do with two/one way binding? 它与两个/单向绑定有关吗?

I think angular zone doesn't patch events emitted from eventSource.onmessage unlike eg setTimeout, SetInterval or xhr request 我认为angular zone不会修复eventSource.onmessage发出的事件,例如setTimeout,SetInterval或xhr请求

From angular2-polyfills.js 来自angular2-polyfills.js

/***/ function(module, exports, __webpack_require__) {

    /* WEBPACK VAR INJECTION */(function(global) {"use strict";
    __webpack_require__(1);
    var event_target_1 = __webpack_require__(2);
    var define_property_1 = __webpack_require__(4);
    var register_element_1 = __webpack_require__(5);
    var property_descriptor_1 = __webpack_require__(6);
    var utils_1 = __webpack_require__(3);
    var set = 'set';
    var clear = 'clear';
    var blockingMethods = ['alert', 'prompt', 'confirm'];
    var _global = typeof window == 'undefined' ? global : window;
    patchTimer(_global, set, clear, 'Timeout');
    patchTimer(_global, set, clear, 'Interval');
    patchTimer(_global, set, clear, 'Immediate');
    patchTimer(_global, 'request', 'cancelMacroTask', 'AnimationFrame');
    patchTimer(_global, 'mozRequest', 'mozCancel', 'AnimationFrame');
    patchTimer(_global, 'webkitRequest', 'webkitCancel', 'AnimationFrame');
    for (var i = 0; i < blockingMethods.length; i++) {
        var name = blockingMethods[i];
        utils_1.patchMethod(_global, name, function (delegate, symbol, name) {
            return function (s, args) {
                return Zone.current.run(delegate, _global, args, name);
            };
        });
    }
    event_target_1.eventTargetPatch(_global);
    property_descriptor_1.propertyDescriptorPatch(_global);
    utils_1.patchClass('MutationObserver');
    utils_1.patchClass('WebKitMutationObserver');
    utils_1.patchClass('FileReader');
    define_property_1.propertyPatch();
    register_element_1.registerElementPatch(_global);
    // Treat XMLHTTPRequest as a macrotask.
    patchXHR(_global);
    var XHR_TASK = utils_1.zoneSymbol('xhrTask');
    function patchXHR(window) {
        function findPendingTask(target) {
            var pendingTask = target[XHR_TASK];
            return pendingTask;
        }

Therefore you need to wrap your callback for eventsource.onmessage something like: 因此,您需要为eventsource.onmessage包装回调类似于:

app.services.ts app.services.ts

import {Injectable, NgZone} from "angular2/core"; // <=== 1) Don't forget to import the NgZone class
import {Observable} from "rxjs/Rx";

@Injectable()
export class AppService {

  constructor(private zone: NgZone) { // <== 2) Don't forget also to inject zone in constructor
    console.log('constructor', 'appService');
    this.constructSomeObservable();
  }

  someObservable$: Observable<string[]>;

  constructSomeObservable() {
    this.someObservable$ = Observable.create(observer => {
      const eventSource = new EventSource('/interval-sse-observable');
      eventSource.onmessage = x => this.zone.run(() => observer.next(JSON.parse(x.data))); // <=== 3) Wrap onmessage event
      eventSource.onerror = x => observer.error(console.log('EventSource failed'));
      return () => {
        eventSource.close();
      };
    })
      .startWith([])
      .scan((acc, value) => acc.concat(value));
  }
}

I would reference the observable within the component itself: 我会在组件本身中引用observable:

@Component({
  selector: 'my-first',
  template: `
    <div>
      <ul>
        <li *ngFor="#s of someObservable$ | async">
           a string: {{ s }}
        </li>
      </ul>
   </div>`
})
export class FirstComponent {
  constructor(private appService:AppService) {
    console.log('constructor', 'first');
    this.someObservable$ = appService.someObservable;
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM