1

誰かが独自のwaitOn関数を構築したかどうか疑問に思っていましたか? サブスクライブを使用してwaitOnを駆動するのではなく、次のようなヘルパーを待機して準備完了状態をトリガーしたい:

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

2 に答える 2

2

カスタムの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 フックの前に移動したい場合があると思います。私のコードが不明な場合は、遠慮なく質問してください。

于 2014-01-07T11:07:30.747 に答える
1

テンプレートで、ironRouter を使用せずに何かを使用できます。「loading」というテンプレートがあり、ironrouter で設定された「layout」というレイアウトを使用しているとします。

HTML

<template name="layout">
    {{#if isready}}
      {{yield}}
    {{else}}
     {{>loading}}
    {{/if
</template>

Javascript (クライアント側)

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;
    }
});
于 2014-01-07T13:05:51.037 に答える