带有可观察对象的Angular 2变化检测 [英] Angular 2 change detection with observables
问题描述
我通常只浏览现有问题就可以找到自己在做错什么,但是在这里,没有任何帮助.
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屋!