使用可观察量

时间:2016-12-19 14:22:58

标签: angular asynchronous typescript rxjs observable

我经常设法找到我在浏览现有问题时遇到的错误,但在这里,没有任何帮助。

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

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

我遇到的问题如下:

  

"异步管道不起作用"。

我有这个模块。

@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的手动订阅确实有效,并为我提供了价值观。但异步管道无法获得它们。

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

EDITS

12/19/16 3:45 pm

  

ngFor指令是&#34; let item of items |异步&#34;之前,我想也许异步管道的范围是项目,而不是我的观察,所以我添加了括号,但结果没有变化。这与此问题无关。

12/20/16 3.06pm

根据@ 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区域之外进行调用。 / p>

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

谢谢@osln。

1 个答案:

答案 0 :(得分:8)

尝试在 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

首先在您的DbService中注入此内容:private ngZone: NgZone(来自@angular/core),然后使用this.sub.next(docs);而不是仅使用this.ngZone.run(() => this.sub.next(docs));

merge

(也适用于add-call)