Использование загрузки jQuery с обещаниями - PullRequest
17 голосов
/ 28 июня 2011

Я все еще пытаюсь обернуть голову вокруг deferred, а что нет, поэтому с учетом этого у меня есть вопрос о том, как сделать следующее.

Моя командаи у меня есть 3 отдельных .load() метода, каждый из которых берет определенный шаблон и добавляет его в тот же контейнер.Каждая загрузка занимает разное время, как вы думаете, поэтому, когда загружается контент, он загружается «ступенькой» (1, затем 2, затем 3).Я хотел бы использовать deferred объектов и подождать, пока они все не будут завершены, а затем добавить их одновременно, чтобы удалить действие «шаг по лестнице».

$('<div>').load(baseInfoTemplate, function () {
    var baseData = {
        // build some object
    };

    $.tmpl(this, baseData).appendTo($generalContainer);
});

Все три звонка аналогичны звонкам выше.

Как мне этого добиться?

Ответы [ 5 ]

8 голосов
/ 06 сентября 2013

Я использую следующий код для этого случая:

$.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");
});

На мой взгляд, он выглядит более структурированным и довольно приятным, чем предыдущие реализации.

6 голосов
/ 28 июня 2011

Вы можете сохранить promise objects в массиве и использовать $.when(), чтобы узнать, выполнены ли эти обещания.Это может выглядеть так:

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);
    });
});

Я использую .apply() здесь, потому что он принимает массив в качестве аргументов для вызова функции.Таким образом, в основном, мы передаем все объекты обещаний .when().

Пример : http://jsfiddle.net/Hg77A/1/


Обновление:

какAlnitak указал, что приведенный выше пример не будет иметь большого смысла, если не будет обработчика обратного вызова «success».Если для передачи данных с помощью .load() достаточно просто запустить обработчик «все сделано», вам просто нужно .resolve() обещания в пределах success handler с .load().Это имеет какой-то смысл?

4 голосов
/ 28 июня 2011

$.load() не предназначен для использования с отложенными объектами, а также специально предназначен для немедленного размещения материалов на странице.

Чтобы решить эту последнюю проблему, необходимо либо визуализировать весь контейнер снаружиDOM, а затем добавьте его, когда все будет готово, или вам нужно собрать три результата, а затем сложить их все за один раз.

В приведенном ниже процессе используется последний подход:

  1. Используйте взамен $.get() и создайте массив объектов jqXHR, возвращаемых $.get()

  2. Сохраните возвращаемые фрагменты из каждого $.get() в массиве тоже

  3. Используйте $.when.apply($, myArray).done(function() { ... }), чтобы применить шаблоны и поместить их в DOM

См. http://jsfiddle.net/alnitak/WW3ja/

2 голосов
/ 22 июля 2014

Я использую следующий код в качестве универсальной библиотеки

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;
})();

, затем вызываю ее как первую функцию в основном js-файле приложения.

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

$( document ).ready( function() {
  templates = [ 'thingies', 'wotsits', 'oojamaflips' ];
  loader.main( templates, () => myMain());
});
1 голос
/ 11 апреля 2014

Вот Gist для небольшого плагина jQuery, который добавляет функцию loadThen к набору элементов jQuery.Это в основном load () без обратного вызова и возвращает обещание, которое разрешается только после того, как содержимое загружено и вставлено в набор выбранных элементов.

Это в основном копирование / вставка собственного кода load () jQueryза исключением того, что он возвращает обещание от фактического вызова ajax.Это позволяет получить отклоненное обещание в случае сбоя ajax.

Поскольку он основан на функциональности load (), вы можете добавить селектор после URL, разделенного пробелом, чтобы получить только фрагмент загруженного html.


Пример 1: Загрузить домашнюю страницу этого сайта в элемент с id = "container"

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

Пример 2: Загрузите заголовок домашней страницы в заголовок этой страницы

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

Содержание суть:

(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));
...