2017-09-02 54 views
0

在淘汰賽很多時候我都在下面的情況:KnockoutJS - 雙向綁定適配器 - 避免循環

我有一個觀察的,我想創造可觀A和B之間的雙向綁定適配器,也就是如果A的變化,改變B和當B的變化,然後改變A.

 
     +-------------+   +-------------+   +-------------+ 
     |  A  | -----> | Adapter | -----> |  B  | 
     | Observable | <----- |    | <----- | Observable | 
     +-------------+   +-------------+   +-------------+ 

起初,這似乎是一個不這樣做,因爲這將創建一個循環依賴,但最終這究竟會發生什麼時您將GUI元素綁定到observable。想象一下,你有一個現有的綁定,但你想改變它的綁定結果,而不必觸摸綁定本身。

讓我們看一個例子(jsfiddle here):

HTML:

<body> 
    <p data-bind="text: 'Value:' + val()"></p> 
    <input type="text" data-bind="textInput: val"></input> 
    <p data-bind="text: 'Value 2:' + val2()"></p> 
    <input type="text" data-bind="textInput: val2"></input> 
</body> 

的Javascript:

function ViewModel() { 
    var self = this; 

    this.val = ko.observable(""); 
    this.val2 = ko.observable(""); 

    this.val.subscribe(function() { 
    console.log("VAL Changed!"); 
    self.val2(self.val().toUpperCase()); 
    }); 

    this.val2.subscribe(function() { 
    console.log("VAL2 Changed!"); 
    self.val(self.val2().toLowerCase()); 
    }); 
} 

ko.applyBindings(new ViewModel()); 

你會發現,當你在輸入的東西第一個文本框一個週期被觸發:

  • 的結合變化VAL
  • 的訂閱VAL火災和變化VAL2
  • 的訂閱VAL2火災和改變VAL
  • 敲除surpresses運行認購VAL再次(週期檢測)

結果是,如果您在第一個輸入框中鍵入一個大寫字母,它將立即轉換爲小寫字母,由第二個訂閱,反之亦然。

雖然這個例子看起來不錯,但它可能會導致很難找到錯誤。要解決現在的問題是一個簡單的方法,有一個標誌裏面的綁定,這將避免升級時,我們是對方的更新裏面:

jsfiddle here

.... 
var flag = false; 
    this.val.subscribe(function() { 
    if (flag) return; 
    flag = true; 
    self.val2(self.val().toUpperCase()); 
    flag = false; 
    }); 

    this.val2.subscribe(function() { 
    if (flag) return; 
    flag = true; 
    self.val(self.val2().toLowerCase()); 
    flag = false; 
    }); 
    .... 

現在當你改變第二個輸入時,它不會「回火」,而只會朝一個方向發射。

現在終於我的問題:

  • 是適配器無效用例和它暗示與代碼的概念問題?

  • 你會如何去防止氣旋?有一個像我的例子中的國旗?也許使用throtteling?

回答

1

你有什麼是一個實際的數據項目和兩個計算器,以呈現爲大寫或小寫。訣竅是計算結果需要可寫。他們的寫函數可以直接寫入底層數據項,因爲它的情況並不重要。

function ViewModel() { 
 
    var self = this; 
 

 
    this.val = ko.observable(""); 
 

 
    this.val1 = ko.pureComputed({ 
 
    read: function() { 
 
     return self.val().toLowerCase(); 
 
    }, 
 
    write: function(newVal) { 
 
     self.val(newVal); 
 
    } 
 
    }); 
 
    this.val2 = ko.pureComputed({ 
 
    read: function() { 
 
     return self.val().toUpperCase(); 
 
    }, 
 
    write: function(newVal) { 
 
     self.val(newVal); 
 
    } 
 
    }); 
 

 
    this.val1.subscribe(function() { 
 
    console.log("VAL Changed!"); 
 
    }); 
 

 
    this.val2.subscribe(function() { 
 
    console.log("VAL2 Changed!"); 
 
    }); 
 
} 
 

 
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script> 
 

 
<body> 
 
    <p data-bind="text: 'Value:' + val1()"></p> 
 
    <input type="text" data-bind="textInput: val1" /> 
 
    <p data-bind="text: 'Value 2:' + val2()"></p> 
 
    <input type="text" data-bind="textInput: val2" /> 
 
</body>

+0

這不能解決循環依賴的影響。如果你運行你的代碼片段,你會意識到,當輸入一個小寫字母到第二個輸入時,它將立即被第一次訂閱反射轉換爲大寫字母。 –

+0

訂閱不做任何轉換。沒有周期。當底層數據項發生變化時,兩個計算的「讀取」值都會改變,就這些了。 –

+0

對不起 - 我沒有花時間理解你的解決方案。所以你說有些情況下,「適配器」的兩端只代表「一個」值。上述解決方案有所不同,它允許適配器的兩端具有不同的狀態,而在您的情況下,適配器的兩端都只依賴一個狀態「val」。這似乎比我更清潔,比馬修斯的解決方案。你認爲有可能出現兩種狀態的解決方案更適用的情況嗎?我的大腦無法完全圍繞它。 –

1

我在類似的情況下使用了下面的方法。我認爲它很乾淨,你不必擔心用標誌管理變量的狀態。

基本上,使用可寫的observables,併爲每個相關項目創建一個支持observable來存儲它的狀態,然後使用可寫的observable來處理邏輯關於當其他可觀察值應該發生什麼變化。

所以,你的視圖模型應該是這樣的:

function ViewModel() { 
    var self = this; 

    self.val = ko.observable(""); 
    self.val2 = ko.observable(""); 

    self.valComputed = ko.pureComputed({ 
    read: function() { return self.val(); }, 
    write: function (value) { 
     self.val(value); 
     self.val2(value.toUpperCase()); 
    } 
    }); 

    self.val2Computed = ko.pureComputed({ 
    read: function() { return self.val2(); }, 
    write: function (value) { 
     self.val2(value); 
     self.val(value.toLowerCase()); 
    } 
    }); 
} 

ko.applyBindings(new ViewModel()); 

而且你會改變你的HTML綁定到計算的觀測,如:

<body> 
    <p data-bind="text: 'Value:' + valComputed()"></p> 
    <input type="text" data-bind="textInput: valComputed"></input> 
    <p data-bind="text: 'Value 2:' + val2Computed()"></p> 
    <input type="text" data-bind="textInput: val2Computed"></input> 
</body> 

我希望幫助! :-)

+0

啊哈我的腦袋疼... :-)行爲實際觀測的差異將是寫val2Computed不會在其自身價值得到體現。你認爲這將是保存添加'self.val2(價值);'到val2Computed的寫處理函數,也是'self.val(value);'寫入val處理器的寫處理程序,以確保計算出的結果與真實可觀察結果相似?看[這個小提琴](https://jsfiddle.net/domoran/L28p2ssd) –

+0

啊,當然。接得好!你一定要存儲正在設置的observable的值。我編輯它,添加在這些行中。 –