2013-10-20 36 views
1

有一個可觀察的銀行交易數組,其中包含金額等。敲除js計算出的運行餘額無限循環

我試圖保持運行平衡作爲可計算但我似乎陷入無限循環和各種惡劣。

這是我能想出的最簡單的小提琴 - http://jsfiddle.net/Nnyxx/2/

JS:

var transactions = [{Amount: -100}, {Amount: 125}, {Amount: 10}, {Amount: 25}, {Amount: -125}, {Amount: 400}]; 

var ViewModel = function() { 
    this.OpeningBalance = ko.observable(1000); 
    this.RunningBalance = ko.observable(this.OpeningBalance()); 
    this.ClosingBalance = ko.observable(this.RunningBalance()); 

    this.UpdateRunningBalance = ko.computed({ 
     read: function() { 
      return this.RunningBalance(); 
     }, 
     write: function(amount) { 
      this.RunningBalance(this.RunningBalance() + amount); 

      return amount; 
     } 
    }, this); 

    this.Transactions = ko.observableArray(ko.mapping.fromJS(transactions)()); 
} 

var model = new ViewModel(); 

ko.applyBindings(model); 

HTML:

<table> 
    <thead> 
     <tr> 
      <th width="150"></th> 
      <th width="150">Money Out</th> 
      <th width="150">Money In</th> 
      <th>Balance</th> 
     </tr> 
    </thead> 
    <tbody> 
     <tr> 
      <td colspan="3"><strong>Opening Balance</strong></td> 
      <th> 
       <span data-bind="text: OpeningBalance"></span> 
      </th> 
     </tr> 
     <!-- ko foreach: Transactions --> 
     <tr> 
      <td></td> 
      <!-- ko if: Amount() < 0 --> 
      <td data-bind="text: Amount"></td> 
      <td></td> 
      <!-- /ko --> 
      <!-- ko if: Amount() > 0 --> 
      <td></td> 
      <td data-bind="text: Amount"></td> 
      <!-- /ko --> 
      <td> 
       <span data-bind="text: $root.UpdateRunningBalance(Amount())"></span> 
      </td> 
     </tr> 
     <!-- /ko --> 
     <tr> 
      <td colspan="3"><strong>Closing Balance</strong></td> 
      <th> 
       <span data-bind="text: ClosingBalance"></span> 
      </th> 
     </tr> 
    </tbody> 
</table> 

它仍然是不完整的,因爲我最終繞來繞去在如何獲得期初餘額顯示和recal的圈子裏culate。

運行餘額需要是可觀察的,所以如果期初餘額或交易發生變化,它將重新計算。

此外期末餘額需要是最終運行餘額。

回答

3

我想你錯過了這裏必不可少的東西。

Knockout中沒有這樣的運行平衡。 屏幕上顯示的每個數據點都必須綁定到視圖模型中的實時值。

沒有任何值「僅在當前行被創建時存在」,類似於PHP在循環過程中構建HTML時所要執行的操作。如果您需要10個「運行值」行,則在您的視圖模型中有10個不同的值。

比較:

function Transaction(amount, previousBalance) { 
    this.amount = amount; 
    this.balance = previousBalance + amount; 
} 

function Account(openingBalance, transactions) { 
    var self = this; 

    // properties 
    self.openingBalance = openingBalance; 
    self.transactions = ko.observableArray(); 
    self.closingBalance = ko.computed(function() { 
     var transactions = self.transactions(), 
      lastTransaction = transactions[transactions.length - 1]; 
     return lastTransaction ? lastTransaction.balance : openingBalance; 
    }); 

    // methods 
    self.addTransaction = function (amount) { 
     var previousBalance = self.closingBalance(); 
     self.transactions.push(new Transaction(amount, previousBalance)); 
    }; 

    // init  
    ko.utils.arrayForEach(transactions, function (t) { 
     self.addTransaction(t.Amount); 
    }); 
} 

var transactions = [{Amount: -100}, {Amount: 125}, {Amount: 10}, 
        {Amount: 25}, {Amount: -125}, {Amount: 400}]; 

ko.applyBindings(new Account(1000, transactions)); 

(看到它住在這裏http://jsfiddle.net/Nnyxx/5/

注意每個錶行有着怎樣的當前運行值的相應值實際存在的視圖模型

淘汰賽不是客戶端頁面處理庫。它是一個數據綁定庫。它只能顯示頁面生命週期中真正存在的數據。

此外,並非Knockout中的每個值都需要包含在可觀察值中,只有您希望在頁面生命週期中更改的值。 Account.openingBalanceTansaction.amount是不可能改變的屬性,它們可以被解包。

不相關,但試圖遵循JS框架的約定:僅對構造函數和構造函數使用PascalCaseNames,所有其他變量(成員函數,屬性,局部變量)將獲得camelCaseNames

還有一點 - 如果您使用貨幣值工作,請小心IEEE 754浮點運算的缺陷。所有操作都應該在其他地方使用前妥善舍入。如果您拖動一個沒有任何中間舍入的運行值(如上面的代碼示例中所示),則最終可能會因內部數字表示問題而關閉值。

+0

真棒,謝謝你的幫助。正式注意到關於語法和舍入的評論。 – Colin

+0

很高興幫助。您可以查看[extenders](http://knockoutjs.com/documentation/extenders.html)來管理貨幣價值,這些貨幣價值恰當地自動調整。 – Tomalak

1

您也可以通過使用普通函數而不是計算可觀察值來實現此目的。

this fiddle

JS:

var transactions = [{ 
    Amount: -100 
}, { 
    Amount: 125 
}, { 
    Amount: 10 
}, { 
    Amount: 25 
}, { 
    Amount: -125 
}, { 
    Amount: 400 
}]; 

var ViewModel = function() { 
    var self = this; 
    self.Transactions = ko.observableArray(ko.mapping.fromJS(transactions)()); 
    self.OpeningBalance = ko.observable(1000); 
    self.runningBalance = self.OpeningBalance(); 

    self.RunningBalance = function (index) { 
     return ko.computed(function() { 
      var total = parseInt(self.OpeningBalance()); 
      for (i = 0; i <= index; i++) { 
       total += parseInt(self.Transactions()[i].Amount()); 
      } 
      return total; 
     }); 
    }; 
    self.ClosingBalance = ko.computed(function() { 
     var total = parseInt(self.OpeningBalance()); 
     ko.utils.arrayForEach(self.Transactions(), function (item) { 
      total += parseInt(item.Amount()); 
     }); 
     return total 
    }); 

} 

var model = new ViewModel(); 

ko.applyBindings(model); 

HTML

Opening balance 
<input data-bind="value: OpeningBalance" /> 
<table> 
    <thead> 
     <tr> 
      <th width="150"></th> 
      <th width="150">Money Out</th> 
      <th width="150">Money In</th> 
      <th>Balance</th> 
     </tr> 
    </thead> 
    <tbody> 
     <tr> 
      <td colspan="3"><strong>Opening Balance</strong> 

      </td> 
      <th> <span data-bind="text: OpeningBalance"></span> 

      </th> 
     </tr> 
     <!-- ko foreach: Transactions --> 
     <tr> 
      <td></td> 
      <!-- ko if: Amount() < 0 --> 
      <td data-bind="text: Amount"></td> 
      <td></td> 
      <!-- /ko --> 
      <!-- ko if: Amount()> 0 --> 
      <td></td> 
      <td data-bind="text: Amount"></td> 
      <!-- /ko --> 
      <td> <span data-bind="text: $root.RunningBalance($index())"></span> 

      </td> 
     </tr> 
     <!-- /ko --> 
     <tr> 
      <td colspan="3"><strong>Closing Balance</strong> 

      </td> 
      <th> <span data-bind="text: ClosingBalance"></span> 

      </th> 
     </tr> 
    </tbody> 
</table> 
+0

是的,這就是我最初的做法,直到我需要更改期初餘額,這反過來又會改變交易的運行餘額。 – Colin

+0

答案和小提琴更新 –