0

Please refer to this question as it helped solving 50% of my issue:

Knockout Mapping reading JSON

the other 50% of issue is updating view, if you call ko.applyBindings(viewModel); twice, you get an error Uncaught Error: You cannot apply bindings multiple times to the same element.

No one online ever proposed a solution to this, even on the official knockout site they mentioned:

// Every time data is received from the server:
ko.mapping.fromJS(data, viewModel);

which is not working either. Anyone knows what the proper method is to update my view each time I fetch new data, knowing that the view is already initialized via ko.applyBindings(viewModel);?

Edit:

HTML :

<select class="input-short artist-list" data-bind="foreach: model">
   <option value="1" selected="selected" data-bind="text: name"></option>                                           
 </select>  

JS

var viewModel = {
        model: ko.observableArray()            
    };


$(window).load(function(){
    fetchArtists();  

  })
function fetchArtists() //this function fetches all artists
{
     // load data
       $.post( "../db/fetch/", { table: "artists"})
          .done(function( data ) {
          //  artists=JSON.parse(data);
          data=JSON.parse(data);//data is array of objects e.g [{name:"xxx"},{name:"yyy"}]   
          artists.model = ko.mapping.fromJS(data);         
          ko.applyBindings(artists);      
          });

}
Community
  • 1
  • 1
ProllyGeek
  • 15,517
  • 9
  • 53
  • 72
  • Generally the `ko.mapping.fromJS(data, viewModel);` should work because it will update your `viewModel` and if your bindings are correctly setup everything will work. However becased on your current question it is not clear what is not working: do you get error messages? Or something is not updating? Can you extend your question with some sample code how your data look and bindings are look like and how do you try to use the mapping plugin? – nemesv Aug 08 '15 at 18:01
  • @nemesv updated question , you cant call fetchArtist() more than once. – ProllyGeek Aug 08 '15 at 18:13

2 Answers2

3

This should do what you want to do:

JS

var viewModel = {
    model: ko.observableArray()            
};

$(window).load(function(){
    ko.applyBindings( viewModel );
    fetchArtists();  
})

function fetchArtists()
{
     $.post( "../db/fetch/", { table: "artists" } ).done(function( data ) {
          ko.mapping.fromJSON( data, {}, viewModel.model ); 
     });

}

As @SVSchmidt mentioned, you can only call ko.applyBindings() once per element. So you will ko.applyBindings once (on page load/ready most likely) and then either update the observableArray (model) directly or use the mapping plugin.

Updating the observableArray (model) directly will mean the values will be plain values. But if you wanted those values to be observable, then ko.mapping will be appropriate.

So to use the mapping plugin, you can call ko.mapping.fromJS or ko.mapping.fromJSON if you have raw JSON data.

The parameters for fromJS and fromJSON will be, in order:

  • incoming data that you want to map (in your case data)
  • options object that you can use to control mapping (empty object for now)
  • destination viewmodel or viewmodel property that you want to update (in your case viewModel.model

Here is a working demo that shows you how this works in action: http://plnkr.co/edit/4g1izaLYraBjganjX2Ue?p=preview

2

In knockout, a ViewModel is applied to the view once (applyBindings). Everytime the observables bind with data-bind are updated (e.g. assigning new data to them), the view is re-rendered. Your mistake is binding a non-observable (model) and re-defining artists.model with every function call.

You should do it the following way:

var viewModel = {
    artists: ko.observableArray()          
};


$(window).load(function(){
    fetchArtists();  
});

function fetchArtists()
{
     // load data
       $.post( "../db/fetch/", { table: "artists"})
          .done(function( data ) {
              viewModel.artists(JSON.parse(data)); // assign new values to artists observable          
       });
}

HTML

<select class="input-short artist-list" data-bind="foreach: artists">
   <option value="1" selected="selected" data-bind="text: name"></option>                                           
</select>

<script>
    ko.applyBindings(viewModel); // apply ViewModel
</script>
SVSchmidt
  • 6,269
  • 2
  • 26
  • 37