我已经设法根据 Niemeyer 给出的答案创建了一个简单的向导。这工作正常。我想添加验证。我设法在 Firstname 字段上添加了必需的验证。将此留空将显示错误。但我无法成功的是:在当前步骤中验证模型,并根据是否有错误启用或禁用下一步。如果启用或禁用下一个按钮太难,那没关系。当出现错误时,我也可以在没有禁用按钮的情况下生活。只要在出现错误时阻止用户进行下一步。
. 我的观点是这样的:
//model is retrieved from server model
<script type="text/javascript">
var serverViewModel = @Html.Raw(Json.Encode(Model));
</script>
<h2>Test with wizard using Knockout.js</h2>
<div data-bind="template: { name: 'currentTmpl', data: currentStep }"></div>
<hr/>
<button data-bind="click: goPrevious, enable: canGoPrevious">Previous</button>
<button data-bind="click: goNext, enable: canGoNext">Next</button>
<script id="currentTmpl" type="text/html">
<h2 data-bind="text: name"></h2>
<div data-bind="template: { name: getTemplate, data: model }"></div>
</script>
<script id="nameTmpl" type="text/html">
<fieldset>
<legend>Naamgegevens</legend>
<p data-bind="css: { error: FirstName.hasError }">
@Html.LabelFor(model => model.FirstName)
@Html.TextBoxFor(model => model.FirstName, new { data_bind = "value: FirstName, valueUpdate: 'afterkeydown'"})
<span data-bind='visible: FirstName.hasError, text: FirstName.validationMessage'> </span>
</p>
@Html.LabelFor(model => model.LastName)
@Html.TextBoxFor(model => model.LastName, new { data_bind = "value: LastName" })
</fieldset>
</script>
<script id="addressTmpl" type="text/html">
<fieldset>
<legend>Adresgegevens</legend>
@Html.LabelFor(model => model.Address)
@Html.TextBoxFor(model => model.Address, new { data_bind = "value: Address" })
@Html.LabelFor(model => model.PostalCode)
@Html.TextBoxFor(model => model.PostalCode, new { data_bind = "value: PostalCode" })
@Html.LabelFor(model => model.City)
@Html.TextBoxFor(model => model.City, new { data_bind = "value: City" })
</fieldset>
</script>
<script id="confirmTmpl" type="text/html">
<fieldset>
<legend>Naamgegevens</legend>
@Html.LabelFor(model => model.FirstName)
<b><span data-bind="text:NameModel.FirstName"></span></b>
<br/>
@Html.LabelFor(model => model.LastName)
<b><span data-bind="text:NameModel.LastName"></span></b>
</fieldset>
<fieldset>
<legend>Adresgegevens</legend>
@Html.LabelFor(model => model.Address)
<b><span data-bind="text:AddressModel.Address"></span></b>
<br/>
@Html.LabelFor(model => model.PostalCode)
<b><span data-bind="text:AddressModel.PostalCode"></span></b>
<br/>
@Html.LabelFor(model => model.City)
<b><span data-bind="text:AddressModel.City"></span></b>
</fieldset>
<button data-bind="click: confirm">Confirm</button>
</script>
<script type='text/javascript'>
$(function() {
if (typeof(ViewModel) != "undefined") {
ko.applyBindings(new ViewModel(serverViewModel));
} else {
alert("Wizard not defined!");
}
});
</script>
knockout.js 实现如下所示:
function Step(id, name, template, model) {
var self = this;
self.id = id;
self.name = ko.observable(name);
self.template = template;
self.model = ko.observable(model);
self.getTemplate = function() {
return self.template;
};
}
function ViewModel(model) {
var self = this;
self.nameModel = new NameModel(model);
self.addressModel = new AddressModel(model);
self.stepModels = ko.observableArray([
new Step(1, "Step1", "nameTmpl", self.nameModel),
new Step(2, "Step2", "addressTmpl", self.addressModel),
new Step(3, "Confirmation", "confirmTmpl", {NameModel: self.nameModel, AddressModel:self.addressModel})]);
self.currentStep = ko.observable(self.stepModels()[0]);
self.currentIndex = ko.dependentObservable(function() {
return self.stepModels.indexOf(self.currentStep());
});
self.getTemplate = function(data) {
return self.currentStep().template();
};
self.canGoNext = ko.dependentObservable(function () {
return self.currentIndex() < self.stepModels().length - 1;
});
self.goNext = function() {
if (self.canGoNext()) {
self.currentStep(self.stepModels()[self.currentIndex() + 1]);
}
};
self.canGoPrevious = ko.dependentObservable(function() {
return self.currentIndex() > 0;
});
self.goPrevious = function() {
if (self.canGoPrevious()) {
self.currentStep(self.stepModels()[self.currentIndex() - 1]);
}
};
}
NameModel = function (model) {
var self = this;
//Observables
self.FirstName = ko.observable(model.FirstName).extend({ required: "Please enter a first name" });;
self.LastName = ko.observable(model.LastName);
return self;
};
AddressModel = function(model) {
var self = this;
//Observables
self.Address = ko.observable(model.Address);
self.PostalCode = ko.observable(model.PostalCode);
self.City = ko.observable(model.City);
return self;
};
我已经为 Firstname 字段中使用的所需验证添加了一个扩展器:
ko.extenders.required = function(target, overrideMessage) {
//add some sub-observables to our observable
target.hasError = ko.observable();
target.validationMessage = ko.observable();
//define a function to do validation
function validate(newValue) {
target.hasError(newValue ? false : true);
target.validationMessage(newValue ? "" : overrideMessage || "This field is required");
}
//initial validation
validate(target());
//validate whenever the value changes
target.subscribe(validate);
//return the original observable
return target;
};