KnockoutJS:その場でリストをフィルタリングする

先週の土曜日、 ドネポルペトロフスクの土曜日にMVVMとKnockoutJSに関するレポートを読むことができました

この報告書は大衆に温かく受け取られ、多くの人が興味深い質問をしました。

レポート自体では公開されませんでした。

実際、私はそれらのいくつかに対する公開回答をHabréに書くことにしました。



今日は、テンプレートバインディングに関する質問に答えます。 「すべてのレコードではなく、特定の条件に適したレコードのみを表示する必要がある場合はどうなりますか。」



答えはhabrakatの下にあります。





MVVMとKnockoutJSについては説明しません。 この記事を読むことができるhabrで、私のレポートからのビデオもあります。



まず最初に、タスクを設定しましょう-人々のリストに男性だけを表示します。

すでに人々と彼らの性別のリストを表示することができるコードがあります(実際の作品をご覧ください



ViewModel


var Person = function(gender, name) { this.gender = ko.observable(gender); this.name = ko.observable(name); }; var viewModel = { persons: ko.observableArray([ new Person('M', 'John Smith'), new Person('M', 'Mr. Sanderson'), new Person('F', 'Mrs. Sanderson'), new Person('M', 'Agent Ralf'), new Person('F', 'Gangretta Peterson') ]) }; ko.applyBindings(viewModel);
      
      







表示する


 <script type="text/html" id="PersonInfo"> <li> <span data-bind="text: gender"></span> <span data-bind="text: name"></span> </li> </script> <div data-bind=" template: { name: 'PersonInfo', foreach: persons}"></div>
      
      







この問題の最も簡単な解決策は、バインディングで配列を直接フィルタリングすることです。 これは、foreachパラメーターを変更することにより行われます。

 foreach: ko.utils.arrayFilter( persons(), function(p){ return p.gender() == 'M';} )
      
      







これは機能し、正しく機能します。 人物のコレクションを変更すると、リストは最新の状態に保たれます。 この場合、テンプレートは完全には再レンダリングされません。 新しいアイテムが追加され、削除されたアイテムは消えます。 職場で見る



したがって、以下の問題を解決するために必要なコード:



ViewModel


 var Person = function(gender, name) { this.gender = ko.observable(gender); this.name = ko.observable(name); }; var viewModel = { persons: ko.observableArray([ new Person('M', 'John Smith'), new Person('M', 'Mr. Sanderson'), new Person('F', 'Mrs. Sanderson'), new Person('M', 'Agent Ralf'), new Person('F', 'Gangretta Peterson') ]), addMale: function() { this.persons.push(new Person('M', 'New male')); }, addFemale: function() { this.persons.push(new Person('F', 'New female')); }, removePerson: function(person) { this.persons.remove(person); } }; ko.applyBindings(viewModel);
      
      







ビュー自体:



表示する


 <script type="text/html" id="PersonInfo"> <li> <span data-bind="text: gender"></span> <span data-bind="text: name"></span> <small data-bind="text: new Date()"></small> <a href="#remove" data-bind="click: function() { viewModel.removePerson($data); }">x</a> </li> </script> <div data-bind=" template: { name: 'PersonInfo', foreach: ko.utils.arrayFilter( persons(), function(p){ return p.gender() == 'M';} )}"></div> <a href="#add-male" data-bind="click: addMale">Add male</a> <a href="#add-male" data-bind="click: addFemale">Add female</a>
      
      







さて、あなたが思考をオンにすると、私たちはカクを書いたことがわかります。 MVVMを使用してプレゼンテーションロジックとビジネスロジックを分離し、同時にViewでフィルタリングするためのコードを記述します。 これはナンセンスです-MVVMではViewModelがこれを担当します。



したがって、問題の正しい解決策は、男性だけがいるViewModelにフィールドを作成することです。 このフィールドは、人フィールドを変更するときに最新の状態に保つ必要があります。 KnockoutJSはこれにDependent Observablesを使用します 。 malesフィールドが追加するコードを見てみましょう。

 viewModel.males = ko.dependentObservable(function() { return ko.utils.arrayFilter(this.persons(), function(p) { return p.gender() == 'M'; }); }, viewModel);
      
      







依存オブザーバブルには、仕事の明白でない特徴が1つあります。 最初の起動時に、Knockoutは、どのオブザーバブルがアクセスされ、変更にサブスクライブしたかを「記憶」します。 関連するオブザーバブルのいずれかで変更が発生した場合、KOはdependentObservableで説明されている関数をオーバーフローさせます。

また、2番目の属性に注意する価値があります。これは、値を取得する関数の実行中であることを示しています。



作業結果



実際、KOが最初に開始したときにアクセス可能な観測可能物を見ていたと言ったとき、私はunningな思いをしていました。 実際、彼は依存オブザーバブルの値を計算するたびにこれを行います。 デモアプリケーションの最終バージョンを提供します。このバージョンでは、表示する人(男性または女性)を選択し、人の性別を変更することもできます(ああ、罪人を許してください)。



ViewModel


 var Person = function(gender, name) { this.gender = ko.observable(gender); this.name = ko.observable(name); this.changeGender = function() { var g = this.gender() == 'F' ? 'M' : 'F'; this.gender(g); } }; var viewModel = { genderToFilter: ko.observable('M'), persons: ko.observableArray([ new Person('M', 'John Smith'), new Person('M', 'Mr. Sanderson'), new Person('F', 'Mrs. Sanderson'), new Person('M', 'Agent Ralf'), new Person('F', 'Gangretta Peterson') ]), addMale: function() { this.persons.push(new Person('M', 'New male')); }, addFemale: function() { this.persons.push(new Person('F', 'New female')); } }; viewModel.males = ko.dependentObservable(function() { var g = this.genderToFilter(); return ko.utils.arrayFilter(this.persons(), function(p) { return p.gender() == g; }); }, viewModel); ko.applyBindings(viewModel);
      
      







新しいから、注意してください:





ビューの変更もかなり控えめです。

 <script type="text/html" id="PersonInfo"> <li> <a href="#change" data-bind="text: gender, click: changeGender"></a> <span data-bind="text: name"></span> </li> </script> <table width="100%"> <tr valign="top"> <td width="50%"> <label> <input type="radio" value="M" data-bind="checked: genderToFilter" />Males </label> <label> <input type="radio" value="F" data-bind="checked: genderToFilter" />Femails </label> <div data-bind=" template: { name: 'PersonInfo', foreach: males }"></div> </td> <td> <strong>All</strong> <div data-bind=" template: { name: 'PersonInfo', foreach: persons }"></div> </td> </tr> </table> <a href="#add-male" data-bind="click: addMale">Add male</a> <a href="#add-male" data-bind="click: addFemale">Add female</a>
      
      







チェックされたバインディングを持つラジオボタンを追加しました。これは、genderToFilterフィールドの値に従って選択されるバインディングを決定します。 これは双方向バインディングであるため、選択したラジオの変更を変更すると、viewModelに変更が加えられます。 フィルタリング中にgenderToFilterフィールドにアクセスしました。つまり、フィルタリングが再び行われます。



同様に、それは性転換でも起こります。 彼女はフィルタリング方法に参加しました。つまり、いずれかの人々の性別を変更することでリストがフィルタリングされます。



これに照らして、cの私の告白はタイムリーでした。 KOが各dependentObservableの再計算中にアクセスしたオブザーバブルをスキャンしていなかった場合、ランタイムに追加された人々の性別の変更は再フィルタリングにつながりませんでした。



最後の段落は少しわかりにくいですが、うまくいけば理解できるでしょう。



最終版を見る



おわりに



実際、この記事全体は、テンプレートバインディングのforeachパラメーターの値としてdependentObservableを使用することに専念しています。

ご覧のとおり、dependentObservableは非常に強力です。 関連オブジェクトへのすべての変更を追跡します。 実際の作業では、同様のタスクが複数回発生します。 したがって、最後の例がなぜ機能するのを理解することを強くお勧めします。



最後まで読んでくれてありがとう。 私は質問と建設的な批判に喜んでいるでしょう。



All Articles