Meteor Iron Router with custom waitOn

谁说我不能喝 提交于 2019-12-22 01:17:09

问题


I was wondering whether anyone has built their own waitOn function? I am not using subscribe to drive the waitOn but rather I want to wait on a helper to trigger the ready state, something like this:

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

回答1:


I've designed my own pattern to work with a custom waitOn. You should be aware that IronRouter won't render your template (and thus none of its helpers except if you call them manually which is usually odd) UNLESS every handles specified in the waitOn function are ready.

waitOn is a reactive computation, so the handles you specify should be reactive data sources, and as their ready state evolves, waitOn will be reevaluated automatically and ultimately it will notify the IronRouter that it's OK to render the template.

So if we want to use waitOn with something else than subscription handles, we have to implement our own object with a reactive ready() method (this is from the docs). We'll call this object a "Waiter" because its role is to wait until some event happens and then it sets his internal state to ready.

I'll introduce you a simple example which solves a common problem : image preloading. Suppose you have a template rendering image elements whose src attributes are stored in a Collection : you'd like to render the template only when the images are loaded client-side.

<template name="view">
    <div>
        <h1>{{title}}</h1>
        <img src="{{firstImageUrl}}" />
        <img src="{{secondImageUrl}}" />
    </div>
</template>

I came up with the following interface :

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

Using this route definition, we display the loading template until the images URLs stored in our collection are finally loaded, thanks to the waitOn method which waits on our ImagePreloadingWaiter interface handle.

Ok so now that we have an overview of the interface we'd like to use, let's actually implement it :

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

Using this example I'm sure you will figure out a nice solution to answer your question.

In particular, I think you may want to move your "helper" logic code inside the before IronRouter hook. Don't hesitate to ask questions if my code is unclear.




回答2:


You could use something without ironRouter i.e in your template. Assuming you have a template called 'loading' and you're using a layout called 'layout' set with ironrouter

HTML

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

Javascript (Client side)

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


来源:https://stackoverflow.com/questions/20968383/meteor-iron-router-with-custom-waiton

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!