Search code examples
javascriptjqueryjsonknockout.jsknockout-mapping-plugin

mapping json array to ko throwing error


I am newbie to knockoutjs . i was trying to map json data to ko. This is my code.

I want to parse the json data and display it in the table. When i am trying ko.compute it throws me self.firstName() is not an function. When i try to log the value of the firstName in console before compute function, it prints the 3 values in the json array and runs the forurth time and throws an error saying self.firstName is not an function.

Edit :When i remove computed and just print the data as in the json it is able to map correctly. the issue occurs when i use computed. When i try console.log(self.firstName() ) before computed and after mapping it prints the values in the jsonarray but instead of parsing 3 times its parses 4 times and 4th time it gives me the "not a function" error

<div id="employeeList">
 <table border ="2">
  <thead>
   <tr>
<th> First Name</th>
<th> Last Name</th>
<th> Age</th>
<th> Phone</th>
<th> Email</th>
</tr>
</thead>
<tbody  data-bind="foreach:employees">
<tr>
 <td data-bind="text:name"></td>
<td data-bind="text:firstName"></td>
<td data-bind="text:lastName"></td>
<td data-bind="text:age"></td>
<td data-bind="text:phone"></td>
<td data-bind="text:email"></td>
</tr>
</tbody>
</table>

</div>

This is my js code

     var personMapping={
          'copy':['age'],
          'employees': {
              create: function(options) {
                return new PersonViewModel(options.data);
            }
          }
     };


  function PersonViewModel(data){
      var self=this;
      ko.mapping.fromJS(data,personMapping,this);
       self.name=ko.computed(function(){
          return self.firstName()+' '+self.lastName();
      },this);


  }



var data={employees:
       [{
            firstName: 'Marco',
            lastName: 'Franssen',
            age: 26,
            phone: 12346789,
            email: "a@a.com"    
        },
        {
            firstName: 'Kumar',
            lastName: 'Rangarajan',
            age: 26,
            phone: 123467890,
            email: "a@b.com"
        },
        {
            firstName: 'A',
            lastName: 'B',
            age: 26,
            phone: 6775675567567,
            email: "a@c.com"}]
}

ko.applyBindings(new PersonViewModel(data), $('#employeeList'));


});

This is the jsfiddle code: http://jsfiddle.net/YfqPs/1/


Solution

  • Your code gets executed 4 times because your are reusing your PersonViewModel as your main view model and your view model in your personMapping:

    • Your PersonViewModel get called 3 items once for each item in your employees array resulting of this call:

      create: function(options) {
         return new PersonViewModel(options.data);
      }
      
    • Your PersonViewModel get called for the 4 time resulting of this call:

      ko.applyBindings(new PersonViewModel(data), $('#employeeList'));
      

      and this time it throws the exception because there is no firstName property on your main data only on your employees.

    There are multiple ways to fix this but all of them require to create a proper standalone "main" viewmodel.

    The easiest solution is to create your "main" viewmodel with var viewModel = ko.mapping.fromJS(data, personMapping); and use it in your ko.applyBindings:

    var viewModel = ko.mapping.fromJS(data, personMapping);
    ko.applyBindings(viewModel, $('#employeeList')[0]);
    

    Demo JSFiddle