Meteor Iron Router con waitOn personalizado
iron-router (2)
Me preguntaba si alguien ha construido su propia función waitOn. No estoy usando suscribirse para manejar el waitOn, sino que quiero esperar en un helper para activar el estado listo, algo como esto:
this.route(''edit_Record'', {
path: ''/record/edit/:_id'',
waitOn: function() {
return XXXXX;
}
});
Template.edit_Record.helpers({
selectedRecord: function () {
wait(3000);
var x = myRecords.findOne({_id: this.editid});
//XXXXX This is where I want to set ''ready''
return x;
}
});
Diseñé mi propio patrón para trabajar con un waitOn personalizado. Debe tener en cuenta que IronRouter no renderizará su plantilla (y, por lo tanto, ninguno de sus ayudantes, excepto si los llama manualmente, lo que generalmente es impar) A MENOS que todos los identificadores especificados en la función waitOn estén listos.
waitOn es un cálculo reactivo, por lo que los identificadores que especifique deben ser fuentes de datos reactivos, y cuando su estado de listo evolucione, waitOn se volverá a evaluar automáticamente y, en última instancia, notificará a IronRouter que está bien renderizar la plantilla.
Entonces, si queremos usar waitOn con algo más que los identificadores de suscripción, tenemos que implementar nuestro propio objeto con un método reactivo ready () (esto es de los documentos). Llamaremos a este objeto "Camarero" porque su función es esperar hasta que ocurra algún evento y luego establece su estado interno en listo.
Le presentaré un ejemplo simple que resuelve un problema común: precarga de imágenes. Supongamos que tiene una plantilla que representa elementos de imagen cuyos atributos src están almacenados en una Colección: le gustaría representar la plantilla solo cuando las imágenes se cargan en el lado del cliente.
<template name="view">
<div>
<h1>{{title}}</h1>
<img src="{{firstImageUrl}}" />
<img src="{{secondImageUrl}}" />
</div>
</template>
Se me ocurrió la siguiente interfaz:
this.route("view",{
path:"/view/:_id",
loadingTemplate:"loadingTemplate",
template:"view",
// our Waiter object handle designed to wait until images are loaded
imagePreloadingWaiter:new ImagePreloadingWaiter(),
// load is called only once each time the route is triggered
load:function(){
// reset our waiter
this.imagePreloadingWaiter.reset();
},
// before : reactive computation that will be rerun until the route template is rendered
before:function(){
// setup collection subscription
var subscriptionHandle=this.subscribe("collectionById",this.params._id);
if(subscriptionHandle.ready()){
// get the route data context
var collection=this.data();
// collect the images URLs we want to preload
var params={
images:[
collection.firstImageUrl,
collection.secondImageUrl
]
};
// fire the preloader
this.imagePreloadingWaiter.fire(params);
}
},
// we specify that we want to wait on our ImagePreloadingWaiter handle
waitOn:function(){
return this.imagePreloadingWaiter;
},
// return the data context used by this route
data:function(){
return Collection.findOne(this.params._id);
}
});
Al usar esta definición de ruta, mostramos la plantilla de carga hasta que finalmente se carguen las URL de las imágenes almacenadas en nuestra colección, gracias al método waitOn que espera en nuestro identificador de la interfaz ImagePreloadingWaiter.
Ok, ahora que tenemos una visión general de la interfaz que nos gustaría usar, impleméntela de hecho:
// Simple interface to use with the IronRouter waitOn method
Waiter=function(){
// avoid firing the waiter multiple time in a Deps.Computation context
this.isFired=false;
// reactive data source : have we been waiting long enough ?
this.isReady=false;
this.dependency=new Deps.Dependency();
};
_.extend(Waiter.prototype,{
// reset method, clear the waiter state
reset:function(){
this.isFired=false;
//
this.isReady=false;
this.dependency.changed();
},
// reactive ready method : this is the interface needed by waitOn
ready:function(){
this.dependency.depend();
return this.isReady;
},
// fire the Waiter object only once before being resetted
fire:function(params){
if(!this.isFired){
this.isFired=true;
// this abstract method must be overloaded in child classes
this.wait(params);
}
},
// must be called in Waiter.wait() to acknowledge we''re done waiting
waitedEnough:function(){
// if we have reset the Waiter meanwhile, silently discard the notification
if(this.isFired){
this.isReady=true;
this.dependency.changed();
}
}
});
// Simple waiter that simply waits N seconds before getting ready
TimeoutWaiter=function(){
Waiter.call(this);
};
TimeoutWaiter.prototype=Object.create(Waiter.prototype);
_.extend(TimeoutWaiter.prototype,{
wait:function(params){
var self=this;
// after N seconds, notify that we are done waiting
Meteor.setTimeout(function(){
self.waitedEnough();
},params.seconds*1000);
}
});
// Image preloader for the IronRouter
ImagePreloadingWaiter=function(){
Waiter.call(this);
};
ImagePreloadingWaiter.prototype=Object.create(Waiter.prototype);
_.extend(ImagePreloadingWaiter.prototype,{
wait:function(params){
var self=this;
//
if(images.length>0){
var imageLoadedCounter=0;
_.each(images,function(imageUrl){
function onImageLoadOrError(){
imageLoadedCounter++;
if(imageLoadedCounter==images.length){
self.waitedEnough();
}
}
//
var image=$("<img/>");
image.load(onImageLoadOrError);
image.error(onImageLoadOrError);
image.prop("src",imageUrl);
});
}
else{
self.waitedEnough();
}
}
});
Con este ejemplo, estoy seguro de que encontrarás una buena solución para responder a tu pregunta.
En particular, creo que es posible que desee mover su código lógico "auxiliar" dentro del enlace antes de IronRouter. No dude en hacer preguntas si mi código no está claro.
Podría usar algo sin ironRouter, es decir, en su plantilla. Suponiendo que tiene una plantilla llamada ''carga'' y está usando un diseño llamado ''diseño'' configurado con ironrouter
HTML
<template name="layout">
{{#if isready}}
{{yield}}
{{else}}
{{>loading}}
{{/if
</template>
Javascript (lado del cliente)
Template.layout.isready = function() {
return !Session.get("isnotready");
}
Template.edit_Record.helpers({
selectedRecord: function () {
Session.set("isnotready", true);
wait(3000);
var x = myRecords.findOne({_id: this.editid});
Session.set("isnotready, false);
return x;
}
});