funcion - Usando jQuery load con promesas
jquery load script (5)
Puede almacenar los promise objects
en una matriz y usar $.when()
para averiguar si esas promesas están cumplidas. Esto podría verse así:
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);
});
});
Estoy usando .apply()
aquí porque acepta una matriz como argumentos para una llamada de función. Básicamente, estamos pasando todos los objetos de promesas a .when()
.
Ejemplo : http://jsfiddle.net/Hg77A/1/
Actualizar:
como señaló Alnitak, el ejemplo anterior no tendría mucho sentido si no hay un controlador de devolución de llamada "exitoso". Si es suficiente disparar el controlador "todo hecho" después de transferir los datos con .load()
, solo necesitaría .resolve()
las promesas dentro del success handler
de .load()
. ¿Eso tiene algún sentido?
Todavía estoy tratando de envolver mi cabeza de forma deferred
y qué no, así que con esto en mente, tengo una pregunta sobre cómo hacer lo siguiente.
Mi equipo y yo tenemos 3 métodos .load()
separados que cada uno toma una plantilla específica y la agrega al mismo contenedor. Cada carga toma una cantidad diferente de tiempo como usted podría pensar, por lo que cuando se carga el contenido, se carga en forma de "escalones" (1, luego 2, luego 3). Me gustaría hacer uso de los deferred y esperar hasta que todos estén listos, luego agregarlos al mismo tiempo para eliminar la acción ''escalón''.
$(''<div>'').load(baseInfoTemplate, function () {
var baseData = {
// build some object
};
$.tmpl(this, baseData).appendTo($generalContainer);
});
Las tres llamadas son similares a la llamada anterior.
¿Cómo puedo conseguir esto?
Uso el siguiente código como biblioteca genérica
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;
})();
luego llámelo como la primera función en el archivo js principal de la aplicación.
function myMain() {
... // do all the things
}
$( document ).ready( function() {
templates = [ ''thingies'', ''wotsits'', ''oojamaflips'' ];
loader.main( templates, () => myMain());
});
Yo uso el siguiente código para este caso:
$.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");
});
En mi opinión, se ve más estructurado y bastante agradable que las implementaciones anteriores.
$.load()
no está diseñado para su uso con objetos diferidos, y también está diseñado específicamente para colocar cosas en la página de inmediato.
Para resolver ese último problema, debe hacer que todo el contenedor quede fuera del DOM, y luego soltarlo cuando estén listos, o acumular los tres resultados y luego colocarlos todos de una vez.
El proceso a continuación utiliza el último enfoque:
Use
$.get()
lugar, y cree una matriz de los objetosjqXHR
devueltos por$.get()
Almacene los fragmentos de retorno de cada
$.get()
en una matriz tambiénUse
$.when.apply($, myArray).done(function() { ... })
para aplicar las plantillas y colocarlas en el DOM
Aquí hay un Gist para un pequeño complemento de jQuery que agrega una función loadThen a un conjunto de elementos jQuery. Básicamente es load () sin devolución de llamada y devuelve una promesa que solo se resuelve una vez que el contenido se carga y se inserta en el conjunto de elementos seleccionados.
Es básicamente una copia / pegado del propio código de carga () de jQuery, excepto que devuelve la promesa de la llamada ajax real. Esto le permite obtener una promesa rechazada si el ajax falla.
Ya que se basa en la funcionalidad de carga (), puede agregar un selector después de la URL separada por un espacio para obtener solo un fragmento del html cargado.
Ejemplo 1: Cargue la página de inicio de este sitio en el elemento con id = "contenedor"
$(''#container'').loadThen(''/'').then(function () {
// loaded and ready.
}, function () {
// error
});
Ejemplo 2: Cargar el encabezado de la página de inicio en el encabezado de esta página
$(''h1'').eq(0).loadThen(''/ h1'').then(function () {
// loaded and ready.
}, function () {
// error
});
Contenido esencial:
(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));