我已經設法根據Niemeyer給出的答案創建了一個簡單的嚮導。這工作正常。我想添加驗證。我已經設法在字段Firstname上添加必需的驗證字。將此項留空將顯示錯誤。但是我不能成功的是: 驗證當前步驟中的模型,並根據是否存在錯誤啓用或禁用該模型。如果啓用或禁用下一個按鈕太困難,那沒問題。出現錯誤時,我也可以在沒有禁用按鈕的情況下生活。只要用戶無法在發生錯誤時繼續下一步。Knockout.js嚮導驗證每個步驟
。我的看法是這樣的:
//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;
};
而且我已經添加了必要的驗證的擴展在該領域使用的名字:
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;
};
我可能會做得非常類似,但可能會把「Step」對象的通用計算看起來是否有任何模型屬性無效(當前代碼只會執行頂級道具)。我會避免將視圖模型綁定到視圖上,方法是查找具有特定類的元素,並在模型中保存邏輯'$('。validationMessage:visible')'。 http://jsfiddle.net/rniemeyer/MK39r/23/ – 2012-07-29 20:36:38
我喜歡這個解決方案!我曾想過在每個模型類中都使用'modelIsValid',但我認爲這同樣糟糕,甚至比我最終得到的還要糟糕。雖然我沒有想過在Step中定義它。我之前也沒有像JS那樣做過反射。偉大的東西......永遠感謝你的投入,瑞恩。 – jimmym715 2012-07-29 20:45:06
我已經通過使用knockout.validation解決了它。在我看來,我認爲這比查看元素是否可見更清晰。我會將你的答覆標記爲答案。如果有人有興趣,我可以稍後發佈使用knockout.validation的解決方案。 – Mounhim 2012-07-30 19:56:39