Saving values into a json object when keypress, ch

2019-07-31 03:49发布

I have a very basic dynamic form in which I am using knockout.js framework. I am able to add or remove contacts as needed in real time. I am having a little difficulty saving the json object inside a textarea. In the function below self.save takes the values and places them inside a json object. Then the textarea holds attribute data-bind=’value: lastSavedJson’, which holds the json object. The tricky part that I am struggling is doing this every time keyup, change is trigger on a input field, checkbox or select menu. Initially this was done with trigger of a button. How can I save the values inside a json object every time a key, checkbox, select menu is triggered? JSFIDDLE

var initialData = [{
    firstName: "Jenny",
    lastName: "LaRusso",
    phone: "(555) 121-2121",
    alt_phone: "(555) 123-4567",
    main1: false,
    main2: true    
}, {
    firstName: "Sensei",
    lastName: "Miyagi",
    phone: "(555) 444-2222",
    alt_phone: "(555) 999-1212",
    main1: true,
    main2: false
}];

var ContactsModel = function (contacts) {
    var self = this;
    self.contacts = ko.observableArray([]);

    ko.utils.arrayForEach(contacts, function (contact) {
        self.contacts.push({
            firstName: contact.firstName,
            lastName: contact.lastName,
            phone: contact.phone,
            alt_phone: contact.alt_phone,
            main1: ko.observable(contact.main1),
            main2: ko.observable(contact.main2)
        });
    });

     $.getJSON("functions/getPerson.php", function(allData) {
          var initialData = $.map(allData, function(person) { return new ContactsModel(person) });
          console.log(self.contacts(initialData));
    });

    self.addContact = function () {
        self.contacts.push({
            firstName: "",
            lastName: "",
            phone: "",
            alt_phone: "",
            main1: false,
            main2: false
        });
    };

    self.removeContact = function (contact) {
        self.contacts.remove(contact);
    };

    self.addPhone = function (contact) {
        contact.phones.push({
            number: ""
        });
    };

    self.removePhone = function (phone) {
        $.each(self.contacts(), function () {
            this.phones.remove(phone)
        })
    };

    $("input,checkbox,selct").bind('change keyup', function() {
        self.save = function () {
           self.lastSavedJson(JSON.stringify(ko.toJS(self.contacts), null, 2));         
        };
    });


    self.lastSavedJson = ko.observable("");

};


ko.applyBindings(new ContactsModel(initialData));

1条回答
我欲成王,谁敢阻挡
2楼-- · 2019-07-31 04:21

I made some changes to your viewmodels, so they're laid out a bit nicer. Also, you'll notice I removed the jQUery bit...here's why. 1) In general, if you find yourself using jQuery and referencing the dom inside of a knockout viewmodel, you're doing it wrong / there is a better way to do it idiomatically, the knockout way. This keeps your viewmodel separate from your templates.

2) That would never work the way you wanted it to. Knockout is going to add and remove bits of dom as it sees fit, and jQuery will only bind to events that exist on the page at the moment the query is ran. There is a technique that could work, but knockout actually interferes with event bubbling, which is why reason #1 above is very pertinent. For more info, if you're curious, check out jQuery on and figure out the difference in behavior between $("input,select").change(changeHandler) and $("body").on('change','input,select',changeHandler);

var initialData = [{
    firstName: "Jenny",
    lastName: "LaRusso",
    phone: "(555) 121-2121",
    alt_phone: "(555) 123-4567",
    main1: false,
    main2: true    
}, {
    firstName: "Sensei",
    lastName: "Miyagi",
    phone: "(555) 444-2222",
    alt_phone: "(555) 999-1212",
    main1: true,
    main2: false
}];
var ContactModel = function(contact) {
  var self = this;
  self.firstName = ko.observable(contact.firstName);
  self.lastName = ko.observable(contact.lastName);
  //what is the point of the phone observable when there is an array of phones?
  //the data structure doesn't match either - self.phone contains a phone number, and
  //self.phones contains objects with a 'number' property
  self.phone = ko.observable(contact.phone);
  //array for all the phones, initialize it with the passed in data or an empty array
  self.alt_phone = ko.observable(contact.alt_phone);
  self.phones = ko.observableArray(contact.phones || []);
  self.main1 = ko.observable(contact.main1);
  self.main2 = ko.observable(contact.main2);
}
var ContactsModel = function (contacts) {
    var self = this;
    self.contacts = ko.observableArray(ko.utils.arrayMap(contacts || [], function(contact) { return new ContactModel(contact)}));

     //this ajax call is going to override the default data passed in via the contacts parameter...Is that what's supposed to happen?
     $.getJSON("functions/getPerson.php", function(allData) {
          var initialData = $.map(allData, function(person) { return new ContactModel(person) });
          console.log(self.contacts(initialData));
    });

    self.addContact = function () {
        self.contacts.push(new ContactModel());
    };

    self.removeContact = function (contact) {
        self.contacts.remove(contact);
    };

    //your contacts didn't have a phone array, so I added one to make this function work.
    self.addPhone = function (contact) {
        contact.phones.push({
            number: ""
        });
    };

    self.removePhone = function (phone) {
        $.each(self.contacts(), function () {
            this.phones.remove(phone)
        })
    };

};

var mainModel = new ContactsModel(initialData);
var modelWithSerializedData = {
  MainModel:mainModel,
  mainModelSerialized: ko.computed(function() { 
      return ko.toJSON(mainModel);
  })
}
ko.applyBindings(modelWithSerializedData);

and then in your view, get at the mainmodel with a 'with', and wrap just the input in the form you're submitting OR avoid putting names on your template inputs so they don't get submitted with the json data. Also, make sure that you use the valueUpdate binding on your inputs so that it gets serialized immediately.

<div data-bind="with:MainModel">
  <!--the rest of your template -->
<ul data-bind="foreach:contacts">
  <li>
    <label>First Name: 
      <input data-bind="value:firstName, valueUpdate:'afterkeydown'" />
    </label>
  </li>
</ul>
</div>
<form>
<textarea name="data" data-bind='value: serializedModel' rows='25' cols='60'></textarea>
<input type="hidden" name="serializedModel" data-bind="value:mainModelSerialized" />
</form>
查看更多
登录 后发表回答