Using jQuery load with promises

与世无争的帅哥 提交于 2019-11-28 08:20:35

$.load() isn't designed for use with Deferred objects, and also it is intended specifically to drop stuff into the page immediately.

To solve that latter problem you either have to render the entire container outside the DOM, and then drop it in when they're all done, or you need to accumulate the three results and then put them all in in one go.

The process below uses the latter approach:

  1. Use $.get() instead, and create an array of the jqXHR objects returned by $.get()

  2. Store the return fragments from each $.get() in an array too

  3. Use $.when.apply($, myArray).done(function() { ... }) to apply the templates and put them into the DOM

See http://jsfiddle.net/alnitak/WW3ja/

I use next code for this case:

$.when(
    $.get('templates/navbar.tmpl.html', function(data) {
        $('#navbar').html(data);
    }),
    $.get('templates/footer.tmpl.html', function(data) {
        $('#footer').html(data);
    }),
    $.Deferred(function(deferred) {
        $(deferred.resolve);
    })
).done(function() {
    $.getScript("js/tools/jquery.min.js");
});

To my mind it looks more structured and pretty nice than previous implementations.

You can store the promise objects in an array and use $.when() to find out if those promises are fullfilled. This could look like this:

var templates = [ ];

function createPromise( baseInfoTemplate ) {
    return $.Deferred(function( promise ) {
        $('<div>').load(baseInfoTemplate, function() {
            var baseData = { /* foobar */ };

            templates.push( baseData );
            promise.resolve();
        });
    }).promise();
}

var myPromises = [ ];

myPromises.push( createPromise( 'some data' ) );
myPromises.push( createPromise( 'even moar data' ) );
myPromises.push( createPromise( 'foo bar heay' ) );

$.when.apply( null, myPromises ).done( function() {
    templates.forEach(function( template ) {
        $.tmpl(this, template).appendTo($generalContainer);
    });
});

I'm using .apply() here because it accepts an array as arguments for a function call. So basically, we're passing all promises objects to .when().

Example: http://jsfiddle.net/Hg77A/1/


Update:

as Alnitak pointed out, the above example wouldn't make much sense if there is no "success" callback handler. If it is just enough to fire the "all done" handler after you transfered the data with .load(), you just would need to .resolve() the promises within the success handler from .load(). Does that make any sense?

I use the following code as a generic library

var loader = {};
(function(){
  var fn = {  
    promises: [],
    templates: [],

    loadTemplate: function( name ) {
      fn.promises.push(
        $.get( `templates/${name}.tmpl.html`,
               (html) => fn.templates.push( html )
        )
      );
    },

    main: function( templates, callback ) {
      templates.forEach( (template) => fn.loadTemplate( template ));
      $.when.apply( $, fn.promises ).done( function() {
        $( '<div id="templates">' ).html( fn.templates.join() ).appendTo( 'body' );
        callback();
      });
    }
  };

  /* EXPORTS */
  loader.main = fn.main;
})();

then call it as the first function in the application's main js file.

function myMain() {
  ... // do all the things
}

$( document ).ready( function() {
  templates = [ 'thingies', 'wotsits', 'oojamaflips' ];
  loader.main( templates, () => myMain());
});

Here's a Gist for a little jQuery plugin that adds a loadThen function to a set of jQuery elements. It's basically load() without the callback and it returns a promise that is only resolved after the content is loaded and inserted into the set of selected elements.

It's basically a copy/paste of jQuery's own load() code except it returns the promise from the actual ajax call. This lets you get a rejected promise if the ajax fails.

Since it's based on the load() functionality, you can add a selector after the url seperated by a space to get only a fragment of the loaded html.


Example 1: Load the home page of this site into element with id="container"

$('#container').loadThen('/').then(function () {
    // loaded and ready.
}, function () {
    // error
});

Example 2: Load the home page's header into this page's header

$('h1').eq(0).loadThen('/ h1').then(function () {
    // loaded and ready.
}, function () {
    // error
});

Gist contents:

(function ($) {
    var _loadThen = $.fn.loadThen;
    $.fn.loadThen = function (url, params) {
        if (typeof url !== "string" && _loadThen) {
            return _loadThen.apply(this, arguments);
        }

        if(this.length <= 0) {
            return jQuery.Deferred().resolveWith(this, ['']);
        }

        var selector, type, response,
            self = this,
            off = url.indexOf(" ");

        if (off >= 0) {
            selector = jQuery.trim(url.slice(off));
            url = url.slice(0, off);
        }

        if (params && typeof params === "object") {
            type = "POST";
        }

        return jQuery.ajax({
            url: url,
            type: type,
            dataType: "html",
            data: params
        }).then(function (responseText) {
                self.html(selector ? jQuery("<div>").append(jQuery.parseHTML(responseText)).find(selector) : responseText);
            return self;
        });
    };
}(jQuery));
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!