简体   繁体   中英

How to obtain input from multiple textareas and radio buttons on the click of a button using knockout?

I have several textareas and radio buttons that are dynamically generated inside a ko: foreach binding. When a user decides they are done typing their input, they will click a button 'OK' which takes all of the input of the textareas and values of the radio buttons they have chosen and it should trigger an ajax call to the server because the input needs to be stored in a database. Each textarea and radiobutton value is stored separately, so they need to be sent to the server as distinguished values. I've been having a lot of trouble given the foreach binding, so I'm not sure where to begin because the textareas do not have an accessible unique identifier because they're generated using foreach . Thank you.

So, in simpler terms:

  1. User types in multiple textareas and clicks on radio buttons.
  2. User clicks 'OK' button when finished typing. Button click function retrieves user input in all textareas and radio button values.
  3. Trigger ajax call and send values to server.

Any help is appreciated or links to any resources that I can follow. My google searches didn't quite answer my questions.

Picture: 在此处输入图片说明

Server:

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

        }

Example Object that is posted via ajax:

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

View:

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

View Model:

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

Generating the textareas in a foreach is not a problem and they don't need unique identifiers.

I am not sure what:

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

does but assuming it generates an array of Projects, the foreach is fine. I did notice not all of your textareas have value bindings which would cause them not to be populated with values.

I did see this binding:$parent.saved_value. In this case $parent would be ViewModel and that is not defined.

I was getting errors with the foreach until I changed the:

<textarea/>

to:

<textarea></textarea>

knockout was throwing an error stating the foreach was not closed.

I suspect you are approaching this from a "jQuery mindset", because you do things like this:

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

It looks to me like you're try to set unique IDs and names, in the hope of reading the val() of the input with jQuery when it's time to submit. While I don't know what your textInput binding is, it seems that doneProjectComment is not an observable on your Project data model, and thus the data entered in that <textarea> doesn't really end up anywhere.

The Knockout way is to add a doneProjectComment observable on your Project , and bind to it with the value binding:

<textarea class="form-control newSessionAnalyst" data-bind="value: doneProjectComment"/>

function Project(proj) {
  self.doneProjectComment = ko.observable();
}

Knockout gives you two-way bindings. In case of the value: doneProjectComment binding, this means that if the user types into the textarea, the value is saved into doneProjectComment . If doneProjectComment is changed, the new value is shown in the textarea.

So if you want to gather all the data when the user clicks 'ok', you can do it easily from inside the Project - just read all the observables you care about, and create a hash that jQuery.post can user as AJAX parameters:

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

html:

If you setup a save function on your ViewModel

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

html:

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

I would suggest installing Knockout Context Debugger it makes debugging knockout problems a lot easier.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM