Search code examples
javascriptasp.net-mvcknockout.jsknockout-mvc

Unable to post the form data to controller


Model: I have a view with collection of addresses and names. I know i am missing something but not able to figure out. I am new to knockout trying to create some sample from using mvc. I see null values coming in the controller. Here is the fiddle: https://jsfiddle.net/iamsrk/9kf41vd7/32/

     public class PersonModel
        {
            public virtual ICollection<NameModel> NameModel { get; set; } = new HashSet<NameModel>();
            public virtual ICollection<AddressModel> AddressModel { get; set; } = new HashSet<AddressModel>();
        }

        public class NameModel
        {
            public string FirstName { get; set; }
            public string MiddleName { get; set; }
            public string LastName { get; set; }
            public string Gender { get; set; }
            public string Suffix { get; set; }
        }
        public class AddressModel
        {
            public string FirstName { get; set; }
            public string MiddleName { get; set; }
            public string LastName { get; set; }
            public string ReportingDate { get; set; }
            public string Gender { get; set; }
            public string Suffix { get; set; }
        }

Controller Action:

public ActionResult GetPerson(PersonModel personModel)
        {
            return null;
        }

View:

  <div class="row">
        <div class="col-md-12">
            <div class="panel panel-info">
                <div class="panel-heading">
                    <h1 class="panel-title">
                        Names
                    </h1>
                </div>
                <div data-bind="with: nameModel" class="panel-body">
                    <table class="table table-hover table-responsive">
                        <thead>
                            <tr>
                                <th>First</th>
                                <th>Middle</th>
                                <th>Last</th>
                                <th>Suffix</th>
                                <th>Gender</th>
                            </tr>
                        </thead>
                        <tbody data-bind="foreach: names">
                            <tr>
                                <td>
                                    <input class="form-control" data-bind="value: firstName" />
                                </td>
                                <td>
                                    <input class="form-control" data-bind="value: middleName" />
                                </td>
                                <td>
                                    <input class="form-control" data-bind="value: lastName" />
                                </td>
                                <td>
                                    <input class="form-control" data-bind="value: suffix" />
                                </td>
                                <td>
                                    <input class="form-control" data-bind="value: gender" />
                                </td>
                                <td style="vertical-align: inherit;"><a href="#" data-bind="click:$parent.removeName.bind($data)">Delete</a></td>
                            </tr>
                        </tbody>
                    </table>
                    <button class="btn btn-success btn-sm" data-bind="click: addName.bind($data)">Add Name</button>
                </div>
            </div>
        </div>
    </div>
    <div class="row">
        <div class="col-md-12">
            <div class="panel panel-info">
                <div class="panel-heading">
                    <h1 class="panel-title">
                        Addresses
                    </h1>
                </div>
                <div data-bind="with: addressModel" class="panel-body">
                    <table class="table table-hover table-responsive">
                        <thead>
                            <tr>
                                <th>street</th>
                                <th>city</th>
                                <th>state</th>
                                <th>country</th>
                            </tr>
                        </thead>
                        <tbody data-bind="foreach:names">
                            <tr>
                                <td>
                                    <input class="form-control" data-bind="value: street" />
                                </td>
                                <td>
                                    <input class="form-control" data-bind="value: city" />
                                </td>
                                <td>
                                    <input class="form-control" data-bind="value: state" />
                                </td>
                                <td>
                                    <input class="form-control" data-bind="value: country" />
                                </td>
                                <td style="vertical-align: inherit;"><a href="#" data-bind="click:$parent.removeAddress.bind($data)">Delete</a></td>
                            </tr>
                        </tbody>
                    </table>
                    <button class="btn btn-success btn-sm" data-bind="click: addAddress.bind($data)">Add Address</button>
                </div>
            </div>
        </div>
    </div>
    <button id="saveButton" type="button" class="btn btn-primary btn-company pull-right" data-bind="click: save">
        Save
        <span class="glyphicon glyphicon-download-alt"> </span>
    </button>

KnockOut Script:

<script>
    var NameModel = function(names) {
        var self = this;
        self.addName = function() {
            self.names.push({
                firstName: "",
                middleName: "",
                lastName: "",
                suffix: "",
                gender: ""
            });
        };
        self.removeName = function(name) {
            self.names.remove(name);
        };
        if (names != null)
            self.names(names);
    };
    var AddressModel = function(addresses) {
        var self = this;
        self.names = ko.observableArray(addresses);

        self.addAddress = function() {
            self.names.push({
                street: "",
                city: "",
                state: "",
                country: ""
            });
        };
        self.removeAddress = function(address) {
            self.names.remove(address);
        };
        if (addresses != null)
            self.names(addresses);
    };
    var viewModel = {
        nameModel: new NameModel([
            {
                firstName: "",
                middleName: "",
                lastName: "",
                suffix: "",
                gender: ""
            }
        ]),
        addressModel: new AddressModel([
            {
                street: "",
                city: "",
                state: "",
                country: ""
            }
        ]),
        save: function () {
            var self = this;
            $.ajax({
                url: '@Url.Action("GetPerson", "Person")',
                type: 'POST',
                contentType: 'application/json',
                data: ko.toJSON({ PersonModel: self
        })
            });
        }
    };
    ko.applyBindings(viewModel);

</script>

Solution

  • This

    data: ko.toJSON({ PersonModel: self })
    

    will send this

    { PersonModel: { nameModel: { names: [...] }, addressModel: { addresses: [...] } }
    

    which does not match your C# classes. You want to send this

    { nameModel: [...], addressModel: [...] }
    

    because in JSON the root object is not named and your C# class does not have a names or addresses property. Try

    data: ko.toJSON({ nameModel: self.nameModel.names,
                      addressModel: self.addressModel.addresses })
    

    Also, POSTing to a controller action called "GetPerson" is a really bad idea. And your address model has an observable array called names, which I assume is a copy-paste error.