Knockout.js 选择模板切换,选择不工作-模型未定义

Knockout.js 选择模板切换,选择不工作-模型未定义,knockout.js,knockout-validation,Knockout.js,Knockout Validation,使用修改后的版本,我计划有一个类似的分步向导 在PaymentModel视图模型步骤中,它有两个子视图模型(payment1Model和payment2Model),显示的子视图模型将取决于所选PaymentOption(选项1或选项2) 我使用PaymentModel上的以下代码在所选视图模型之间切换 self.selectedPaymentOption.subscribe(function(newValue) { if(typeof newValue == "undefined")

使用修改后的版本,我计划有一个类似的分步向导

在PaymentModel视图模型步骤中,它有两个子视图模型(payment1Model和payment2Model),显示的子视图模型将取决于所选PaymentOption(选项1或选项2)

我使用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);
我可以第一次在模板之间切换OK,但是当切换回来时,我得到一个空引用错误,下面的绑定失败

我怀疑这与在
selectedModel
之前设置
selectedTemplate
有关(反之亦然),并且一个试图绑定另一个(鸡和蛋的情况),因为模板使用

是否有任何方法可以推迟更新,直到选择Template和selectedModel都已更新,或者有更好的方法解决此问题

和下面的代码:

<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());

以前的
下一个
付款选择1

付款选择2

名称 名字

其他一些细节

名称
证实 ko.validation.configure({ insertMessages:false, 装饰元素:是的, errorElementClass:'错误' }); 函数模板页面(id、名称、模板、模型){ var self=这个; self.id=id; self.name=ko.observable(名称); self.template=模板; self.model=ko.可观察(model); self.getTemplate=函数(){ 返回self.template; }; } 函数ViewModel(){ var self=这个; self.nameModel=ko.observable(新nameModel()); self.paymentModel=ko.observable(新paymentModel()); var确认模型={ NameModel:self.NameModel(), PaymentModel:self.PaymentModel() }; self.stepModels=ko.array([ 新模板页(1,“Step1”,“nameTmpl”,self.nameModel()), 新模板页(2,“Step2”,“paymentTmpl”,self.paymentModel()), 新模板页(3,“确认”,“确认TMPL”,确认模型) ]); self.currentStep=ko.observable(self.stepModels()[0]); self.currentIndex=ko.computed(函数(){ 返回self.stepModels.indexOf(self.currentStep()); }); self.getTemplate=函数(数据){ 返回self.currentStep().template(); }; self.canGoNext=ko.computed(函数(){ 返回(self.currentIndex()<(self.stepModels().length-1)); }); self.modelIsValid=ko.computed(函数(){ if(typeof(self.currentStep().model().isValid)!=“undefined”){ 返回self.currentStep().model().isValid(); } 其他的 返回true; }); self.goNext=函数(){ if(self.currentIndex()0; }); self.goPrevious=函数(){ if(self.currentIndex()>0){ var count=self.currentIndex()-1; 控制台日志(计数); self.currentStep(self.stepModels()[count]); } }; } Payment1Model=函数(){ var self=这个; self.details1=ko.observable().extend({ 必填项: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({ 必填项:true }); self.expiresYear=ko.observable().extend({ 必填项:true }); 验证组(自我); } Payment2Model=函数(){ var self=这个; self.details2=ko.observable().extend({ 必填项:true }); 验证组(自我); } 名称模型=函数(模型){ var self=这个; //可观测 self.firstName=ko.observable().extend({ 必填项:true }); 验证组(自我); 回归自我; }; var PaymentModel=函数(){ var self=这个; self.payment1Model=ko.observable(新payment1Model()); self.payment2Model=ko.observable(新payment2Model()); self.someOtherDetail=ko.observable().extend({ 必填项:true }); self.options=ko.array([ 新模板页(1,“Payment1”,“Payment1Templ”,self.payment1Model()), 新模板页(1,“Payment2”,“Payment2Templ”,self.payment2Model()), ]); var optionNames=[]; 对于(var i=0;i工作解决方案。现在它可以正常工作,没有任何错误。 您需要做的是停止使用分隔变量旁边的值:

self.selectedTemplate = ko.observable();
self.selectedModel = ko.observable();
而是使用另一个变量来保存选项数组中的选定选项:

 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()模板和selectedTemplate()模型()更改selectedTemplate和selectedModel的所有实例

非常感谢+1并准备奖励,只是一个简单的问题-为什么当我将付款方式从选择更改为单选按钮时,初始视图中是否未选择第一个选项,有什么想法吗?我已在此处更新了小提琴,您所做的是设置对自我的引用。选项()[I].name属性。相反,您需要断开它们的连接,并将self.selectedPaymentOption的第一个值设置为op