1

拥有一组可观察的银行交易,其中包含金额等。

我试图保持一个可计算的运行平衡,但我似乎陷入了无限循环和各种不良情况。

这是我能想到的最简单的小提琴 - 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>

它仍然不完整,因为我最终围绕如何让期初余额显示和重新计算。

流动余额需要是可观察的,因此如果期初余额或交易发生变化,它将重新计算。

期末余额也需要是最终的流动余额。

4

2 回答 2

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 不是客户端页面处理库。它是一个数据绑定库。它只能显示页面生命周期内真正存在的数据。

此外,并非 Knockout 中的每个值都需要包装在可观察值中,只有您希望在页面生命周期内更改的值。Account.openingBalance或者Tansaction.amount是不太可能更改的属性,可以将它们展开。

不相关,但尽量遵循 JS 大小写的约定:PascalCaseNames仅用于构造函数和构造函数,所有其他变量(成员函数、属性、局部变量)都会得到camelCaseNames.

还有一件事 - 如果您使用货币值,请注意 IEEE 754 浮点运算的陷阱。在您在其他任何地方使用它们之前,所有操作都应该被适当地四舍五入。如果您在没有任何中间舍入的情况下拖动运行值(如上面的代码示例中),您最终可能会由于内部数字表示问题而得到关闭的值。

于 2013-10-20T23:54:48.120 回答
1

你也可以通过使用普通函数而不是计算的 observable 来做到这一点。

看到这个小提琴

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>
于 2013-10-22T07:00:19.553 回答