0

i'm trying to learn knockoutjs and can't figure out how the children observable works! i've tried reading many articles and many problems even on SO but probably missing something somewhere.

my json structure is (generated by Rails json renderer):

Albums -> Tracks -> Metadata

Album
 -Track
  --Metadata
 -Track
  --Metadata
Album
 -Track     
  -- Metadata

etc.

My view's are nested as well. In a page, each album goes under a div and each track goes under table row in the above div.

My View and JS codes are in JS Fiddle. http://jsfiddle.net/var56/

Problem: If you see the view in JSFiddle, you will see that i've marked html part which is working (album) and which is not working (track). The initial view rendered correctly. When I update the album name in the text field, the above h2 is updating correctly. but in the track template, when i update the track title, it is not updating in the underneath span.

Moreover, i've tried to export the data in the chrome console.

ko.toJSON(viewModel)

or, ko.mapping.toJSON(viewModel)

it gives me old data (that means, the changed values are not returned).

I will also add/remove albums & tracks and want the viewModel and views to reflect the same.

Where i'm missing something?


Note: Actual JSON data is here:

[
  { "id":9,
    "image_path":null,
    "name":"Test Album",
    "price":null,
    "sort_order":null,
    "tracks":[
      { "file_name":"01._Ei_Mon_Joshonay.mp3",
        "file_path":"/media/tracks/1/01._Ei_Mon_Joshonay.mp3",
        "id":192,
        "length":null,
        "metadata":{
          "artist":"Aroti (MR present)",
          "composer":"",
          "created_at":"2012-09-10T11:33:42Z",
          "duration":211,
          "genre":"Other",
          "id":124,
          "lyrics":null,
          "title":"01. Ei Mon Joshonay.mp3",
          "track_id":192,
          "updated_at":"2012-09-10T11:33:54Z",
          "year":0
        },
        "price":null,
        "thumb":"/media/track_thumbs/1/thumb_192.jpg",
        "title":"01. Ei Mon Joshonay.mp3"
      },
      { "file_name":"03._Jare_Ure_Ja_Pakhi.mp3",
        "file_path":"/media/tracks/1/03._Jare_Ure_Ja_Pakhi.mp3",
        "id":193,
        "length":null,
        "metadata":{
          "artist":"MR present",
          "composer":"",
          "created_at":"2012-09-10T11:33:48Z",
          "duration":204,
          "genre":"lata",
          "id":125,
          "lyrics":null,
          "title":"03.Jare ure ja pakhi",
          "track_id":193,
          "updated_at":"2012-09-10T11:33:54Z",
          "year":0
        },
        "price":99.0,
        "thumb":null,
        "title":"03.Jare ure ja pakhi"
      }
    ]
  },
  { "id":11,
    "image_path":null,
    "name":"Album 2",
    "price":null,
    "sort_order":null,
    "tracks":[  ]
  }
]

UPDATE:

i could not get anything working. now i've tried creating a jsFiddle that can simulate the problem i'm facing.

please check http://jsfiddle.net/Bb538/3/.

I've now a mapping object too (though in the code you may see that is not referenced, you can try if you want). You will see that album names are updating correctly. But Title and File Name fields are not updating the adjacent span (bound to same observable).

HungryCoder
  • 7,506
  • 1
  • 38
  • 51

2 Answers2

3

According to your update the issue is not that the child objects are not observable, it is with the data-bind attribute on your input fields for the track info. The "value" data-bind is not a child of the "attr" data-bind.

Here's a working jsFiddle http://jsfiddle.net/Bb538/4/

What you had

<input data-bind="attr: { name: 'albums[track_attributes][$index][id]', value: title}" id="" name="" type="text" value=""/>

What it should be

<input data-bind="attr: { name: 'albums[track_attributes][$index][id]'}, value: title" id="" name="" type="text" value=""/>
Reid Evans
  • 1,611
  • 15
  • 19
  • hi, thanks for spotting the mistake i've done! now i see the Track title is updating the beneath span accordingly! however, why the FileName is not working same way? it is not updating the span (``) – HungryCoder Sep 20 '12 at 04:05
  • sorry, it was the same error on both inputs and I only changed one on the other jsFiddle. here's an update [http://jsfiddle.net/Bb538/5/](http://jsfiddle.net/Bb538/5/) – Reid Evans Sep 20 '12 at 04:44
  • thanks a lot! this really solved the problem that i suffered for about 1 week. btw, how it is working nicely even without referencing any mapping object (the second argument for the fromJS method)? – HungryCoder Sep 21 '12 at 05:15
1

My suspicion is that your 'track' objects and properties aren't being mapped to observables by the mapping plugin, which would prevent them from notifying the view when their values change, and vice versa.

Check out my Q & A here on mapping nested JSON object structures to representative view models: Map JSON data to Knockout observableArray with specific view model type

A key take-away is the mapping object being used to specify to the mapping plugin how the nested objects are to be mapped and converted.

Here is a specific example pertaining to your Albums-Tracks scenario: http://jsfiddle.net/KodeKreachor/qRKtK/26/

Community
  • 1
  • 1
KodeKreachor
  • 8,852
  • 10
  • 47
  • 64
  • i've tried similar mapping but it did not work either. ok, i will post this implementation too. thanks for your input – HungryCoder Sep 14 '12 at 16:50
  • hello, i could not understand the implementation in the above link correctly! now i've tried a manual way of making observables. please check why this does not work either (it behaves exactly same as earlier: only album works but not its children). http://jsfiddle.net/Tmj9X/ – HungryCoder Sep 15 '12 at 05:10
  • 1
    Check out my updated answer, there's a specific fiddle that uses knockout's mapping plugin with template bindings to render your Album-Tracks scenario. – KodeKreachor Sep 19 '12 at 23:28