带有可观察对象的Angular 2变化检测 [英] Angular 2 change detection with observables

查看:74
本文介绍了带有可观察对象的Angular 2变化检测的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我通常只浏览现有问题就可以找到自己在做错什么,但是在这里,没有任何帮助.

I usually manage to find what I'm doing wrong just browsing existing questions, but here, nothing has helped.

我正在使用一个简单的Ng2模块,该模块尝试列出和更新NeDB存储的内容.

I'm working with a simple Ng2 module that attempts to list and update the contents of a NeDB store.

请记住,我的NeDB存储没有问题,我已确认它已正确更新,并且最初已正确加载,所以我的问题出在其他地方.

Mind you, I have no issues with the NeDB store, I have confirmed that it gets updated correctly, and correctly loaded initially, so the problems I have lie elsewhere.

我遇到的问题如下:

异步管道不起作用".

"the async pipe doesn't work".


我有这个模块.


I have this module.

@NgModule({
    imports: [CommonModule],
    exports: [],
    declarations: [WikiComponent],
    providers: [WikiDbService],
})
export class WikiModule { }

我有这个组成部分.

@Component({
    selector: 'wiki',
    templateUrl: './wiki.component.html'
})
export class WikiComponent implements OnInit {

    items: Observable<WikiItem[]>;

    constructor(private _db : WikiDbService) { }

    ngOnInit() {
        this.items = this._db.items;
        this.items.subscribe({
            next: x => console.log("got value", x),
            error: e => console.error("observable error", e),
            complete: () => console.log("done")
        });
    }
}

我有这个模板.

<p>{{items | async | json}}</p>
<ul>
    <li *ngFor="let item of (items | async)">{{item.name}}</li>
</ul>
<input #newName (keyup)="0">
<button (click)="_db.addByName(newName.value)">ADD</button>

我有这项服务.

@Injectable()
export class WikiDbService {
    private sub: BehaviorSubject<WikiItem[]> = new BehaviorSubject<WikiItem[]>([]);
    private db: DataStore;
    public items: Observable<WikiItem[]> = this.sub.asObservable();
    constructor() {
        console.log("BehaviorSubject", this.sub);
        console.log("Observable", this.items);
        this.db = new DataStore(
            { 
                filename: path.join(app.getAppPath(),"wiki.db"),
                autoload: true,
                onload:
                (err)=>{
                    if(!err) {
                        this.db.find<WikiItem>({},
                        (e,docs) => {
                            if(!e) {
                                this.sub.next(docs);
                            }
                        })
                    }
                }
            });
    }

    public add(v: WikiItem) {
        this.db.insert(
            v,
            (e, nDoc) =>
            {
                if(!e) {
                    this.sub.next([...this.sub.getValue(),nDoc]);
                }
            }
        )
    }
    public addByName(str:string) {
        this.add({name: str, _id: undefined});
    }
}


当使用非空的持久性存储路由到我的组件时,我得到以下控制台日志(与该组件的OnInit方法中的记录相对应):


When routing to my component with a non-empty persistent store I get the following console log (corresponding to the logging in the OnInit method of the component):

got value > [] (wiki.component.ts:20)
got value > [Object, Object, Object, Object] (wiki.component.ts:20)

但是我的DOM保持不变:

However my DOM stays as this:

<wiki>
    <p>[]</p>
    <ul>
        <!--template bindings={
          "ng-reflect-ng-for-of": ""
        }-->
    </ul>
    <input>
    <button>ADD</button>
</wiki>


因此,手动订阅我的可观察对象确实有效,并获得了我的价值.但是异步管道无法获取它们.


So a manual subscription to my observable does work and gets me the values. But the async pipe doesn't get them.

我在这里做错什么了吗,或者这是一个错误吗?

Am I doing something wrong here, or is this a bug?

编辑

16/12/19下午3:45

ngFor指令之前是让项目|异步",我认为异步管道的作用域是该项目,而不是我的观察范围,因此我添加了方括号,但结果保持不变.这与问题无关.

The ngFor directive was "let item of items | async" before, and I thought maybe the async pipe was scoped to the item and not my observable so I added brackets, but results were unchanged. This is not relevant for the issue.

16/12/20 3.06pm

按照@olsn的建议,使用自动日志初始化组件的items属性,以检查模板是否订阅了Observable.

As per @olsn's advice, Initialized the component's items property with an auto-log, to check if the template subscribed to the Observable.

是的.因此,我想这取决于检测变化.修改标题.

It does. So it comes down to detecting the changes, I guess. Amending the title.

添加以下信息: 我的组件现在就这样(注释更改)

Adding this bit of information : My Component is now as such (commented changes)

@Component({
    selector: 'wiki',
    templateUrl: './wiki.component.html',
    changeDetection: ChangeDetectionStrategy.OnPush // <=== I've read this might help. It doesn't.
})
export class WikiComponent implements OnInit {

    items: Observable<WikiItem[]> = this._db.items //
        .do(x => console.log("got value", x))      // <== new initialization, with a stream
        .publishReplay().refCount();               //

    constructor(private _db : WikiDbService, private _cd: ChangeDetectorRef) { }

    ngOnInit() {
                      // <=== moved items initialization
    }

    reload() : void {
        this._cd.markForCheck(); // <== added a button to force the change detector to react. Does not do anything.
    }
}

此添加内容在模板中:

<button (click)="reload()">REFRESH</button>


解决方案

@osln给出了正确的答案.


SOLUTION

@osln gave a correct answer.

问题根本不是关于订阅或检测更改,这是因为我的sub.next调用是在给外部库的回调中进行的,具体而言,这意味着我是在Angular领域之外进行的.

The problem wasn't fundamentally about subscription or detecting changes, it was because my sub.next call were in callbacks given to an external library, which concretely meant that I was doing them outside of Angular territory.

通过NgZone调用将其强制返回Angular土壤是解决此问题的方法.

Forcing them back onto Angular soil with NgZone calls was the way to fix this issue.

感谢@osln.

推荐答案

尝试在ngInit 之前初始化您的项目对象,然后将临时日志直接添加到流中,这样您就可以知道模板是否真正订阅了该流,因为您当前的日志是在完全独立的流上完成的.

Try to initialize your item-object before ngInit and add a temporary log directly into the stream, that way you know if the template really subscribes to the stream, because your current log is done on a completely separate stream.

@Component({
    selector: 'wiki',
    templateUrl: './wiki.component.html'
})
export class WikiComponent implements OnInit {

    items: Observable<WikiItem[]> = this._db.items
        .do(x => console.log("got value", x)
        // if items is not a Behavior- or ReplaySubject or ReplayObservable, also add the following:
        .publishReplay()
        .refCount(); 

    constructor(private _db : WikiDbService) { }

    ngOnInit() {
        // ..nothing to do here
    }
}


此外,您可能会尝试将数据检索包装在NgZone.run中:

首先将其注入到DbService中:private ngZone: NgZone(来自@angular/core),然后不只是使用this.sub.next(docs);,而使用:

First inject this in your DbService: private ngZone: NgZone (from @angular/core) and then instead of just using this.sub.next(docs);, use:

this.ngZone.run(() => this.sub.next(docs));

(也用于添加呼叫)

这篇关于带有可观察对象的Angular 2变化检测的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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