Edit newly added items within array of objects and

2019-09-08 04:19发布

Here's the JSFiddle.

The array of objects looks like below:

var data = [{
  "ConferenceUsers": [{
    "Id": 3006,
    "ConferenceId": 8,
    "Name": null,
    "Email": "mail@lala.com",
    "UserName": null,
    "PhoneNumber": "234234234234"
  }],
  "Id": 8,
  "Subject": "YYYhaaaaa",
  "StartTime": "2016-05-29T18:30:00",
  "EndTime": "2016-05-29T19:30:00",
  "OrganizerEmail": "elpas@live.com",
  "OrganizerName": "dasdasd",
}, {
  "ConferenceUsers": [{
    "Id": 3013,
    "ConferenceId": 12,
    "Name": null,
    "Email": "dsfdfsdfdsf@dfdfdf.com",
    "UserName": null,
    "PhoneNumber": null
  }],
  "Id": 12,
  "Subject": "dsfsdfdsfsdf",
  "StartTime": "2016-05-31T22:00:00",
  "EndTime": "2016-05-31T23:00:00",
  "OrganizerEmail": "d@adssad.com",
  "OrganizerName": "dsfdsfsdf"
}];

I need to be able to add and edit new ConferenceUsers.

Adding new users works but I can't edit them.

ConferenceUser ViewModel:

var ConferenceUser = function (user) {
    this.ConferenceId = ko.observable(user.ConferenceId);
    this.Email = ko.observable(user.Email);
    this.Id = ko.observable(user.Id);
    this.Name = ko.observable(user.Name);
    this.PhoneNumber = ko.observable(user.PhoneNumber);
}; 

ConferenceList ViewModel and mappings:

var createConferenceUser = function (user) {
    return new ConferenceUser(user);
};

var ConferenceList = function(conferencesJSON) {
  var self = this;

  var users = [];
  for (i = 0; i < conferencesJSON.length; i++) {
      users.push(conferencesJSON[i].ConferenceUsers);
  }

  this.conferences = ko.observableArray(conferencesJSON.map(createConference));
  this.conferenceUsers = ko.observableArray(users.map(createConferenceUser));

  this.addConference = function(conferenceJSON) {
    self.conferences.push(createConference(conferenceJSON));
  };
};

ko.applyBindings(new ConferenceList(data));

Q: How can I update the existing / newly added ConferenceUsers and the DOM also, like in the jsFiddle?

1条回答
Summer. ? 凉城
2楼-- · 2019-09-08 05:00

Like I commented: if you were to actually use the ConferenceUser view models with the observable properties, you would probable able to figure out how to both create new, and edit existing users. I do see some other points of improvement though, so I think it's still worth to formulate an answer to your question:

  • The edit/save/cancel logic is cluttering your Conference view model and can easily be separated into its own little widget
  • The way you're switching between the Add and Update button UI doesn't work well with knockout

I've written an answer that's more about the architecture because I feel that the answer from your question follows naturally from a well defined separation of concerns. And because I enjoy refactoring :)

Here's my suggestion after having moved stuff around. You can decide for yourself how much of it you'll use, but at least the answer to your question can be found in the code!

ConferenceList

Only serves to manage a list of conferences. Could easily be extended with removeConference or getConferencesFromServer etc.

var ConferenceList = function(conferencesJSON) {
  this.conferences = ko.observableArray(conferencesJSON.map(Conference.create));

  this.addConference = function(conferenceJSON) {
    self.conferences.push(Conference.create(conferenceJSON));
  };
};

Conference

Holds a list of ConferenceUser instances and a widget to edit and create new users. This is where your question is answered:

var Conference = function(conferenceJSON) {
  var self = this;
  this.Id = conferenceJSON.Id;

  // Note that we're mapping the plain objects from the json to
  // ConferenceUser instances!
  this.users = ko.observableArray(
    conferenceJSON.ConferenceUsers.map(ConferenceUser.create));

  this.userEditor = new UserEditor(this.users, this.Id);

  this.onUserClick = function(user, event) {
    self.userEditor.edit(user);
  };
};

ConferenceUser

This is where we make sure the UI is updated after edits: note that the Email and PhoneNumber properties are observable. I didn't create observables for all properties to indicate not all properties are meant to be changed in the UI.

var ConferenceUser = function(user) {
  this.Email = ko.observable(user.Email);
  this.PhoneNumber = ko.observable(user.PhoneNumber);

  this.ConferenceId = user.ConferenceId;
  this.Id = user.Id;
  this.Name = user.Name;
};

I've created a static create method for two reasons:

  1. It keeps track of an id inside a closure to make sure it's unique
  2. It's easy to use inside an Array.prototype.map method.

The code:

ConferenceUser.create = (function() {
  var id = 0;

  return function(userOptions) {
    // Add an ID if not present in options
    return new ConferenceUser(Object.assign(userOptions, {
      Id: userOptions.Id || id++
    }));
  };
}());

UserEditor

This is the biggest improvement (I believe) to your code: an editor widget that helps you create, edit and save new users. The methods it exposes are much easier to understand and write because they're not inside your Conference viewmodel.

var UserEditor = function(users, conferenceId) {
  var self = this;

  // Holds the user that's being edited, is null when
  // creating a new user
  this.editing = ko.observable(null);

  this.phoneInput = ko.observable("");
  this.emailInput = ko.observable("");

  this.clear = function() {
    self.phoneInput("");
    self.emailInput("");
  };

  this.add = function() {
    var newUserOptions = {
      Email: self.emailInput(),
      PhoneNumber: self.phoneInput(),
      ConferenceId: conferenceId
    };
    users.push(ConferenceUser.create(newUserOptions));
    self.clear();
  };

  this.edit = function(user) {
    self.editing(user);

    self.phoneInput(user.PhoneNumber());
    self.emailInput(user.Email());
  };

  this.reset = function() {
    self.editing(null);
    self.clear();
  };

  this.save = function() {
    var currentUser = self.editing();

    currentUser.Email(self.emailInput());
    currentUser.PhoneNumber(self.phoneInput());

    self.reset();
  };

};

Now, after all this code, you'll see your HTML is very straight forward. Here's your edit widget:

<div data-bind="with: userEditor">
  <input type="tel" placeholder="phone number" 
    data-bind="value: phoneInput" />
  <input type="email " placeholder="email" 
    data-bind="value: emailInput" />
  <!-- ko ifnot: editing -->
  <button data-bind="click: add">Add</button>
  <!-- /ko -->
  <!-- ko if: editing -->
  <button data-bind="click: save">Save</button>
  <button data-bind="click: reset">Cancel</button>
  <!-- /ko -->
</div>

Kind of a long story to get to the fix, but if you've made it this far: here's an updated fiddle! https://jsfiddle.net/e2ox4doj/

查看更多
登录 后发表回答