Leonardo Wildt
Leonardo Wildt

Reputation: 2599

Create HTML Elements From Knockout Array

I have a model that i am using in knockout that contains different observables like so.

self.myModel = { 
FirstName: ko.observable(), 
LastName: ko.observable(),
MiddleName : ko.observable(),
CatName: ko.observable(),
DogName: ko.observable()
... approx 80 properties
}

I would like to create an html text input for each property in my model.

like so

<!--ko foreach: myModel-->
<span data-bind="text: myModel.item"></span>
<!--/ko-->

Here is my Fiddle

How can i create an Html Element for each observable property using a foreach loop?

Upvotes: 0

Views: 476

Answers (2)

caseyWebb
caseyWebb

Reputation: 2096

As you've got it, you could make this work using Object.keys...

<!-- ko foreach: { data: Object.keys(myModel), as: 'key' } -->
  <p>
    <input data-bind="textInput: $parent.myModel[key]"/>
    <span data-bind="text: key"></span>
  </p>
<!--/ko-->

You could then go one step further and create a custom binding for iterating objects, such as this...

ko.bindingHandlers.foreachProperty = {
  init(el, valueAccessor) {
    const keyValuePairs = ko.pureComputed(() => {
      const raw = ko.unwrap(valueAccessor());
      return Object.keys(raw).map((key) => ({
        key,
        value: raw[key]
      }));
    });

    ko.applyBindingsToNode(el, {
      foreach: keyValuePairs
    });

    return { controlsDescendantBindings: true };
  }
}

ko.virtualElements.allowedBindings.foreachProperty = true;

<!-- ko foreachProperty: myModel -->
  <p>
   <input data-bind="textInput: value"/>
   <span data-bind="text: key"></span>
  </p>
<!--/ko-->

Here is your fiddle with a custom binding, https://jsfiddle.net/p1vfe6fu/9/

Please note though, what you've got is an object, not an array.

Upvotes: 1

Bryan Dellinger
Bryan Dellinger

Reputation: 5304

here is a solution. of interest I used the rawData binding to keep the observable in sync with the span after it.

function ViewModel(){
var self = this;

self.myModel = { 
    FirstName: ko.observable("Bert"), 
    LastName: ko.observable("Anderson"),
    MiddleName : ko.observable(),
    CatName: ko.observable(),
    DogName: ko.observable(),
    };
 
self.labels= ko.computed(function() {
    var array = $.map(self.myModel, function(value, key) {
    return [key];
    });
    return array
}, this);
    
self.myArray= ko.computed(function() {
    var array = $.map(self.myModel, function(value, index) {
    return [value];
    });
    return array
}, this);
}

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



<!--ko foreach: myArray-->
<p>
 <strong data-bind="text: $root.labels()[$index()]"></strong> 
 <input data-bind="textInput: $rawData"/>
 <span data-bind="text: $data"></span>
</p>
<!--/ko-->

all that being said not sure it this is a great approach you may want to look into templates and components, and the knockout mapping plugin.

Upvotes: 1

Related Questions