Question

I'm making a little app that displays a list of the top first song of an artist's related artists. When I try and load my app for the first time, it shows nothing. But, when I "Reload Application" everything seems to work. When I constantly start "Reloading" it keeps adding more of the same tracks to the list as well.

How do I stop it from continually appending more tracks to the list as well as tighten up the code so that it works on load?

require([
  '$api/models',
  '$views/list#List',
  '$api/toplists#Toplist'

  ], function(models, List, Toplist){
    'use strict';

  // Build playlist
  function buildList(trackURIArray){
    var arr = trackURIArray;
    models.Playlist
      .createTemporary("myTempList")
      .done(function(playlist){ 
        playlist.load("tracks").done(function(loadedPlaylist){
          for(var i = 0; i < arr.length; i++){
            loadedPlaylist.tracks.add(models.Track.fromURI(arr[i]));
          }
        });

        // Create list
        var list = List.forPlaylist(playlist,{
          style:'rounded'
        });

        $('#playlistContainer').append(list.node);
        list.init();
      });
  }

  // Get top track
  function getTopTrack(artist, num, callback){
    var artistTopList = Toplist.forArtist(artist);

    artistTopList.tracks.snapshot(0, num).done(function (snapshot){ 
      snapshot.loadAll('name').done(function(tracks){
        var i, num_toptracks;
        num_toptracks = num; 

        for(i = 0; i < num_toptracks; i++){
          callback(artist, tracks[i]);
        }
      });
    });
  }

  // Get Related
  function getRelated(artist_uri){
    var artist_properties = ['name', 'popularity', 'related', 'uri'];

    models.Artist
    .fromURI(artist_uri)
    .load(artist_properties)
    .done(function (artist){

      artist.related.snapshot().done(function(snapshot){
        snapshot.loadAll('name').done(function(artists){
          var temp = [];

          for(var i = 0; i < artists.length; i++){

            getTopTrack(artists[i], 1, function(artist, toptrack){
              var p, n, u;

              p = artist.popularity;
              n = artist.name;
              u = artist.uri;

              temp.push(toptrack.uri);

            });
          }
          // Build a list of these tracks
          buildList(temp);
        });
      });
    });
  }

  getRelated('spotify:artist:2VAvhf61GgLYmC6C8anyX1');
});
Was it helpful?

Solution

By using Promises you can delay the rendering of the list until you have successfully composed the temporary list with your tracks. Also, in order to prevent the addition of repeated tracks on reload, assign a unique name to your temporary playlist.

require([
  '$api/models',
  '$views/list#List',
  '$api/toplists#Toplist'
], function (models, List, Toplist) {
  'use strict';

  // Build playlist
  function buildList(trackURIArray) {
    var arr = trackURIArray;
    models.Playlist
      .createTemporary("myTempList_" + new Date().getTime())
      .done(function (playlist) {
        playlist.load("tracks").done(function () {
          playlist.tracks.add.apply(playlist.tracks, arr).done(function () {
            // Create list
            var list = List.forCollection(playlist, {
              style: 'rounded'
            });

            $('#playlistContainer').appendChild(list.node);
            list.init();
          });
        });
      });
  }

  // Get top track
  function getTopTrack(artist, num) {
    var promise = new models.Promise();
    var artistTopList = Toplist.forArtist(artist);

    artistTopList.tracks.snapshot(0, num).done(function (snapshot) {
      snapshot.loadAll().done(function (tracks) {
        promise.setDone(tracks[0]);
      }).fail(function (f) {
        promise.setFail(f);
      });
    });

    return promise;
  }

  // Get Related
  function getRelated(artist_uri) {
    models.Artist
      .fromURI(artist_uri)
      .load('related')
      .done(function (artist) {
        artist.related.snapshot().done(function (snapshot) {
          snapshot.loadAll().done(function (artists) {

            var promises = [];

            for (var i = 0; i < artists.length; i++) {
              var promise = getTopTrack(artists[i], 1);
              promises.push(promise);
            }

            models.Promise.join(promises)
              .done(function (tracks) {
                console.log('Loaded all tracks', tracks);
              })
              .fail(function (tracks) {
                console.error('Failed to load at least one track.', tracks);
              })
              .always(function (tracks) {
                // filter out results from failed promises
                buildList(tracks.filter(function(t) {
                  return t !== undefined;
                }));
              });
          });
        });
      });
  }

  getRelated('spotify:artist:2VAvhf61GgLYmC6C8anyX1');
});

OTHER TIPS

The way I think about stuff like this is to imagine I'm on an super slow connection. If every callback (done, or the function passed to getTopTrack) took 2 seconds to respond, how do I need to structure my code to handle that?

How does that apply here? Well, when you call buildList, temp is actually empty. I suspect if you created the playlist first in getRelated, then added songs to it in your callback for getTopTrack, then it would work because the List would keep itself up to date.

Alternatively, you could rework getTopTrack to return a Promise, join all the top track promises together (see Promise doc's on each() and join()), then build the list when they're all complete.

As far as why you're getting multiple lists, it's because you append a new List each time you call buildList. Though I'm not seeing this behavior when I threw the code as is into my playground area. It only happens once, and when I reload application it starts from scratch. Perhaps you have a reload button which is calling getRelated.

Update I've been trying to get this to work, and having lots of trouble. Tried calling list.refresh after each add. Trying a Promise based method now, but still can't get the List to show anything.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top