我计划使用Knockout.js wizard validation on each step的修改版,以提供一个类似的逐步向导。
在PaymentModel视图模型步骤中,它具有两个子视图模型(payment1Model和payment2Model),显示的子视图模型将取决于selectedPaymentOption(option1或option2)。
我在PaymentModel上使用以下代码在选定的视图模型之间进行切换
self.selectedPaymentOption.subscribe(function(newValue) {
if(typeof newValue == "undefined")
return;
var found = ko.utils.arrayFirst(self.options(), function (item) {
return item.name() == newValue;
}, this);
self.selectedTemplate(found.template);
self.selectedModel(found.model());
},self);
第一次可以在模板之间切换,但是切换回来时出现空引用错误,并且下面的绑定失败。
我怀疑这与在
selectedTemplate
之前设置selectedModel
有关(反之亦然),并且由于模板使用<div data-bind="template: { name: selectedTemplate, data: selectedModel }"></div>
,因此一种尝试不进行其他绑定(鸡肉和鸡蛋情况)。有没有什么方法可以推迟更新,直到都同时选择了selectedTemplate和selectedModel,还是有更好的方法来解决此问题?
My fiddle is here和以下代码:
<div data-bind="template: { name: 'currentTmpl', data: currentStep }"></div>
<button data-bind="click: goPrevious, visible: canGoPrevious">Previous</button>
<button data-bind="click: goNext, visible: canGoNext, enable: modelIsValid">Next</button>
<script id="payment1Tmpl" type="text/html">
<p>payment option 1</p>
<input id="details1" type="text" data-bind="value: details1, valueUpdate: 'afterkeydown'" />
<select id="expiresMonth" data-bind="options: expiresMonthOptions, value: expiresMonth" />
<select id="expiresYear" data-bind="options: expiresYearOptions, value: expiresYear" />
</script>
<script id="payment2Tmpl" type="text/html">
<p>payment option 2</p>
<input id="details2" type="text" data-bind="value: details2, valueUpdate: 'afterkeydown'" />
</script>
<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>Name</legend>
<p>
<label for"FirstName">First Name</label>
<input id="FirstName" type="text" data-bind="value: firstName, valueUpdate: 'afterkeydown'" />
<p data-bind="validationMessage: firstName"></p>
</p>
</fieldset>
</script>
<script id="paymentTmpl" type="text/html">
<fieldset>
<p>
<label for"someOtherDetail">Some other detail</label>
<input id="someOtherDetail" type="text" data-bind="value: someOtherDetail, valueUpdate: 'afterkeydown'" />
</p>
<p>
<select data-bind="options: paymentOptions, value: selectedPaymentOption"></select>
<div data-bind="template: { name: selectedTemplate, data: selectedModel }"></div>
</p>
</fieldset>
</script>
<script id="confirmTmpl" type="text/html">
<fieldset>
<legend>Name</legend>
<b><span data-bind="text:NameModel.firstName"></span></b>
<br/>
</fieldset>
<button data-bind="click: confirm">Confirm</button>
</script>
ko.validation.configure({
insertMessages: false,
decorateElement: true,
errorElementClass: 'error'
});
function TemplatePage(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() {
var self = this;
self.nameModel = ko.observable(new NameModel());
self.paymentModel = ko.observable(new PaymentModel());
var confirmModel = {
NameModel: self.nameModel(),
PaymentModel: self.paymentModel()
};
self.stepModels = ko.observableArray([
new TemplatePage(1, "Step1", "nameTmpl", self.nameModel()),
new TemplatePage(2, "Step2", "paymentTmpl", self.paymentModel()),
new TemplatePage(3, "Confirmation", "confirmTmpl", confirmModel)
]);
self.currentStep = ko.observable(self.stepModels()[0]);
self.currentIndex = ko.computed(function () {
return self.stepModels.indexOf(self.currentStep());
});
self.getTemplate = function (data) {
return self.currentStep().template();
};
self.canGoNext = ko.computed(function () {
return (self.currentIndex() < (self.stepModels().length - 1));
});
self.modelIsValid = ko.computed(function () {
if (typeof(self.currentStep().model().isValid) != "undefined") {
return self.currentStep().model().isValid();
}
else
return true;
});
self.goNext = function () {
if (self.currentIndex() < self.stepModels().length - 1) {
var count = self.currentIndex() + 1;
console.log(count);
self.currentStep(self.stepModels()[count]);
}
};
self.canGoPrevious = ko.computed(function () {
return self.currentIndex() > 0;
});
self.goPrevious = function () {
if (self.currentIndex() > 0) {
var count = self.currentIndex() - 1;
console.log(count);
self.currentStep(self.stepModels()[count]);
}
};
}
Payment1Model = function() {
var self = this;
self.details1 = ko.observable().extend({
required: true
});
self.expiresMonthOptions = ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"];
self.expiresYearOptions = ["2013", "2014", "2015", "2016"];
self.expiresMonth = ko.observable().extend({
required: true
});
self.expiresYear = ko.observable().extend({
required: true
});
ko.validation.group(self);
}
Payment2Model = function() {
var self = this;
self.details2 = ko.observable().extend({
required: true
});
ko.validation.group(self);
}
NameModel = function(model) {
var self = this;
//Observables
self.firstName = ko.observable().extend({
required: true
});
ko.validation.group(self);
return self;
};
var PaymentModel=function() {
var self = this;
self.payment1Model = ko.observable(new Payment1Model());
self.payment2Model = ko.observable(new Payment2Model());
self.someOtherDetail = ko.observable().extend({
required: true
});
self.options = ko.observableArray([
new TemplatePage(1, "Payment1", "payment1Tmpl", self.payment1Model()),
new TemplatePage(1, "Payment2", "payment2Tmpl", self.payment2Model()),
]);
var optionNames = [];
for(var i=0; i<self.options().length; i++)
{
optionNames.push(self.options()[i].name);
}
self.paymentOptions = optionNames;
self.selectedPaymentOption = ko.observable(optionNames[0]);
self.selectedTemplate = ko.observable();
self.selectedModel = ko.observable();
self.selectedPaymentOption.subscribe(function(newValue) {
if(typeof newValue == "undefined")
return;
var found = ko.utils.arrayFirst(self.options(), function (item) {
return item.name() == newValue;
}, this);
self.selectedTemplate(found.template);
self.selectedModel(found.model());
},self);
ko.validation.group(self);
self.isParentAndChildValid = function () {
if (typeof self.selectedModel() == "undefined")
return false;
return self.isValid() && self.selectedModel().isValid();
};
return {
someOtherDetail: self.someOtherDetail,
selectedPaymentOption: self.selectedPaymentOption,
paymentOptions: self.paymentOptions,
selectedTemplate: self.selectedTemplate,
selectedModel: self.selectedModel,
isValid: self.isParentAndChildValid
}
}
ko.applyBindings(new ViewModel());
最佳答案
Here is工作解决方案。现在它可以正常工作了。
您需要做的是停止在分离的变量旁边使用:
self.selectedTemplate = ko.observable();
self.selectedModel = ko.observable();
相反,您使用了另一个变量,该变量将保留options数组中的选定选项:
self.selectedTemplate = ko.observable(self.options()[0]);
每次更改付款方式时,都会在“选项”数组中找到选定的选项。只需将其放入新变量self.selectedTemplate中即可:
var found = ko.utils.arrayFirst(self.options(), function (item) {
return item.name() == newValue;
}, this);
self.selectedTemplate(found);
相应地更改HTML:
<div data-bind="template: { name: selectedTemplate().template, data: selectedTemplate().model() }"></div>
(编辑)使用新的selectedTemplate()。template和selectedTemplate()。model()更改selectedTemplate和selectedModel的所有实例
关于knockout.js - 选定的模板切换,但选择不起作用-模型未定义,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17752038/