【问题标题】:Build radio button view dynamically, checked binding is not working using ko.observable动态构建单选按钮视图,检查绑定无法使用 ko.observable
【发布时间】:2016-03-18 03:49:20
【问题描述】:

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

我的问题可能看起来与 Build knockout model and view dynamically, radio buttons not being set

根据我的研究,上述问题与我的问题非常接近,但仍然无法解决。

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

以下是我的观点sn-p

<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 -->

下面是我的 Viewmodel 的 sn-p

// 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');
        }


    }

尝试了多个选项,但无法了解我的代码有什么问题。非常感谢任何帮助。

【问题讨论】:

  • 问题是您在视图模型中使用了 jQuery,这意味着您没有正确地建模视图。如果没有看到您的单选按钮 HTML 和绑定,我们无法理解为什么您没有设置您选择的值。
  • 单选按钮 HTML 是在 viewmodel 函数 self.addRadioInput 中动态构建的,单选按钮插入的 HTML div 标签为&lt;div id="inputdiv"&gt; &lt;/div&gt;。如果我遗漏了什么,请告诉我。
  • 通过 jQuery 插入代码是错误的做法。 Knockout 可以处理动态创建元素。您在单选按钮上的绑定似乎适合设置 response
  • 为什么在div#radio 上有一个value 绑定?
  • 是的,value 不需要绑定div#radio,但我正在尝试您建议的方法。

标签: javascript jquery-mobile knockout.js radio-button


【解决方案1】:

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

with 绑定只有在填充了其变量时才会呈现其内容。 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>

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

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

这至少值得一试。还可以看看this article,也许可以用谷歌搜索 jqm 和淘汰赛。

【讨论】:

  • 感谢您的回答,您刚刚解决了我的问题。对于您问我为什么使用 jQuery 构建视图的第一个问题,这是因为我从研究中了解到 JQM 增强页面加载后将无法工作。现在,尽管响应已被捕获,但问题和选择已经失去了 JQM 增强功能。我认为您使用 jqm 自定义绑定的建议是为了解决同样的问题,因为我对自定义 ko 自定义绑定器了解甚少,您能否指出对我有帮助的最佳资源?
  • 谢谢 Roy,当然我需要一些关于 `jqm' 自定义绑定的帮助,但你已经解决了我最初问题中的问题,我接受你的作为最佳答案。
  • @Lakshmi 查看我更新的答案以获取有关 jqm 的一些指示。如果你不能让它工作,请再问一个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-05
  • 2021-05-20
  • 1970-01-01
  • 2020-04-10
  • 2021-02-22
  • 1970-01-01
相关资源
最近更新 更多