Search code examples
javascriptknockout.jsdata-bindingknockout-2.0

Knockout.js Collection of collections


I just started learning knockout and i am looking for some help in figuring the way to implement collection of collections for the view model to build a json result for collections. I have two form lists names and addresses for which user inputs multiple values. When i try with one form it works fine. When i include the address model i am having issues in building the view model. I think i am doing it wrong. I would appreciate if you can guide me in fixing this. Thanks in advance

<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 class="panel-body">
        <table data-bind="visible: names().length > 0" 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:$root.removeName">Delete</a></td>
            </tr>
          </tbody>
        </table>
        <button class="btn btn-success btn-sm" data-bind="click: addName">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 class="panel-body">
        <table data-bind="visible: addresses().length > 0" 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:addresses">
            <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:$root.removeAddress">Delete</a></td>
            </tr>
          </tbody>
        </table>
        <button class="btn btn-success btn-sm" data-bind="click: addAddress">Add Address</button>
      </div>
    </div>
  </div>
</div>

var NameModel = function(names) {
  var self = this;
  self.names = ko.observableArray(names);

  self.addName = function() {
    self.names.push({
      firstName: "",
      middleName: "",
      lastName: "",
      suffix: "",
      gender: "",
      reportingDate: ""
    });
  };
  self.removeName = function(name) {
    self.names.remove(name);
  };
};
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);
  };
};
//var viewModel = new NameModel([
//  { firstName: "", middleName: "", lastName: "", suffix: "", gender: "", reportingDate: "" }
//]);
var viewModel = {
  nameModel: new NameModel([{
    firstName: "",
    middleName: "",
    lastName: "",
    suffix: "",
    gender: "",
    reportingDate: ""
  }]),
  addressModel: new AddressModel([{
    street: "",
    city: "",
    state: "",
    country: ""
  }])
};
ko.applyBindings(viewModel);

https://jsfiddle.net/iamsrk/9kf41vd7/


Solution

  • See working fiddle now:

    https://jsfiddle.net/9kf41vd7/30/

    The reason it didn't work is that it didn't build. ApplyBindings was falling over. It was falling over for mulitple reasons including simple ones like addaddress instead of addAddress.

    Because its just an example, I am going to ignore the bad naming conventions.

    Added withs (better practice and allowed your add name button to be simplified from nameModel.addName -> addName) Also this is a function so should of been nameModel(). Withs help you from making this mistake.

    data-bind="with: nameModel"
    

    Look at my fiddle, and ask any questions if you don't understand why I've done what ive done.