KoJs: bind a dynamic number of text boxes to elements of an array

时间:2017-10-12 09:59:52

标签: javascript jquery arrays knockout.js

I have a front-end which allows for adding and removing of text boxes suing the foreach binding. A text box looks something like this

<div id="dynamic-filters" data-bind="foreach: filterList">
        <p>
            <input type="text" data-bind="textInput: $parent.values[$index()], autoComplete: { options: $parent.options}, attr: { id : 'nameInput_' + $index() }"/>
        </p>
 </div>

What I want to do, as shown in the code above is to bind each of these dynamically generated text boxes to an element in the array using the $index() context provided by knockout.js

However it doesn't work for me, my self.values=ko.observableArray([]) doesn't change when the text boxes change.

My question is, if I want to have a way to bind these dynamically generated text boxes, is this the right way to do it? If it is how do I fix it? If it's not, what should I do instead?

Thanks guys!

EDIT 1 the values array is an observable so I thought I should unwrap it before use. I changed the code to

<input type="text" data-bind="textInput: $parent.values()[$index()], autoComplete: { options: $parent.options}, attr: { id : 'nameInput_' + $index() }"/>

This works in a limited way. When I add or change the content of text boxes, the array changes accordingly. However when I delete an element it fails in two ways:

  • If I delete the last item, the array simply doesn't change
  • If I delete an item in between, everything is shifted back

I suppose I have to add a function that changes the text-input value before destroying the text box itself.

Any help or advice on how to do this?

1 个答案:

答案 0 :(得分:1)

我建议首先获取值数组并将其映射到某种模型,然后将其转储到 filterList ko.observableArray中。它可以像需要的那样复杂或简单。

通过这种方式,您可以直接访问ko foreach:level中的这些属性,而不必进行高级索引访问。

我还添加了一个简单的淘汰组件示例,以向您展示可以实现的目标。

var PageModel = function() {
  var self = this;
  var someArrayOfValues = [{label: 'label-1', value: 1},{label: 'label-2', value: 2},{label: 'label-3', value: 3},{label: 'label-4', value: 4}];
  
  this.SimpleInputs = ko.observableArray(_.map(someArrayOfValues, function(data){
    return new SimpleInputModel(data);
  }));
  this.AddSimpleInput = function(){
    self.SimpleInputs.push(new SimpleInputModel({value:'new val', label:'new label'}));
  };
  this.RemoveSimpleInput = function(obj){
    self.SimpleInputs.remove(obj);
  }
}

var SimpleInputModel = function(r) {
  this.Value = ko.observable(r.value);
  this.Label = r.label;
};

var SimpleInputComponent = function(params){
  this.Id = makeid();
  this.Label = params.label;
  this.Value = params.value;
  function makeid() {
    var text = "";
    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    for (var i = 0; i < 5; i++)
      text += possible.charAt(Math.floor(Math.random() * possible.length));

    return text;
  }
}

ko.components.register('input-component', {
  viewModel: SimpleInputComponent,
  template: '<label data-bind="text: Label, attr: {for: Id}"></label><input type="text" data-bind="textInput: Value, attr: {id: Id}" />'
})

window.model = new PageModel();
ko.applyBindings(model);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<!-- ko if: SimpleInputs -->
  <h3>Simple Inputs</h3>
  <!-- ko foreach: SimpleInputs -->
    <input-component params="value: Value, label: Label"></input-component>
    <button data-bind="click: $parent.RemoveSimpleInput">X</button>
    <br>
  <!-- /ko -->
<!-- /ko -->
<button data-bind="click: AddSimpleInput">Add Input</button>