web-dev-qa-db-ja.com

Angular 2オブザーバブルによる変化の検出

私は通常、既存の質問を閲覧するだけで、自分が間違っていることを見つけることができますが、ここでは何も役に立ちませんでした。

NeDBストアのコンテンツを一覧表示して更新しようとする単純なNg2モジュールを使用しています。

念のために言っておきますが、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>

したがって、オブザーバブルへの手動サブスクリプションは機能し、値を取得します。しかし、非同期パイプはそれらを取得しません。

私はここで何か間違ったことをしていますか、それともこれはバグですか?


[〜#〜] edits [〜#〜]

2016年12月19日15:45

ngForディレクティブは以前は「アイテムのアイテムをlet | async」でしたが、非同期パイプはアイテムにスコープされており、監視できないと思ったので、角かっこを追加しましたが、結果は変わりませんでした。これは問題には関係ありません。

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領域外でそれらを実行していたことを意味します。

それらをAngular NgZone呼び出しで土壌に戻すことは、この問題を修正する方法でした。

@oslnに感謝します。

6
FacelessPanda

Item-object before 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));
_

(追加呼び出しの場合も)

9
olsn