2

I am using facebooks javascript API to import the albums and photos from a Facebook page.

First, I import the albums with an API call.

Then I import the photos of every album into arrays with another API call.

The final step - with another API call, I import the coverPhotos of every album so I can make a jquery mobile listView with all the albums and their coverPhotos.

My code looks like this:

<script>
    var albumPhotos = new Array();
    var albumThumbnails = new Array();
    window.fbAsyncInit = function() {
        // init the FB JS SDK 
        FB.init({
            appId      : '564984346887426',       // App ID from the app dashboard
            channelUrl : 'channel.html',          // Channel file for x-domain comms
            status     : true,                    // Check Facebook Login status
            xfbml      : true                     // Look for social plugins on the page
        });

        // Additional initialization code such as adding Event Listeners goes here
        FB.api('169070991963/albums', function(response) {
            if(!response || response.error) {
                // render error
                alert("Noo!!");
            } else {
                // render photos
                for(var i=0; i<response.data.length; i++) { 
                    (function (i) {
                        var albumName = response.data[i].name;
                        var albumCover = response.data[i].cover_photo;
                        var albumId = response.data[i].id;
                        var numberOfPhotos = response.data[i].count;

                        FB.api(albumId + "/photos", function(response) {
                            if(!response || response.error) {
                                // render error
                                alert("Noo!!");
                            } else {
                                for(var k=0; k<response.data.length; k++) { 
                                    albumThumbnails[i] =  albumThumbnails[i]||[];
                                    albumThumbnails[i][k] = response.data[k].picture;
                                    albumPhotos[i] = albumPhotos[i]||[];
                                    albumPhotos[i][k] = response.data[k].source;
                                }
                            }
                        }); 

                        console.log(albumName);
                        FB.api( albumCover, function(response) {
                            if(!response || response.error) {
                                // render error
                                alert("Noo!!");
                            } else {
                                // render photos
                                $(".albums").append(
                                    '<li>'+
                                        '<a href="#Gallery' + i + '"' + 'data-transition="slidedown">'+
                                            '<img src= "' + response.picture + '"  />'+
                                            '<h2>' + albumName + '</h2>'+
                                            '<p>' + "Number of Photos:  " + numberOfPhotos +'</p>'+
                                        '</a>'+
                                    '</li>')
                                    .listview('refresh');

                                $("#home").after('<div data-role="page" data-add-back-btn="true" id=Gallery'+ i +
                                    ' class="gallery-page"> ' +
                                    ' <div data-role="header"><h1>Gallery</h1></div> ' + ' <div data-role="content"> ' +
                                    ' <ul class="gallery"></ul> ' + ' </div> ' +
                                    ' </div> ');

                                for(var x=0; x < albumPhotos[i].length; x++)
                                    $('#Gallery' + i + ' .gallery').append('<li><a href="' + albumPhotos[i][x] + '" rel="external"><img src="' +  albumThumbnails[i][x] + '" /></a></li>');                                                             
                            }
                        });                             
                    })(i);                                      
                } //end of for loop
            }
        });     
    };

I have a problem in this piece of code:

for(var x=0; x < albumPhotos[i].length; x++)
    $('#Gallery' + i + ' .gallery').append('<li><a href="' + albumPhotos[i][x] + '" rel="external"><img src="' +  albumThumbnails[i][x] + '" /></a></li>');

and more specifically here : albumPhotos[i].length

Because the calls to the API are asynchronous, the previous API call that actually creates the array albumPhotos has not finished yet, meaning the array is not defined yet.

I need a way before I call the last API call FB.api(albumCover, function(response) {..}

to make sure that the previous API call:

FB.api(albumId + "/photos", function(response) {
    if(!response || response.error) {
        // render error
        alert("Noo!!");
    } else {
        for(var k=0; k<response.data.length; k++) { 
            albumThumbnails[i] =  albumThumbnails[i]||[];
            albumThumbnails[i][k] = response.data[k].picture;
            albumPhotos[i] = albumPhotos[i]||[];
            albumPhotos[i][k] = response.data[k].source;
        }
    }
});

has finished.

What is the easiest way to do something like this here?

Lauren Rutledge
  • 1,195
  • 5
  • 18
  • 27
Johny Jaz
  • 875
  • 2
  • 13
  • 26
  • 1
    [Promises](http://en.wikipedia.org/wiki/Promise_(programming)) would be a good solution. Have a look at when.js: https://github.com/cujojs/when (for example) if you don't mind using a library. – Felix Kling Jun 26 '13 at 15:06
  • Its a jquery mobile website. Can this javascript execute in all mobile browsers? Also if you have some experience with it , could you provide me an answer on how you could use it in my code? Thank you very much. – Johny Jaz Jun 26 '13 at 15:21
  • If you use jQuery anyway, you can use jQuery's deferred/promise implementation. See http://api.jquery.com/category/deferred-object/ and http://api.jquery.com/deferred.then/ (the last example, chain tasks). You will find many questions regarding this here on SO, e.g. http://stackoverflow.com/q/8049041/218196. – Felix Kling Jun 26 '13 at 15:35

1 Answers1

3

Short answer, you need to make all the code that depends on the albums being populated work after they get populated. That means copy and pasting some code around.

Long answer...

First, let's refactor a bit. For your sanity, you'll want this function

// checkForErrorFirst wraps your function around the error checking code first
// if there is no response, then your code will not be called
// this allows you to just write the juicy working code 
//   and not worry about error checking
function checkForErrorFirst(myFunc) {
  return function(response) { 
    if (!response || response.error) {
      alert("Noo!!");
    } else {
      myFunc(response);
    }
  };
}

So that you can do things like this:

function DoWork(response) {
  // usefulwork...
}

And the FB call looks like...

FB.api('169070991963/albums', checkForErrorFirst(DoWork));

Now that we have settled that, we need to chain each of your call together since they depend on each other. I set albumPhotos and albumThumbnails in the global space just like you did.

var albumPhotos = new Array();
var albumThumbnails = new Array();

function getAlbums(response) {
  for (var i=0; i < response.data.length; ++i) {
    processAlbum(response.data[i], i);
  } 
}

function processAlbum(album, i) {
  FB.api(album.id + "/photos", checkForErrorFirst(populateAlbum(album, i)));
}

function populateAlbum(album, i) {
  return function(response) {
    for (var k=0; k < response.data.length; ++k){ 
      albumThumbnails[i] =  albumThumbnails[i]||[];
      albumThumbnails[i][k] = response.data[k].picture;
      albumPhotos[i] = albumPhotos[i]||[];
      albumPhotos[i][k] = response.data[k].source;
    }

    // now that we've populated the album thumbnails and photos, we can render the album
    FB.api(album.cover_photo, checkForErrorFirst(renderAlbum(album, i)));
  };
}

function renderAlbum(album, i) {
  return function(response) {
    var albumName = album.name;
    var albumCover = album.cover_photo;
    var albumId = album.id;
    var numberOfPhotos = album.count;

    // render photos
    $(".albums").append('<li>'+
      '<a href="#Gallery' + i + '"' + 'data-transition="slidedown">'+
      '<img src= "' + response.picture + '"  />'+
      '<h2>' + albumName + '</h2>'+
      '<p>' + "Number of Photos:  " + numberOfPhotos +'</p>'+
      '</a>'+
      '</li>').listview('refresh');

    $("#home").after('<div data-role="page" data-add-back-btn="true" id=Gallery'+ i +
     ' class="gallery-page"> ' +
     ' <div data-role="header"><h1>Gallery</h1></div> ' + ' <div data-role="content"> ' +
     ' <ul class="gallery"></ul> ' + ' </div> ' +
     ' </div> ');


    for(var x=0; x < albumPhotos[i].length; x++)
      $('#Gallery' + i + ' .gallery').append('<li><a href="' + albumPhotos[i][x] 
        + '"  rel="external"><img src="' +  albumThumbnails[i][x] + '" /></a></li>');
  };
}

// start the entire process
FB.api('169070991963/albums', checkForErrorFirst(getAlbums));

Clearly I have not tested this - but I think this will help your sanity a bit.

Andy Jones
  • 6,205
  • 4
  • 31
  • 47
  • What an incredible answer really. With a couple of lines changes this works like a charm. Well, i really cant thank you enough Andy. :D No need of libraries , no need of anything too complicated. Simple elegant solution. Truly amazing. – Johny Jaz Jun 27 '13 at 08:58
  • The more i read over and over your code again , the more i appreciate its sophistication. Out of curiosity , have you worked with this before or you just wrote that off the top of your head? What i was doing was so UGLY in comparison to this(however i am completely new to js). – Johny Jaz Jun 27 '13 at 09:25
  • Hello @Andy! Since you were the only one that could help me with this, i have one more question for you. Could you please check this one : http://stackoverflow.com/questions/17474006/facebook-javascript-api-callback-function What i am looking for now , is how to know when the API call has finished and the html page has been dynamically created. I want to put a loading widget just before i call the API (which i do) and then i want to stop the loading widget as soon as the albums have been imported. But how do i check that? – Johny Jaz Jul 05 '13 at 08:04