2

i want a loading template to appear before the site has all data to be rendered.

And after the serverside Method gives me the Data(from an API [async]) via Meteor.call i want to load the correct layout.

I tried many ways found on Google which describe similar but not exatcly the same problem. Including the way to define a function with ready handle, also doesn´t work. I can´t get it running.

i don´t want to use Collections because this is user specific Data.( i think this is not efficient to make a collection for each user [no logged in users], or do i miss something) Is this possible?

Here my Code. The console logs 2 befor 1.

Router.route('/search/:term',{
    name: 'search',
    loadingTemplate: 'loading',
    waitOn : function(){
        var term = this.params.term;
        //i think here has be something differnet either with return subscribe or function with ready-handle
        Meteor.call('search',term,function(err, response) {
            Session.set('shops', response);
            console.log(1);
        });
    },
    action : function(){
        console.log(2);
        this.render();
    }
});

Template.search.helpers(
    {
        "shops" : function(){
            return Session.get('shops');
        }
    }
);

Server Side Method returns an Array.

Thanks for help

Sven Delueg
  • 1,001
  • 11
  • 23
  • Right now since you are not returning a handle with a ready function from `waitOn`, `action` will execute immediately. Do you still have the code returning a function with a ready handle that you tried? – Curtis Apr 28 '15 at 18:51
  • No .. When i did this and checked if this.ready() in the action method it was always true and it triggered/rerun infinite – Sven Delueg Apr 28 '15 at 19:27

2 Answers2

4

Iron Router's waitOn will not wait on a Meteor.call(). Instead, the way to set this up is to subscribe to the record set in waitOn, publish a function which contains the Meteor.call(), and then create a client-side collection for each user to receive the results of the call. It will look something like the following:

Client:

// create this collection only on the client
// to receive publication on a per-user basis

Search = new Mongo.Collection('search');

Route:

Router.route('/search/:term',{
  waitOn : function(){
    var term = this.params.term;
    return Meteor.subscribe('search', term);
  }
});

Server:

Meteor.publish('search', function(term){
  check(term, String);
  var self = this;
  Meteor.call('search', term, function(error, result){
    if (result){
      self.added("search", "mySearch", {results: result});
      self.ready();
    } else {
      self.ready();
    }
  });
});

Meteor.methods({
  search: function(term){
    //do the api call and return it
  }
});

I recommend taking a look at the example of the low-level added/changed/removed publish function in the Meteor documentation here. It is a dense section but ultimately contains what you need to get your use case working.

Jeremy S.
  • 4,599
  • 3
  • 18
  • 25
  • Works like a charm .. just added return statement before Meteor.subscribe('search', term); otherwise i got n exception and the loading hook is not triggered. Thanks a lot – Sven Delueg Apr 29 '15 at 11:40
  • @JeremyS. Hello there! Thank you for posting this solution. Its very helpful! Following your solution, I would like to incorporate a `show.modal()` in my `Meteor.call()`. Any suggestions would be appreciated. [link] (https://stackoverflow.com/questions/46306445/how-do-i-modal-show-from-the-server-inside?noredirect=1#comment79575051_46306445) – SirBT Sep 19 '17 at 18:10
3

I know you've accepted the answer about getting this done via creating a psuedo-publication, but I think there's a more fitting solution.

The good folks at ars-nebula already did the work to make a Meteor method call wait-able using the https://github.com/arsnebula/reactive-promise/ library.

Get the promise via:

var $dep = new $.Deferred();
Meteor.call("myMethod", function(err, result) {
  if (err) { $dep.reject(err); }
  $dep.resolve(result);
});

var promise = $dep.promise();

Then wait on it via

Router.route('/route', {
  "loadingTemplate": loading,
  "waitOn": function() {
    return ReactivePromise.when("myTask", $def1.promise(), $def2.promise());
  },
  "action": function() {
    this.render("myTemplate");
  }
});

As a shortcut, the Meteor.promise library (https://atmospherejs.com/deanius/promise) can get you a Promise for a method call with merely

Meteor.promise("methodName", args);

Pubsub is flexible, but reactivity goes beyond that - give this a try and let me know!

Dean Radcliffe
  • 2,194
  • 22
  • 29