【问题标题】:How to obtain input from multiple textareas and radio buttons on the click of a button using knockout?如何使用敲除单击按钮从多个文本区域和单选按钮获取输入?
【发布时间】:2015-01-23 21:18:42
【问题描述】:

我有几个在ko: foreach 绑定中动态生成的文本区域和单选按钮。当用户决定完成输入时,他们将单击“确定”按钮,该按钮将获取所有文本区域的输入和他们选择的单选按钮的值,并且它应该触发对服务器的 ajax 调用,因为输入需要存储在数据库中。每个 textarea 和 radiobutton 的值是分开存储的,因此需要将它们作为区分值发送到服务器。考虑到foreach 绑定,我遇到了很多麻烦,所以我不确定从哪里开始,因为文本区域没有可访问的唯一标识符,因为它们是使用foreach 生成的。谢谢。

所以,简单来说:

  1. 用户在多个文本区域中键入并单击单选按钮。
  2. 用户在完成输入后单击“确定”按钮。按钮单击功能检索所有文本区域和单选按钮值中的用户输入。
  3. 触发 ajax 调用并将值发送到服务器。

感谢任何帮助或链接到我可以关注的任何资源。我的谷歌搜索并没有完全回答我的问题。

图片:

服务器:

//retrieves form data from the client and serialized it
        if (Request.HttpMethod == "POST")
        {            
            // get json out of body
            var serializer = new JsonSerializer();
            var sr = new StreamReader(Request.InputStream);
            var jtr = new JsonTextReader(sr);
            dynamic data = serializer.Deserialize(jtr);

            if (data.action == "getProjects")
            {
                getProjects(data);
            }

        }

通过 ajax 发布的示例对象:

    Obj = {};
    Obj.action = "getProjects";
    Obj.list = arrayOfCheckboxValues;

查看:

        <!-- ko foreach: projects -->
        <div id="eachOppyProject">
            <table>
                <tbody>
                    <tr>
                        <td><a data-bind="attr: { href: '/tools/oppy/' + guid }"><span class="link" data-bind="value: guid, text: name"></span></a></td>
                    </tr>
                    <tr data-bind="text: projectDescription"></tr>
                </tbody>
            </table>
            <div class="btn-group" data-toggle="buttons">
                <label class="btn btn-default" data-bind="visible: firstAnswers, click: function () { showBtnOK(); showDoneTA(); }">
                    <input type="radio" class="btn btn-default" />
                    Did it already
                </label>
                <label class="btn btn-default" data-bind="visible: firstAnswers, click: function () { showInterestedTA(); showBtnOK(); }">
                    <input type="radio" class="btn btn-default" />Didn't do it, but interested</label>
                <label class="btn btn-default" data-bind="visible: firstAnswers, click: function () { neverInterested(); showBtnOK(); }">
                    <input type="radio" class="btn btn-default" />Never done it; not interested</label>
            </div>

            <div data-bind="visible: doneAnswer">
                <textarea placeholder="Tell us a little of what you've done. Like, when did you do it? Who was in charge of it? Things like that."
                    class="form-control newSessionAnalyst" data-bind="textInput: doneProject, attr: { id: guid, name: guid + 'doneProject' }"
                    />
                <textarea placeholder="If there's anything else you'd like us to know, tell us here."
                    class="form-control newSessionAnalyst" data-bind="textInput: doneProjectComment, attr: { id: guid, name: guid + 'doneProjectComment' }"/>
            </div>

            <div data-bind="visible: interestedAnswer">
                <textarea placeholder="So, you're interested, huh? Tell us why."
                    class="form-control newSessionAnalyst" data-bind="attr: { id: guid, name: guid + 'interestedProject' }"/>
                <textarea placeholder="If there's anything else you'd like us to know, tell us here."
                    class="form-control newSessionAnalyst" data-bind="attr: { id: guid, name: guid + 'interestedProjectComment' }"/>
            </div>


            <div class="btn-group" data-toggle="buttons">
                <label class="btn btn-default" data-bind="visible: doneAnswer, click: function () { showBtnOK(); showInterestedMoreTA(); }">

                    <input type="radio" class="btn btn-default" />
                    Interested in doing more</label>
                <label class="btn btn-default" data-bind="visible: doneAnswer, click: function () { showBtnOK(); notInterestedMore(); }">

                    <input type="radio" class="btn btn-default" />
                    No plans to do this again</label>
            </div>

            <div data-bind="visible: interestedMore">
                <textarea placeholder="You want to do more? Way to go! Tell us more!"
                    class="form-control newSessionAnalyst" data-bind="attr: { id: guid, name: guid + 'interestedMore' }, value: $parent.saved_value"/>
                <textarea placeholder="If there's anything else you'd like us to know, tell us here."
                    class="form-control newSessionAnalyst" data-bind="attr: { id: guid, name: guid + 'interestedMoreComment' }"/>
            </div>
            <div style="text-align: right;">

            <input type="button" data-bind="visible: btnOK, click: function () { clearView(); }" value="OK" class="btn btn-default "/><br /><br />
        </div> //this is the button that captures all input

        </div>


        <!-- /ko -->

查看模型:

function ViewModel(proj) {
    var self = this;
    var wrappedProjects = proj.map(function (p) {
        return new Project(p);
    });
    self.projects = ko.observableArray(wrappedProjects);
}


function Project(proj) {
    var self = proj;
    self.firstAnswers = ko.observable(true);
    self.doneAnswer = ko.observable(false);
    self.showDoneTA = function () {
        self.doneAnswer(true);
        self.interestedAnswer(false);
    }
    self.interestedAnswer = ko.observable(false);
    self.showInterestedTA = function () {
        self.interestedAnswer(true);
        self.doneAnswer(false);
        self.interestedMore(false);
    }
    self.interestedMore = ko.observable(false);
    self.showInterestedMoreTA = function () {
        self.interestedMore(true);
    }
    self.notInterestedMore = function () {
        self.interestedMore(false);
    }
    self.neverInterested = function () {
        self.doneAnswer(false);
        self.interestedAnswer(false);
        self.interestedMore(false);
    }
    self.btnOK = ko.observable(false);
    self.showBtnOK = function () {
        self.btnOK(true);
        console.log(self.btnOK());
    }
    self.savedMSG = ko.observable(false);
    self.clearView = function () {
        self.firstAnswers(false);
        self.doneAnswer(false);
        self.interestedAnswer(false);
        self.interestedMore(false);
        self.btnOK(false);
        self.savedMSG(true);
    }
    self.showFirstAnswers = function () {
        self.firstAnswers(true);
        self.savedMSG(false);
    }

    return self;
}

【问题讨论】:

    标签: javascript jquery html knockout.js textarea


    【解决方案1】:

    在 foreach 中生成文本区域不是问题,它们不需要唯一标识符。

    我不确定是什么:

    var wrappedProjects = proj.map(function (p) {
            return new Project(p);
        });
    

    确实,但假设它生成了一个项目数组,那么 foreach 就可以了。 我确实注意到并非所有文本区域都具有值绑定,这会导致它们不填充值。

    我确实看到了这个绑定:$parent.saved_value。在这种情况下,$parent 将是 ViewModel 并且没有定义。

    我在更改 foreach 之前遇到了错误:

    <textarea/>
    

    到:

    <textarea></textarea>
    

    knockout 抛出一个错误,说明 foreach 没有关闭。

    我怀疑您是从“jQuery 思维方式”来解决这个问题的,因为您会这样做:

    <textarea class="form-control newSessionAnalyst" data-bind="textInput: doneProjectComment, attr: { id: guid, name: guid + 'doneProjectComment' }"/>
    

    在我看来,您正在尝试设置唯一的 ID 和名称,希望在提交时使用 jQuery 读取输入的 val()。虽然我不知道您的textInput 绑定是什么,但似乎doneProjectComment 在您的Project 数据模型上不是可观察的,因此在&lt;textarea&gt; 中输入的数据并没有真正结束。

    Knockout 方法是在您的 Project 上添加一个 doneProjectComment observable,并使用 value 绑定对其进行绑定:

    <textarea class="form-control newSessionAnalyst" data-bind="value: doneProjectComment"/>
    
    function Project(proj) {
      self.doneProjectComment = ko.observable();
    }
    

    Knockout 为您提供双向绑定。在 value: doneProjectComment 绑定的情况下,这意味着如果用户在 textarea 中键入,则该值将保存到 doneProjectComment。如果doneProjectComment 被更改,新值会显示在文本区域中。

    因此,如果您想在用户单击“确定”时收集所有数据,您可以从 Project 内部轻松完成 - 只需读取您关心的所有 observables,并创建一个 jQuery.post 可以的哈希用户作为 AJAX 参数:

    function Project(proj) {
      self.toAjaxParameters = function(){
        return {
          interested: self.interestedAnswer(),
          done: self.doneAnswer(),
          ...
        }
      }
    }
    

    html:

    如果您在 ViewModel 上设置 save 函数

    self.save = function(project){
      $.post('/url-to-post-to', project.toAjaxParameters());
    }
    

    html:

    <button data-bind="click:$parent.save"></button>
    

    我建议安装Knockout Context Debugger,它使调试淘汰赛问题变得容易得多。

    【讨论】:

    • 嗨。谢谢你的详尽评论!! proj.map 是我用来隐藏/显示文本区域和其他控件的解决方案。我更新了我的帖子,添加了一张图片,向您展示了一个显示一些控件的项目,而它下面的项目具有隐藏的控件。您将如何从selfProject 中获取值?另外,是否可以在函数内部触发 ajax 调用?因为我必须将捕获的数据发送到服务器。否则,我想我理解你的建议,谢谢!
    • @SKY GrayTower 是对的——在视图模型上创建一个save 函数,并让project 数据模型进行数据收集。 @GrayTower 我会扩展你的答案,而不是写一个新的——希望没关系。
    • @janfoeh 谢谢。您在哪里有&amp;.post('url'),我该如何指定操作?我目前没有直接发布到方法,即:url/method.我指定了一个 object.action = 'methodName' 因为我正在发布表单数据。我更新了我的帖子,向您展示服务器上发生的情况。
    • @SKY 我不确定您所说的“object.action”是什么意思,但我怀疑您在谈论表单构建器的后端实现。我无法帮助你,但从前端的角度来看,它只是 URL——你必须弄清楚它是哪个 URL。 $.post 也可以发布表单数据 - 仅通过 AJAX。您不需要 &lt;form&gt; 元素。视图模型save 函数中的project 参数来自click:$parent.save 绑定。 click 将当前的$data 范围传递给$parent.save,因此您可以在$data 中获得由ko foreach: projects 设置的当前项目。
    • @SKY &lt;form action="/my-url"&gt; 等于 $.post('/my-url') 的 jQuery AJAX 调用。两者都定义了一个将向其发出 HTTP POST 请求的 URL。不同之处在于如何定义将与 POST 请求一起发送的数据。而在 HTML 中,您可以定义 &lt;input type="text" name="myvalue1"&gt;&lt;input type="text" name="myvalue2"&gt;,而对于 AJAX POST 请求,您只需将值作为对象哈希传递:$.post('/my-url', {myvalue1: 'a', myvalue2: 'b'})
    猜你喜欢
    • 1970-01-01
    • 2014-02-27
    • 2016-10-30
    • 2015-09-24
    • 2017-02-05
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    相关资源
    最近更新 更多