Binding undefined fields from observableArray or providing default

What's the problem

let's say I want do a query on my MongoDB and display the result in a table. Some documents have more or less fields then others do - which is fine for me. But I run into a problem on the client side.

I'm using "mongoose" with a model to retrieve the data via NodeJS and then on the client, I'm using "knockout.js" to bind the list to the table. Now that's where the problem occurs - as soon as any field does not exist, but is bound in the table, KO will throw an exception and stop processing.

What did I try

  • I tried to define a default value on the mongoose-model, but this does not add non-existing fields. Additionally it would increase the payload going over the wire ;-(

  • I tried "knockout.punches" and the "default" text-filter which works fine, but only when the value is null or empty. Undefined still throws an exception.

  • I tried several approaches to use the extend-mechanism of KO, but either I have to write a wired syntax in the binding or it also does not work for undefined.

  • Another possibility I did not try so far could be creating a model on the client and then map it to the response - honestly I just don't want to do that, because in most cases the data-structure from the DB is totally fine and it would just mean additional effort.

What I'm looking for

Does anyone has a solution to either automatically add missing fields from the mongoose model to the response or get it running so that KO does not stop processing for undefined fields (is there maybe some parameter). In my case it's an observableArray, most workarounds and solutions here were regarding single fields. So interesting would be a solution for a list with undefined fields.

Thanks

[{"firstname":"Foo", "lastname":"Bar"},{"firstname":"John"}];

self.allUsers = ko.observableArray();

self.onResponseAllUsers = function (response) {
            if (response && response.length > 0) {                
                self.allUsers(response);
            }
        };

<tbody data-bind="foreach: allUsers">
<tr>
<td data-bind="text: firstname"></td>                           
<td data-bind="text: lastname"></td>   <<<< This is undefined for one row, and KO stops processing.
</tr>
</tbody>

There are two ways to solve this problem.

  1. You can use || in binding to give default value in case of missing property so binding will be like this:-

    <tbody data-bind="foreach: allUsers">
       <tr>
        <td data-bind="text: $data.firstname"></td>
        <td data-bind="text: $data.lastname || 'NA'"></td> // used $data context property
       </tr>
    </tbody>
    

    Fiddle Demo

  2. You can iterate all record to check if any property is missing and if something is missing you can provide some default value.

    function User(item) {
      this.firstname = item.firstname || "NA";
      this.lastname = item.lastname || "NA";
    }
    var viewmodel = function (data) {
      var self = this;
      self.allUsers = ko.observableArray();
      self.onResponseAllUsers = function (response) {
      if (response && response.length > 0) {
         ko.utils.arrayForEach(response, function (item) {
            self.allUsers.push(new User(item));
         });
      }
     };
      self.onResponseAllUsers(data);
    }
    

    Fiddle Demo