Question

I have a list of folders belonging to a user, and I want them to be able to reorder them by dragging and dropping. I can get the drag-drop to work ok, but I can't figure out how to save the results. I'm using Parse, and I think that means I have to retrieve each object, change the order attribute, and then resave it... but that doesn't seem to be working, as they all come out with the same order.

After sorting and then doing .sortable('toArray'), I have an array of folder ids, prepended with the word 'folder', so it looks something like ["folder_ab9gu3nd", "folder_kwkgiutyqo", "folder_s856skt8w"].

So, I want to save folder ab9gu3nd with order 0, kwkgiutyqo with order 1, and s856skt8w with order 2.

Here's my code ('result' is the array with the folder ids):

        for(var ii=0; ii<result.length; ii++) {
                            // Get actual id
            var folderId = result[ii].replace('folder_', '');
            var folderOrder = ii;
            var folder = Parse.Object.extend("Folder");
            var query = new Parse.Query(folder);
            query.get(folderId, {
                success: function(folder) {
                    // The object was retrieved successfully.
                    folder.set('order', folderOrder);
                    folder.save();
                }
            });
        }

When I run this, the folders all end up with order '2', as if the final value of ii was used for all of them. How do I stop this from happening? Thanks!

Was it helpful?

Solution

You're being fooled by two things:

  1. Your vars inside the loop are hoisted to the surrounding function, they're not local to the loop.
  2. Your success handlers are triggered asynchronously and they're closures over exactly the same folderOrder.

For the first one, your loop actually looks like this as far as JavaScript is concerned:

function whateverItIsCalled() {
    var folderId, folderOrder, folder, query, ii;
    //...
    for(ii = 0; ii < result.length; ii++) {
        //...
        folderOrder = ii;
        //...
    }
    //...
}

so there is only one folderOrder for the entire function and your loop simply assigns ii to it on each iteration.

The second problem is the usual "AJAX in a loop" problem: you're constructing a series of anonymous functions which are closures over the same folderOrder variable. The result of that is that the all three of your success handlers end up referencing the same folderOrder value and, by the time they are triggered, the loop will be finished and folderOrder will have its last value from the loop; the last value in your case is two.

To solve your problem, you just need to get separate folderOrder values into the various success handlers. One way is to use a function to generate your success handlers:

function makeSuccess(folderOrder) {
    return function(folder) {
        folder.set('order', folderOrder);
        folder.save();
    }
}

and then:

query.get(folderId, {
    success: makeSuccess(folderOrder)
});

Another common approach is to use a self-executing function to effectively creating a distinct scope for the loop's body:

for(var ii = 0; ii < result.length; ii++)
    (function(ii) {
        // Same stuff you currently have...
    })(ii);

Which approach you take is a matter of personal preference.

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