ノックアウト用のLinqのような構文

私たちのチームがMVVMパターンと特にKnockoutフレームワークを使用してWebポータルを開発してから1年が経過しました。 少しずつ経験が蓄積され、さまざまな解決策が現れ、良い習慣と悪い習慣があり、今ではいわば熟していました。 linq.jsライブラリはlinq構文のjavascript用に既に存在し、長い間、プロジェクトに取り込むかどうかを考えていました。 使用例でさえ、インターネットでのノックアウトと結びついてます。

私にふさわしいアイデアは、Linqメソッド内で計算カプセル化を作成することでした。

比較のために、フィドルのコード:

this.filteredItems = ko.computed(function() { var term = this.searchTerm(); return this.items.where(function(item) { return item.name.indexOf(term) > -1; }); }, this);
      
      





代わりに書きたいコード:

  this.filteredItems = this.items .Where(function(item) { return item.name.indexOf(this.searchTerm()) > -1; });
      
      







上記の計算されたカプセル化の後、linq.jsライブラリは実際には必要ないことが判明しました。 ノックアウトに組み込まれた十分なツール。 さらに、それらは一度だけ書く必要があり、たとえそれが最も直接的で単純なforループであっても、外部で違いはありません。



そのため、最初にメソッドを使用してオブジェクトを準備します。

 var methods = { First: function(predicate) { return ko.computed(function() { return ko.utils.arrayFirst(this(), predicate); }, this, { deferEvaluation: true }); }, Select: function(func) { return ko.computed(function() { return ko.utils.arrayMap(this(), function(item) { return ko.utils.unwrapObservable(func(item)); }); }, this, { deferEvaluation: true }); }, SelectMany: function(func) { return ko.computed(function() { var result = []; ko.utils.arrayForEach(this(), function(item) { result = result.concat(ko.utils.unwrapObservable(func(item))); }); return result; }, this, { deferEvaluation: true }); }, Where: function(predicate) { return ko.computed(function() { return ko.utils.arrayFilter(this(), predicate); }, this, { deferEvaluation: true }); }, Distinct: function(func) { if (!func) { return this.DistinctValue(); } return ko.computed(function() { var obj = {}; return ko.utils.arrayFilter(this(), function(item) { var val = ko.utils.unwrapObservable(func(item)); return obj[val] ? false : (obj[val] = true); }); }, this, { deferEvaluation: true }); }, DistinctValue: function() { return ko.computed(function() { var obj = {}; return ko.utils.arrayFilter(this(), function(val) { return obj[val] ? false : (obj[val] = true); }); }, this, { deferEvaluation: true }); }, Sum: function(func) { return func ? this.Select(func).Sum() : this.SumValue(); }, SumValue: function() { return ko.computed(function() { var result = 0; ko.utils.arrayForEach(this(), function(item) { result = result + (+item); }); return result; }, this, { deferEvaluation: true }); }, StringJoin: function(joinString) { joinString = joinString || ', '; return ko.computed(function() { return this().join(joinString); }, this, { deferEvaluation: true }); }, };
      
      





obsrvableArrayでメソッドをフックして計算した2番目のアクション:

  for (var i in methods) { ko.observableArray.fn[i] = methods[i]; ko.computed.fn[i] = methods[i]; }
      
      





料理の準備ができたら、それを使用してください。 例:

  self.DistinctEntities = policy.Coverages .SelectMany(function(item) { return item.Entities; }) .Distinct(function(item) { return item.Name; }); self.EmployeeCount = policy.CoveredTotalCurrentYear .Sum(function (item) { return item.Quantity; }); self.LineOfCoverageColumnName = policy.Coverages .Select(function (item) { return item.LineOfCoverage.ShortDisplayName; }) .StringJoin();
      
      







まず、Selectメソッドに似たMapメソッド。ただし、複雑でコストのかかる操作、特に入力配列の各データモデルに対して、出力にビューモデルを作成する必要がある場合。 入力配列に要素を追加するとき、選択操作は配列のすべての要素に対して「ラムダ」を再呼び出しし、マップ操作は新しく追加された要素に対してのみこれを行います。

 Map: function (converter) { var oldValues = []; var oldResults = []; return ko.computed(function() { var values = this().slice(); var results = []; ko.utils.arrayForEach(values, function(item) { var index = oldValues.indexOf(item); results.push(index > -1 ? oldResults[index] : converter(item)); }); oldValues = values; oldResults = results; return results; }, this, { deferEvaluation: true }); },
      
      





使用法:

 self.Coverages = policy.Coverages.Map(function(coverage) { return new coverageViewModel(coverage); });
      
      







PSメソッドのリストはまだLinQのセット全体を網羅していませんが、拡張するのは難しくありません。



All Articles