使用Knockout Observable Array添加动态UI元素

时间:2016-10-05 20:55:06

标签: knockout.js

这是我的问题,我正在努力。

1-我正在使用映射插件来创建Knockout ViewModel。从服务器端我有一个复杂的对象,它有几个属性(与这个问题无关)。除了其他属性,我有一个属性TotalofExpenses,还有一个名为Expenses的列表(费用项是一个有金额和描述的对象)。映射插件将此列表转换为ObservableArray,将TotalofExpenses转换为observalbe

2-在UI上我想实现以下

  • 最初我想为费用项目提供输入。两个输入框(一个用于数量,一个用于描述)
  • 一旦用户添加了描述和费用金额(两者),我想为另一个费用项目显示输入框,并在某处显示当前费用的总和(比如在表格的底部)。随着用户不断添加更多费用,我不断展示新的输入框并继续显示totalofExpenses
  • 如果用户返回之前添加的费用项目并进行更改,请更改我想要更新总金额的金额。

以下是我试图实现上述功能的代码。它没有按照预期的那样工作。 注意:我试图只添加与此问题相关的代码

      var data = @Html.Raw(Model.ToJson());
       //Add total expenses  TotalShiftExpenses
    var mappingOptionsForTotalShiftExpenses = {
        create: function(options) {
            return new ComputeTotalShiftExpenses(options.data);
        }
    };

    var Expense = function(data) {
        var self = this;
        self.ShiftId = ko.observable(@Model.Shift.Id),
            self.Id = ko.observable(0),
            self.Amount = ko.observable(0),
            self.Description = ko.observable("");
    }        

    function ComputeTotalShiftExpenses(data) {
        var self = this;
        var model = ko.mapping.fromJS(data, {}, self);
        model.TotalShiftExpenses = ko.computed(function() {
            var sum = 0;

            for (var i = 0; i < model.Shift.ShiftExpenses().length; i++) {
                //                 if ((parseFloat(model.Shift.ShiftExpenses()[i].Amount)) > 0) {

                sum += parseFloat(model.Shift.ShiftExpenses()[i].Amount());

            }
           //Add empty Expense to Observable array
           if (parseFloat(model.Shift.ShiftExpenses()[ (model.Shift.ShiftExpenses().length - 1)].Amount()) > 0) {

                model.Shift.ShiftExpenses
                    .push( new Expense({ "ShiftId": 0, "Id": 0, "Amount": 0, "Description": "" }));

          }

            return sum.toFixed(2);
        });
        return model;
    }

        //Apply MappingOptions
      var AppViewModel = ko.mapping.fromJS(data, mappingOptionsForTotalShiftExpenses);

        $(document)
        .ready(function() {

            ko.applyBindings(AppViewModel);


        });

通过上面的代码,我能够得到     - 第一个费用项目的第一个输入框     - 一旦我添加费用项目,它将显示为下一个项目设置的另一个输入框,并显示总计。     - 问题是当我尝试添加第二个费用时它没有触发任何东西(似乎第二组输入框是不可观察的)。但是,如果我在第一个费用项目中进行金额更改,它将触发可观察行为,并将正确添加两个项目的项目..并添加第三个费用的UI输入框。由于某种原因,最后一个输入框不会触发可观察的行为,但是在最后一个输入框之前的任何一个框都会触发。 注意:我发布的代码只是试图增加费用。

迫不及待地想要听一些淘汰专家的消息......提前多多感谢。

2 个答案:

答案 0 :(得分:0)

因此,根据您所说的内容,我创建了一个简单的小提琴,输入字段的数量由ko.observableArrayforeach绑定控制。当您从数组中添加或删除(不是小提琴中)项目时,视图会自动更新。在你的情况下,似乎你需要做更多的功能和其他特殊检查,但我相信这可以帮助你完成你想要完成的任务。

JSFiddle

function TestVM() {
  this.inputFields = ko.observableArray([
    { type: 'type1', displayName: 'name1' },
    { type: 'type2', displayName: 'name2' },
    { type: 'type3', displayName: 'name3' }
  ]);
}

var dynamic = new TestVM();
ko.applyBindings(dynamic);

$('#addInput').click(function(){
dynamic.inputFields.push({ type: 'newtype', displayName: 'newField' });
  console.log(dynamic.inputFields());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div>
  <div data-bind="foreach: inputFields">
    <label data-bind="text: $data.displayName"></label><br>
    <input data-bind="name: $data.type"><br>
  </div>
</div>

<button id="addInput">Add</button>

答案 1 :(得分:0)

在我看来,你有一些过于复杂的东西。在数据中映射并创建跟踪总量的计算可观察量应该非常简单:

编辑:已更新,以包含动态输入框功能。您可以通过向计算的observable添加几行代码来完成此操作,以保证数组底部始终存在空白项:

// Data pulled in from wherever
var data = [{
  ShiftId: 1,
  Id: 1,
  Amount: 25.00,
  Description: 'Supplies'
}, {
  ShiftId: 2,
  Id: 2,
  Amount: 20.00,
  Description: 'Lunch'
}];

// Expense model
var Expense = function(shiftId, id, amount, desc) {
  var self = this;
  self.ShiftId = ko.observable(shiftId);
  self.Id = ko.observable(id);
  self.Amount = ko.observable(amount);
  self.Description = ko.observable(desc);
};

//  Main ViewModel
var AppViewModel = function() {
  var self = this;

  // Expenses array
  self.Expenses = ko.mapping.fromJS(data);

  // Compute the grand total of all expenses
  self.TotalofExpenses = ko.computed(function() {
    // First, ensure that a blank item remains at the bottom of array
    self.Expenses.remove(function(item) {
        return !item.Amount() && !item.Description();
    });
    self.Expenses.push(new Expense());

    // Now calculate the sum of all expenses
    var total = 0;
    self.Expenses().forEach(function(item, index) {
      total += +item.Amount() || 0;
    });

    return total;
  });
};

ko.applyBindings(new AppViewModel());

编辑2:更新了JSFiddle,以便在用户为Description输入提供值之前禁用Amount输入框(请参阅JSFiddle链接中的HTML)。我这样做只需添加&#39;启用:说明&#39;到金额输入框的绑定。

https://jsfiddle.net/dw1284/aaysdxmb/9/