Angular 2 变化检测与 observables

标签 angular asynchronous typescript rxjs observable

我通常只是浏览现有的问题就能找到我做错了什么,但在这里,没有任何帮助。

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

请注意,我对 NeDB 商店没有任何问题,我已确认它已正确更新,并且最初已正确加载,所以我的问题出在其他地方。

我遇到的问题如下:

"the async pipe doesn't work".


我有这个模块。

@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 方法中的日志记录):

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

但是我的 DOM 保持不变:

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

因此,手动订阅我的 observable 确实有效并为我获取了值。但是异步管道没有得到它们。

我是不是做错了什么,或者这是一个错误?


编辑

2016 年 12 月 19 日下午 3:45

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.

2016 年 12 月 20 日下午 3 点 06 分

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

确实如此。所以归结为检测变化,我猜。修改标题。

添加这些信息: 我的组件现在是这样的(评论更改)

@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 给出了正确的答案。

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

用 NgZone 调用迫使他们回到 Angular 土壤是解决这个问题的方法。

感谢@osln。

最佳答案

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

@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 中:

首先将其注入(inject)您的 DbService:private ngZone: NgZone(来自 @angular/core),然后不只是使用 this.sub.next(文档);,使用:

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

(也用于添加调用)

关于Angular 2 变化检测与 observables,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41224671/

相关文章:

angular - Firebase Angularfire2 检查用户是否存在于数据库中

node.js - 声明导出函数的 JS 依赖项的 .d.ts 文件

spring - Grails 持久化 Spring 的 "Async"带注释的方法不起作用

javascript - 如何阻止连接提前关闭?

intellij-idea - 在 WebStorm/IntelliJ 中使用 lodash 定义 stub 时,TypeScript 导入会阻止智能感知/自动完成

javascript - Typescript 如何推断泛型以及为什么它在一种情况下不起作用?

angular - 如何从 Ionic 动态选择选项中获取 ID 值和 NAME 值

jquery - 使用带 angular4 的灯光 slider

javascript - 导入自定义模块时获取 'Cannot read property ' ɵmod' of undefined' [Angular 11]

c# - 选择不在 ASP.NET Core Web API Controller 中等待异步函数运行