Reputation: 1369
I'm still new to knockout, so maybe I'm doing something wrong. I have an html form with dynamic input controls which can be added by the user. I don't know if the problem is caused by the containerless control flow syntax, but I don't know how I can achieve the same result without it.
The markup for the dynamic part is this:
//begin form
<!-- ko foreach: durationValues -->
<div class="form-group">
<!-- ko if: ($index() === 0) -->
<label for="Values[0].Value">Values</label>
<!-- /ko -->
<div class="input-group">
<input class="form-control" type="text" data-bind="value: text, attr: { name: 'Values[' + $index() + '].Value' }" data-val="true" data-val-required="Value required." />
<span class="input-group-btn">
<!-- ko if: ($index() === 0) -->
<button class="btn btn-outline-secondary" type="button" data-bind="click: addDurationValue"><i class="fa fa-plus-circle"></i> Add</button>
<!-- /ko -->
<!-- ko if: ($index() > 0)-->
<button class="btn btn-outline-secondary" type="button" data-bind="click: $parent.removeDurationValue"><i class="fa fa-trash"></i> Remove</button>
<!-- /ko -->
</span>
</div>
<span class="field-validation-valid invalid-feedback" data-bind="attr: { 'data-valmsg-for': 'Values[' + $index() + '].Value' }" data-valmsg-replace="true"></span>
</div>
<!-- /ko -->
//endform
//jquery, jquery-validate, jquery-validate-unobtrusive, bootstrap and knockout script files loaded
<script>
function DurationValue(text) {
this.text = text;
}
function DurationValuesViewModel() {
var self = this;
self.durationValues = ko.observableArray([
new DurationValue("")
]);
self.addDurationValue = function () {
self.durationValues.push(new DurationValue(""));
//Remove current form validation information
$("form")
.removeData("validator")
.removeData("unobtrusiveValidation");
//Parse the form again
$.validator.unobtrusive.parse("form");
};
self.removeDurationValue = function (durationValue) { self.durationValues.remove(durationValue); };
}
ko.applyBindings(new DurationValuesViewModel());
</script>
The page keeps yelling me these errors:
Uncaught ReferenceError:
Unable to process binding "foreach: function (){return durationValues }"
Message: Unable to process binding "if: function (){return ($index() === 0) }"
Message: Unable to process binding "click: function (){return addDurationValue }"
Message: addDurationValue is not defined at click (eval at createBindingsStringEvaluator (knockout-3.4.2.debug.js:2992), :3:58) at newValueAccessor (knockout-3.4.2.debug.js:4231) at init (knockout-3.4.2.debug.js:4241) at init (knockout-3.4.2.debug.js:4234) at knockout-3.4.2.debug.js:3368 at Object.ignore (knockout-3.4.2.debug.js:1480) at knockout-3.4.2.debug.js:3367 at Object.arrayForEach (knockout-3.4.2.debug.js:159) at applyBindingsToNodeInternal (knockout-3.4.2.debug.js:3353) at applyBindingsToNodeAndDescendantsInternal (knockout-3.4.2.debug.js:3233)
Upvotes: 0
Views: 361
Reputation: 35232
Your containerless control flow syntax is fine. But, you need to add $parent
or $root
before the addDurationValue
used in click
binding function to provide proper binding context
Inside the foreach
binding, knockout looks for addDurationValue
in each DurationValue
object rather than the DurationValuesViewModel
instance. So, we need to specify where to look for the function. In this case, $root
and $parent
refer to the same object.
function DurationValue(text) {
this.text = text;
}
function DurationValuesViewModel() {
var self = this;
self.durationValues = ko.observableArray([
new DurationValue("")
]);
self.addDurationValue = function() {
self.durationValues.push(new DurationValue(""));
//Remove current form validation information
$("form")
.removeData("validator")
.removeData("unobtrusiveValidation");
// Commented for now
//Parse the form again
// $.validator.unobtrusive.parse("form");
};
self.removeDurationValue = function(durationValue) {
self.durationValues.remove(durationValue);
};
}
ko.applyBindings(new DurationValuesViewModel());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<!-- ko foreach: durationValues -->
<div class="form-group">
<!-- ko if: ($index() === 0) -->
<label for="Values[0].Value">Values</label>
<!-- /ko -->
<div class="input-group">
<input class="form-control" type="text" data-bind="value: text, attr: { name: 'Values[' + $index() + '].Value' }" data-val="true" data-val-required="Value required." />
<span class="input-group-btn">
<!-- ko if: ($index() === 0) -->
<button class="btn btn-outline-secondary" type="button" data-bind="click: $parent.addDurationValue"><i class="fa fa-plus-circle"></i> Add</button>
<!-- /ko -->
<!-- ko if: ($index() > 0)-->
<button class="btn btn-outline-secondary" type="button" data-bind="click: $parent.removeDurationValue"><i class="fa fa-trash"></i> Remove</button>
<!-- /ko -->
</span>
</div>
<span class="field-validation-valid invalid-feedback" data-bind="attr: { 'data-valmsg-for': 'Values[' + $index() + '].Value' }" data-valmsg-replace="true"></span>
</div>
<!-- /ko -->
Upvotes: 2