1

私のビューモデルは次のとおりで、knockout.js を使用してページにバインドされています。エピソードはエンティティの配列であり、それぞれがページのチェックボックスにバインドされたブール型プロパティ「ListenedTo」を持っています。

function episodesViewModel() {

    this.episodes = ko.observableArray();

    this.sync = function () {
        repository.populateEpisodes(this);
    }

    this.save = function () {
        repository.saveChanges();
    }

    //this.episodes.subscribe(function (episodes) {
    //    ko.utils.arrayForEach(episodes, function (episode) {
    //        episode.ListenedTo.subscribe(function () {
    //            window.repository.saveChanges();
    //        });
    //    });
    //});

    // perform an initial sync
    this.sync();   
}

リポジトリ オブジェクトは、基になる Breeze 呼び出しの単なるラッパーです。

ボタンのクリックに save() をバインドすると、すべてが期待どおりに機能し、そよ風は変更があることを確認し、サーバーにコールバックします。ListenedTo 変更へのサブスクリプションを作成する行のコメントを外すと (そしてボタンをクリックする必要なく保存する)、奇妙なことが起こります。

ページが読み込まれてデータが入力された後、チェックボックスを最初にクリックすると、期待どおりに Breeze saveChanges() が自動的に呼び出されます。ビューモデルの値が期待どおりで、チェックボックスと一致していても、そよ風の hasChanges() は false を返し、サーバー呼び出しは行われません。もう一度クリックすると、hasChanges が true になり、サーバー呼び出しが行われます!? Breeze が遅れており、ビューモデルのエンティティへの変更を登録していないようです。

何か案は?

4

2 に答える 2

4

なんてこった。あなたのコードに大きな問題があります!詳細を掘り下げる前に、ページから飛び出すのは、episodes配列が変更されるたびに、配列内のすべてのエピソードにサブスクリプションを繰り返し追加する可能性があることです。うわぁ!

イベント伝播の遅延

私の不安について説明する前に、リスナーが発火したときと の値との間に矛盾があるというあなたの具体的な経験を認めたいと思いますmanager.hasChanges。それは本当です。KO サブスクリプションは、 が変更について聞く前に発火しEntityManagerています ... これが false を報告する理由です。マネージャーが聞くには、変更のニュースはまだ届いていません。

私もこれに気づきました。実際、TodoSampleviewModel.jssetTimeoutを見ると、 Breeze に変更を聞く機会を与えるために を追加することで、この問題を回避していることがわかります。

// Breeze PropertyChanged イベントで変更をリッスンします
item.entityAspect.propertyChanged.subscribe(function () {
    if (suspendItemSave) { return; }
    // EntityManager に変更を聞く時間を与える
    setTimeout(saveIfModified, 0);

    関数 saveIfModified() {
        if (item.entityAspect.entityState.isModified()) {
                dataservice.saveChanges();
        }
    }
});

別の方法を知りたいです。これは、KO と Breeze の間のタイミングの問題です。私たちはまだそれを巧みに処理する方法を考え出していません. できるかどうかわかりません。

モデルルールまたはビュールール?

バックアップしましょう。どのビューに表示されていても、プロパティが変更episodeされたときに保存することになっていますか? 答えが「はい」の場合、モデルに本当に属しているアプリケーション ビジネス ルールがあり、エピソードの変更をリッスンする何かが本当に必要です。listenedTo

しかし、答えが「いいえ」の場合...保存がオブジェクトの変更ではなく「チェックボックス」によってトリガーされる場合、これはUIルールです...ビュールール...そしてKOを使用する必要がありますエピソードプロパティではなく、チェックボックスをリッスンします。

エンティティが変更されたときの保存

最初のケースで、アプリケーション ルールが次のようであると断言listenedToしましょう。

TodoSampleで、エンティティのプロパティの 1 つ (プロパティのいずれか) の変更を直接リッスンする方法を示したことは知っています。これは強力で直感的です。

それが最も安全な方法ではないと信じるようになりました。同じエンティティを共有する複数の ViewModel を持つアプリでメモリ リークが発生する可能性があります。この「Todo」アプリは画面が1つしかないので、そんな心配はありません。しかし、より大きなアプリでは...気になります。

したがって、プロパティへの変更をリッスンしないことをお勧めしEpisodeます。代わりに、EntityManager! 最近リリースされたBreeze SPA テンプレートのdatacontext.js からのこのスニペットを見てください。

関数 configureManagerToSaveModifiedItemImmediately() {
    manager.entityChanged.subscribe(entityStateChanged);
    
    関数 entityStateChanged(args) {
        if (args.entityAction ===そよ風.EntityAction.EntityStateChange) {
            var エンティティ = args.entity;
            if (entity.entityAspect.entityState.isModified()) {
                saveEntity(エンティティ);
            }
        }
    }
}   

EntityManager がキャッシュするエンティティの状態変化をリッスンする方法に注意してください。「変更済み」状態への遷移のみに関心があります。検出されると、エンティティが保存されます。

これは広すぎるかもしれません。ただし、アプリケーションの特定のニーズに合った追加のフィルタリング ロジックを登録することは想像できます。

セーブキューイング

自動保存に関する新しい問題。この問題は、保存をトリガーする方法に関係なく重要です。

ユーザーはかなり速くクリックできます。彼女は、処理できるよりも速く保存要求をトリガーすることになります。Breeze (デフォルト) はEntityManager、保留中の保存操作からサーバーが結果を返すのを待っている間、再度保存することはできません。例外がスローされます。

保存が成功したかどうかがわかるまで、エンティティの状態を変更済みから未変更に変更できないため、待機する必要があります。保存に失敗した場合、エンティティを「保存されていない」状態のままにしておく必要があります。

のdatacontext.jsの上部を見てくださいmanager.enableSaveQueuing(true)。これはBreeze 固有の機能ではありません。これはプラグインScripts/breeze.savequeuing.jsの機能 です。このプラグインをロードする必要があります。Breeze SPA テンプレートの記事でそれについて読んでください。

なぜそんなに複雑なのか

この Breeze のことは非常に複雑だと思うかもしれません。実際、複雑化を導入しているのは Breeze ではありません。複雑さを追加しているエンティティの状態が変化したときに保存をトリガーするのはあなたの望みです。

そうするのが間違っていると言っているのではありません。このアプローチは、実装に注意を払っている Breeze によって提供される機会であると言っています。

Breeze がなければ、エンティティの状態を追跡するのに時間がかかります。したがって、本当に安全な唯一のオプションは、チェックボックスへの変更に基づいて保存をトリガーすることです...この場合、チェックボックスへの KO バインディングよりも複雑ではありません。

setTimeoutまあ...わかりました...あなたがつまずいたBreeze / KOのタイミングの問題により、ギャンビットが必要になるため、もう少し複雑です。しかし、あなたが私の主張を理解してくれることを願っています。

于 2013-03-04T03:29:43.843 に答える
0

詳細情報がないとわかりにくい (たとえば、他のどのフレームワークが使用されているか、VM インスタンス化コードをトリガーするものは何か、KO.ApplyBind はどこにあるのか) VM の作成時にビューが適切に構築されているか?

私は現在、最初に VM を作成し、次にビューを作成する Durandal を使用しています。Durandal の activate イベント中 (またはそれ以前のインスタンス化コードなど) に追加のサブスクリプションを作成しようとすると、無効であることが判明したため、KO ビュー/viewModel サブスクリプションが破棄されました。Durandal の後の viewAttached イベントでサブスクリプションを作成する必要がありました

サブスクライブをワークフローの後半に入れてみてください。

于 2013-03-04T02:48:50.410 に答える