カスタムのwaitOnで動作する独自のパターンを設計しました。waitOn 関数で指定されたすべてのハンドルが準備できていない限り、IronRouter はテンプレートをレンダリングしないことに注意してください (したがって、手動で呼び出した場合を除いてヘルパーはレンダリングされません)。
waitOn はリアクティブな計算であるため、指定するハンドルはリアクティブなデータ ソースである必要があり、準備状態が変化するにつれて、waitOn は自動的に再評価され、最終的にテンプレートをレンダリングしてもよいことが IronRouter に通知されます。
そのため、サブスクリプション ハンドル以外で waitOn を使用する場合は、リアクティブな ready() メソッドを使用して独自のオブジェクトを実装する必要があります (これはドキュメントからのものです)。このオブジェクトを「ウェイター」と呼びます。その役割は、何らかのイベントが発生するまで待機し、内部状態を準備完了に設定することだからです。
一般的な問題である画像のプリロードを解決する簡単な例を紹介します。src 属性が Collection に格納されている画像要素をレンダリングするテンプレートがあるとします。画像がクライアント側で読み込まれるときにのみテンプレートをレンダリングしたいとします。
<template name="view">
<div>
<h1>{{title}}</h1>
<img src="{{firstImageUrl}}" />
<img src="{{secondImageUrl}}" />
</div>
</template>
次のインターフェースを思いつきました:
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);
}
});
ImagePreloadingWaiter インターフェイス ハンドルを待機する waitOn メソッドのおかげで、このルート定義を使用して、コレクションに保存されている画像 URL が最終的に読み込まれるまで、読み込みテンプレートを表示します。
使用したいインターフェースの概要が分かったので、実際に実装してみましょう。
// 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();
}
}
});
この例を使用して、あなたの質問に答える良い解決策を見つけられると確信しています。
特に、「ヘルパー」ロジック コードを IronRouter フックの前に移動したい場合があると思います。私のコードが不明な場合は、遠慮なく質問してください。