Reputation: 2599
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
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
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