Angular 2 异步管道不会自动渲染/更新 Observable 数据 [英] Angular 2 async pipe not rendering/updating Observable data automatically

查看:26
本文介绍了Angular 2 异步管道不会自动渲染/更新 Observable 数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了 Angular2 路由器和异步管道的问题.

我正在尝试呈现 RxJs Observable,但数据不会自动呈现.

必须单击要呈现数据的路线的链接.

这是根应用程序:

import {bootstrap} from 'angular2/platform/browser';从 'angular2/http' 导入 {HTTP_PROVIDERS};从'angular2/router'导入{ROUTER_PROVIDERS};从'./app.component.ts'导入{AppComponent};bootstrap(AppComponent, [HTTP_PROVIDERS, ROUTER_PROVIDERS]);

这是根组件:

import {Component} from 'angular2/core';从 'angular2/router' 导入 {RouteConfig, ROUTER_DIRECTIVES};从'./app.first-component.ts'导入{FirstComponent};从'./app.second-component.ts'导入{SecondComponent};从./app.services.ts"导入{AppService};@成分({选择器:'我的应用',提供者:[AppService,FirstComponent,SecondComponent],指令:[FirstComponent,SecondComponent,ROUTER_DIRECTIVES],模板:`<h1>Angular 2 App</h1><a [routerLink]="['First']">first-default</a><a [routerLink]="['Second']">second</a><路由器插座></路由器插座>`})@RouteConfig([{path: '/', name: 'First', 组件: FirstComponent, useAsDefault: true},{路径:'/second',名称:'Second',组件:SecondComponent}])导出类 AppComponent {}

这是第一个组件:

import {Component} from "angular2/core";从./app.services.ts"导入{AppService};导入rxjs/Rx";@成分({选择器:'我的第一个',模板:`<div><ul><li *ngFor="#s of appService.someObservable$ | async">一个字符串:{{ s }}

`})导出类 FirstComponent {构造函数(私有应用服务:应用服务){console.log('constructor', 'first');}}

最后是服务(数据所在的位置):

从angular2/core"导入{Injectable};从rxjs/Rx"导入{Observable};@Injectable()导出类 AppService {构造函数(){console.log('constructor', 'appService');this.constructSomeObservable();}someObservable$:Observable ;构造一些Observable(){this.someObservable$ = Observable.create(observer => {const eventSource = new EventSource('/interval-sse-observable');eventSource.onmessage = x =>观察者.next(JSON.parse(x.data));eventSource.onerror = x =>观察者.错误(console.log('事件源失败'));返回 () =>{eventSource.close();};}).从...开始([]).scan((acc, value) => acc.concat(value));}}

路由器或管道有什么问题?

查看 github 上的示例项目此处.

编辑:这是组件的修改版本:

import {Component} from "angular2/core";从./app.services.ts"导入{AppService};从rxjs/Rx"导入{Observable};@成分({选择器:'我的第一个',模板:`<div><ul><li *ngFor="#s of someObservable$ | async">一个字符串:{{ s }}

`})导出类 FirstComponent {someObservable$:Observable ;构造函数(私有应用服务:应用服务){console.log('constructor', 'first');this.someObservable$ = appService.someObservable$;}}

模板中的数据未更新.是否与双向/单向绑定有关?

解决方案

我认为 angular zone 不会修补从 eventSource.onmessage 发出的事件,不像例如setTimeout、SetInterval 或 xhr 请求

来自 angular2-polyfills.js

/***/function(module, export, __webpack_require__) {/* WEBPACK VAR 注入 */(function(global) {"use strict";__webpack_require__(1);var event_target_1 = __webpack_require__(2);vardefine_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 blocksMethods = ['alert', 'prompt', 'confirm'];var _global = typeof window == 'undefined' ?全球:窗口;patchTimer(_global, set, clear, '超时');patchTimer(_global, set, clear, 'Interval');patchTimer(_global, set, clear, '立即');patchTimer(_global, 'request', 'cancelMacroTask', 'AnimationFrame');patchTimer(_global, 'mozRequest', 'mozCancel', 'AnimationFrame');patchTimer(_global, 'webkitRequest', 'webkitCancel', 'AnimationFrame');for (var i = 0; i 

因此,您需要为 eventsource.onmessage 包装回调,例如:

app.services.ts

import {Injectable, NgZone} from "angular2/core";//<=== 1) 不要忘记导入 NgZone 类从rxjs/Rx"导入{Observable};@Injectable()导出类 AppService {constructor(private zone: NgZone) {//<== 2) 不要忘记在构造函数中注入 zoneconsole.log('constructor', 'appService');this.constructSomeObservable();}someObservable$: Observable;构造一些Observable(){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) 包装 onmessage 事件eventSource.onerror = x =>观察者.错误(console.log('事件源失败'));返回 () =>{eventSource.close();};}).从...开始([]).scan((acc, value) => acc.concat(value));}}

I am facing an issue with the Angular2 router and an async pipe.

I am trying to render an RxJs Observable and the data does not render automatically.

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.

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 e.g. setTimeout, SetInterval or xhr request

From 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:

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

这篇关于Angular 2 异步管道不会自动渲染/更新 Observable 数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆