1

I'm using the spotify API to dump all of a user's playlists, and the track names contained. I figured out how to request that info and am trying to store it in a hash where an array of tracks can be found by playlist name (hash[playlistName] = [track1, track2, etc]). When I console.log this, everything looks fine. enter image description here

But when I try and use a handle bars helper to iterate through this and display everything, JavaScript thinks the object is empty. Here is some code:

//helper that will display eventually, but right now logs "undefined"
Handlebars.registerHelper('read_hash', function(hash){
    console.log(hash.length); //undefined
    console.log(Object.keys(hash); //undefined
    console.log(hash); //looks fine.. results in screenshot..
});

//ajax request that successfully get's all of the playlist data I want
        $.ajax({
            url: 'https://api.spotify.com/v1/me/playlists',
            headers: {
              'Authorization': 'Bearer ' + access_token
            },
            success: function(response) {
              //make a new hash and put in template
              var processed = 0;
              response.items.forEach(function(e){
                var playlistName = e.name;
                //get tracks by nesting a request lol
                $.ajax({
                  url: e.tracks.href,
                  headers: {
                    'Authorization': 'Bearer ' + access_token
                  },
                  success: function(responseTwo) { //returns track objects
                    playlists[playlistName] = responseTwo.items;
                    processed++;
                  }
                }).done(function(){
                  if(processed >= response.items.length)
                  {
                    playlistPlaceholder.innerHTML = playlistTemplate({Playlists: playlists}); //get playlists
                  }
                });
              });

//handle bars template with call to read_hash
<script id="playlist-template" type="text/x-handlebars-template">
  <h1>playlists</h1>
  <table>
    <tr>
      <th>playlist</th>
      <th>tracks</th>
    </tr>
    {{read_hash Playlists}}
  </table>
</script>

Also, when I console log JSON.stringify(hash) from read_hash I get results I want so... maybe something is working?

lehermj
  • 916
  • 4
  • 9
  • 20
  • 1
    Where is `read_hash` called? – epascarello Mar 18 '19 at 16:58
  • It's called in my handle bars template, the value being passed in right now is the playlists hash. – lehermj Mar 18 '19 at 16:59
  • Can we see it?. – epascarello Mar 18 '19 at 17:00
  • yes, just edited. – lehermj Mar 18 '19 at 17:01
  • 1
    You are trying to access the object before it is populated, which is evident by the fact that the console shows `{ }`. `$.ajax` is asynchronous. `playlists[playlistName] = responseTwo.items;` happens **after** `playlistPlaceholder.innerHTML = playlistTemplate({Playlists: playlists});`. Also note the little blue `i` box. – Felix Kling Mar 18 '19 at 17:02
  • 1
    Probably a classic case of lazy loading in console making think it is defined. `console.log(JSON.stringify(hash));` – epascarello Mar 18 '19 at 17:04
  • 1
    For what it's worth, JavaScript does not have a `hash` type and objects are not implemented as true hashtables. You may want to log `typeof hash` to see what you're actually working with. – coreyward Mar 18 '19 at 17:05
  • 1
    Have a look at [Wait until all jQuery Ajax requests are done?](https://stackoverflow.com/q/3709597/218196). It can also be done with arrays. – Felix Kling Mar 18 '19 at 17:06
  • I just updated the code to use the ajax method's done method, but am still running into the same problem. Just updated the snippet i provided. – lehermj Mar 18 '19 at 23:58
  • looks like jQuery tag might be fitting? – Ronnie Royston Mar 19 '19 at 00:09

2 Answers2

0

I fixed it by utilizing the ajax done callback. Theres probably a more organized way to take care of this, but here is my solution:

ajax request to dump playlist data:

$.ajax({
    url: 'https://api.spotify.com/v1/me/playlists',
    headers: {
      'Authorization': 'Bearer ' + access_token
    },
    success: function(response) {
      //make a new hash and put in template
      var processed = 0;
      response.items.forEach(function(e){
        var playlistName = e.name;
        //get tracks by nesting a request lol
        $.ajax({
          url: e.tracks.href,
          headers: {
            'Authorization': 'Bearer ' + access_token
          },
          success: function(responseTwo) { //returns track objects
            playlists[playlistName] = responseTwo.items;
            processed++;
          }
        }).done(function(){
          if(processed >= response.items.length)
          {
            playlistPlaceholder.innerHTML = playlistTemplate({Playlists: playlists}); //get playlists
          }
        });
      });
      //console.log(playlists);
      $('#login').hide();
      $('#loggedin').show();
    }
});

Handlebars helper to parse associative array:

Handlebars.registerHelper('read_hash', function(hash){
  jsonHash = JSON.parse(JSON.stringify(hash));
  var html = "<table style='width:5px'><tr><th>Playlist</th><th>Track</th></tr>"; //stores markup to write and return at end
  for(var playlistName in jsonHash)
  {
    var playlistTracks = jsonHash[playlistName];
    html = html + "<tr><td>" + playlistName + "</td><td><ul>";
    for(var i = 0; i < playlistTracks.length; i++)
    {
      html =  html + "<li>" + playlistTracks[i].track.name + "</li>";
    }
    html = html + "</ul></td></tr>";
  }
  console.log(html + "</table>");
  return new Handlebars.SafeString(html + "</table>");
});

handlebars template to display:

<script id="playlist-template" type="text/x-handlebars-template">
  <h1>playlists</h1>
  {{read_hash Playlists}}
  <table>
    <tr>
      <th>playlist</th>
      <th>tracks</th>
    </tr>
  </table>
</script>
lehermj
  • 916
  • 4
  • 9
  • 20
-1

the length property is only applicable to arrays, and the response is an object

 var property = {"test object track 123":"test"};
 console.log(property.length); //shows undefined

Additionally, the registerHelper statement must generate an error since there is no closed parenthesis Iterating over properties

//show error "closed parenthesis"
console.log(Object.keys(hash); //undefined

//helper that will display eventually, but right now logs "undefined"
Handlebars.registerHelper('read_hash', function(hash){
        for (var playlist in hash) {
            console.log("the length of item is" + hash[playlist].length);
        }
});