动态构建单选按钮视图,使用ko.observable检查绑定不起作用

时间:2022-12-03 07:35:05

This is my first question, pardon for any mistakes in posting the question.

这是我的第一个问题,请原谅发布问题时的任何错误。

My question might looks exactly similar to Build knockout model and view dynamically, radio buttons not being set

我的问题看起来可能与Build knockout模型完全相似,并且动态查看,未设置单选按钮

From my research, the above question comes very close to my problem, but still could not get a fix.

根据我的研究,上面的问题非常接近我的问题,但仍无法得到解决。

In my requirement, for each question there are multiple radio buttons as choices and also there are prev and next navigation buttons. The question and choices are dynamically built, the choice the user has made on the question is not being captured in the viewmodel property self.current_question.response.

在我的要求中,对于每个问题,有多个单选按钮作为选项,还有prev和下一个导航按钮。问题和选择是动态构建的,用户在问题上做出的选择未在viewmodel属性self.current_question.response中捕获。

Below is snippet of my view

以下是我的观点片段

<div id="questionpage" data-role="page" data-theme="c">

    <div data-role="header" data-theme="c">
      <h1>Question <span data-bind="text: current_question().queNo"></span></h1>
    </div><!-- /header -->

    <!-- inputs -->
    <div id="inputs" data-role="content" data-theme="c">    
      <a id="prevbtn" href="#question" data-icon="arrow-l" data-bind="click: $root.prev, visible: hasPrev()" data-role="button" data-inline="true">Prev</a>
      <a id="nextbtn" href="#question" data-icon="arrow-r" data-bind="click: $root.next, visible: hasNext()" data-role="button" data-inline="true">Next</a>     
      <a id="completebtn" href="#question" data-icon="check" data-bind="click: $root.submit, visible: !hasNext()" data-role="button" data-inline="true">Submit</a>

      <p data-bind="text: current_question().queText"></p>
      <div id="inputdiv">
      </div>

    </div><!-- /content -->    
</div><!-- /page -->

Below is my snippet of my Viewmodel

下面是我的Viewmodel片段

// Question response object

    function QuestionResponse(stuId, examQueId,selAns)
    {
        var self=this;
        self.Student__c=stuId;
        self.Exam_Question__c=examQueId;
        self.Selected_Answer__c=selAns;
    }

    // Question object
    function Question()
    { 
        var self=this;
        self.response=ko.observable('');
        self.options=ko.observableArray();
    }

    function Question(id, queNo, queText, response, queChA, queChB, queChC, queChD, queChE)
    {
        var self=this;
        self.id=id;
        self.queNo=queNo;
        self.queText=queText;
        self.response=ko.observable(response);
        self.options=ko.observableArray();
        //self.checked=ko.observableArray();

        // helper function to add an option
        self.addOption = function(option)
        {
            if (null!=option)
            {
                self.options().push(option);
            }
        }

        self.addOption(queChA);
        self.addOption(queChB);
        self.addOption(queChC);
        self.addOption(queChD);
        self.addOption(queChE);
    }

    function ExamViewModel()
    {
        var self=this;

        // initialise variables to default observables
        // self.question_response=ko.observable(new QuestionResponse());
        self.current_question=ko.observable(new Question());
        self.current_question_no=0;
        self.email=ko.observable('Your email id');
        self.questions=ko.observableArray();
        self.studentId;

        self.errorMsg=ko.observable('');
        self.completeMsg=ko.observable('');
        self.hasPrevious=ko.observable();
        self.hasNext=ko.observable(false);
        self.hasPrev=ko.observable(false);

        // function executed when the user clicks the 'next' button
        self.next=function()
        {
            $.mobile.loading( 'show', { theme: "a", text: "Loading", textVisible: true });

            // advance to the next question
            self.current_question_no++;
            self.current_question(self.questions()[self.current_question_no]());

            // setup previous/next buttons
            self.hasPrev(true);
            self.hasNext(self.current_question_no<self.questions().length-1);

            // add the input markup
            self.addInput();
            $.mobile.loading('hide');
        }

        // function executed when the user clicks the 'prev' button - could refactor
        // some of this and the next function into a common helper
        self.prev=function()
        {
            $.mobile.loading( 'show', { theme: "a", text: "Loading", textVisible: true });
            //alert('in prev '+self.current_question().response);
            self.current_question_no--;
            self.current_question(self.questions()[self.current_question_no]());
            self.hasPrev(self.current_question_no!=0);
            self.hasNext(true);
            self.addInput();
            $.mobile.loading('hide');
        }

        // callback for the JavaScript remoting call    
        self.responseCB=function(record, event)
        {
            if ( (!event) || (event.status) ) 
            {
                $.each(record,
                        function()
                        {
                            var question=new ko.observable(new Question(
                                                            this.Id, this.Exam_Question_Number__c, this.Question__r.Question_Text__c,'', 
                                                            this.Question__r.ChoiceA__c, this.Question__r.ChoiceB__c, this.Question__r.ChoiceC__c,
                                                            this.Question__r.ChoiceD__c,this.Question__r.ChoiceE__c));

                            self.questions.push(question);

                            // if the current question is empty, set it to this question 
                            if (typeof self.current_question().queText === 'undefined')
                            {
                                self.current_question(question());
                            }
                        });

                // setup whether to display the next button                             
                if (self.questions().length>1)
                {
                    self.hasNext(true);
                }

                // add the input markup
                self.addInput();
            }
            else if (event.type === 'exception')
            {
                self.error(event.message);
            }
        }

        // add the appropriate input markup
        self.addInput=function()
        {
            self.addRadioInput();

        }


        self.addRadioInput=function()
        {
            $('#inputdiv').html(
                '<div id="radio" data-bind="value: $root.current_question.response">' +
                '   <fieldset id="radiocontrol" data-role="controlgroup">' + 
                '       <' +  // Visualforce will break the comment that encloses the containerless element, so split over two lines
                '!-- ko foreach: current_question().options --> ' +
                '       <input name="radio-options" type="radio" data-bind="value: $data, checked: $root.current_question.response, attr: {id:\'rb_\' + $index() }"></input> ' +
                '       <label data-bind="attr:{for : \'rb_\' + $index() }, text: $data"></label> ' + 
                '       <' + 
                '!-- /ko --> ' +
                '   </fieldset> ' +
                '</div> ');

            // bind the new markup to the viewmodel
            ko.applyBindings(viewModel, $('#radio')[0]);

            // trigger JQM to enhance the newly created radio button div
            $('#radio').trigger('create');
        }


    }

Have tried multiple options, but could not get what's wrong with my code. Any help is greatly appreciated.

尝试了多种选择,但无法解决我的代码有什么问题。任何帮助是极大的赞赏。

1 个解决方案

#1


0  

Your div#inputdiv should look something like this, which will handle displaying radio buttons appropriately so that you don't have to resort to injecting DOM elements with jQuery. My logic might not be exactly what you're looking for, but you can adjust as appropriate.

您的div #inputdiv应该看起来像这样,它将适当地处理显示单选按钮,这样您就不必使用jQuery注入DOM元素。我的逻辑可能并不完全符合您的要求,但您可以根据需要进行调整。

The with binding will only render its contents if its variable is populated. Variables in the div will refer to the variable's members. The jqm binding is a custom binding handler you would need to write (or find).

with binding仅在填充其变量时才呈现其内容。 div中的变量将引用变量的成员。 jqm绑定是您需要编写(或查找)的自定义绑定处理程序。

<div id="inputdiv" data-bind="with:$root.current_question">
  <div id="radio" data-bind="jqm: true">
    <fieldset id="radiocontrol" data-role="controlgroup" data-bind="foreach:options">
      <input name="radio-options" type="radio" data-bind="value: $data, checked: response, attr: {id:'rb_' + $index() }" />
      <label data-bind="attr:{for: 'rb_' + $index() }, text: $data"></label>
    </fieldset>
  </div>
</div>

Your jqm binding handler might be as simple as this:

你的jqm绑定处理程序可能就像这样简单:

ko.bindingHandlers.jqm = {
  init: function (el) {
    $(el).create();
  }
};

That's at least worth a shot. Also take a look at this article, and maybe google around for jqm and knockout.

这至少值得一试。另外看看这篇文章,也许谷歌周围的jqm和淘汰赛。

#1


0  

Your div#inputdiv should look something like this, which will handle displaying radio buttons appropriately so that you don't have to resort to injecting DOM elements with jQuery. My logic might not be exactly what you're looking for, but you can adjust as appropriate.

您的div #inputdiv应该看起来像这样,它将适当地处理显示单选按钮,这样您就不必使用jQuery注入DOM元素。我的逻辑可能并不完全符合您的要求,但您可以根据需要进行调整。

The with binding will only render its contents if its variable is populated. Variables in the div will refer to the variable's members. The jqm binding is a custom binding handler you would need to write (or find).

with binding仅在填充其变量时才呈现其内容。 div中的变量将引用变量的成员。 jqm绑定是您需要编写(或查找)的自定义绑定处理程序。

<div id="inputdiv" data-bind="with:$root.current_question">
  <div id="radio" data-bind="jqm: true">
    <fieldset id="radiocontrol" data-role="controlgroup" data-bind="foreach:options">
      <input name="radio-options" type="radio" data-bind="value: $data, checked: response, attr: {id:'rb_' + $index() }" />
      <label data-bind="attr:{for: 'rb_' + $index() }, text: $data"></label>
    </fieldset>
  </div>
</div>

Your jqm binding handler might be as simple as this:

你的jqm绑定处理程序可能就像这样简单:

ko.bindingHandlers.jqm = {
  init: function (el) {
    $(el).create();
  }
};

That's at least worth a shot. Also take a look at this article, and maybe google around for jqm and knockout.

这至少值得一试。另外看看这篇文章,也许谷歌周围的jqm和淘汰赛。