【问题标题】:IronRouter: template is rendered before call in waitOn is finishedIronRouter:模板在waitOn调用完成之前渲染
【发布时间】:2015-03-15 10:25:27
【问题描述】:

我想根据我收藏的数据创建图表。 此数据由我的路由器设置中的聚合拉取调用,并由 template.rendered 函数中的 Session.data 设置。

Meteor.call 被放置在 waitOn 函数中。 如果模板被渲染,则数据不存在。

我尝试了 onBeforeAction、action、setTimeout... 但我无法将渲染函数设置为等到调用数据出现。

我尝试在我的 RouteController 和 Router.route 的操作、waitOn 和数据函数中设置 onBeforeAction 和 onRun 挂钩中的调用。

我用 setTimeout 包装了我的渲染代码,但它不起作用。

Router.configure({
  layoutTemplate: 'global',
  loadingTemplate: 'loading',
  notFoundTemplate: 'notFound',
});
Router.onBeforeAction("loading");

在我的全局路由设置中设置。

我已经尝试过以下解决方案:
question 23575826
question 26198531
https://github.com/EventedMind/iron-router/issues/554#issuecomment-39002306
最后几天还有更多。

对我的路由器设置或其他解决此问题并及时呈现数据的方法有什么建议吗? 我考虑选择 npm-modules fiber/future,但我不知道如何嵌入和使用它们。

我的设置: 流星是 v1.0.2.1

router.js 自带控制器

StatsController = RouteController.extend({
  template: 'statsShow',
  waitOn: function () {
    return [
      Meteor.call('saleGetDataPerYear', 'nYWpgxR3kEY8kwBkA', 'SellerOne', 2014, function(error, result){
        if(!error)
          Session.set('brockhausUnits', result['units']);
          Session.set('brockhausVolumes', result['volumes']); 
      }),
      Meteor.call('saleGetDataPerYear', 'nYWpgxR3kEY8kwBkA', 'SellerTwo', 2014, function(error, result){
        if(!error)
          Session.set('info3Units', result['units']);
          Session.set('info3Volumes', result['volumes']); 
      }),
      Meteor.call('saleGetDataPerYear', 'nYWpgxR3kEY8kwBkA', 'SellerThree', 2014, function(error, result){
        if(!error)
          Session.set('avaUnits', result['units']);
          Session.set('avaVolumes', result['volumes']);  
      })
    ];
  },
  data: function () {
    return Books.findOne({_id: this.params._id});
  },
  action: function () {
    if (!this.ready()) {
      this.render('Loading');
    } else {
      this.render(); 
    }
  }
});

Router.route('stats/show/', {
  name: 'stats.show',
  controller: 'TestController'
});

methods.js

Meteor.methods({
  saleGetDataPerYear: function(bookId, seller, year) {
    var sellerUnits = [];
    var sellerVolumes = [];
    var resultData = {};

    var pipeline = [
      {
        $match : { bookId: bookId, salesSeller: seller, salesYear: year }
      },
      {
        $group : {
          _id : {
            sale: { "salesMonth": "$salesMonth" }
          },
          units: { $sum: "$salesUnits" },
          volumes: { $sum: "$salesVolumes" },
          month: { $first: "$salesMonth" },
          year: { $first: "$salesYear" },
          seller: { $first: "$salesSeller" }   
        }
      },
      {
        $sort : {
          month: 1
        }
      }      
    ];    
    result = Sales.aggregate(pipeline);              

    if(result){
      sellerUnits.push(seller);
      sellerVolumes.push(seller);
      result.forEach(function(data){
        sellerUnits.push(data.units);
        sellerVolumes.push(data.volumes);
      });
      resultData['units'] = sellerUnits;
      resultData['volumes'] = sellerVolumes;
    }

    if(resultData){
      return resultData;     
    } else {
      throw new Meteor.Error("no-data", "No Data collected");
    }
  }

模板

//-- template rendered functions
Template.statsShow.rendered = function(){ 
    var chartUnitsBrockhaus = Session.get('brockhausUnits');
    var chartUnitsInfo3 = Session.get('info3Units');
    var chartUnitsAva = Session.get('avaUnits');
    var chartUnitsSumme = Session.get('sumUnits');

    console.log(chartUnitsBrockhaus);

    var chartUnits = c3.generate({
      bindto: this.find('.chartUnits'),
      data: {
        columns: [
          chartUnitsBrockhaus,
          chartUnitsInfo3,
          chartUnitsAva,
          chartUnitsSumme   
        ],
        type: 'bar',
        types: {
          Summe: 'spline',
        },
      },
      axis: {
        x: {
          type: 'category',
            categories: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez']
        }
      },
      bar: {
        width: {
          ratio: 0.5
        }
      }
    });       
};

accounts-password             1.0.5
accounts-ui                   1.1.4 
alanning:roles                1.2.13
aldeed:autoform               4.2.2 
aldeed:autoform-select2       1.0.3
aldeed:collection2            2.3.1
aldeed:simple-schema          1.3.0 
anti:fake                     0.4.1
chrismbeckett:fontawesome4    4.2.2 
coffeescript                  1.0.5 
ctjp:meteor-bootstrap-switch  3.3.1_1 
dburles:collection-helpers    1.0.2 
francocatena:status           1.0.3  
iron:router                   1.0.7 
lepozepo:accounting           1.0.0 
less                          1.0.12  
matteodem:easy-search         1.4.6  
meteor-platform               1.2.1 
meteorhacks:aggregate         1.1.0   
mrt:jquery-csv                0.7.1  
natestrauser:select2          3.5.1  
nemo64:bootstrap              3.3.1_1 
ongoworks:security            1.0.1  
peerlibrary:xml2js            0.4.4_3 
peernohell:c3                 1.1.2 
sacha:spin                    2.0.4  
service-configuration         1.0.3  
underscore                    1.0.2 
zimme:select2-bootstrap3-css  1.4.1

编辑
正如@DavidWeldon 提到的,我将 waitOn 函数更改为:

waitOn: function () {
  return [
    // first call
    Meteor.callWithReady('saleGetDataPerYear', 'nYWpgxR3kEY8kwBkA', 'Brockhaus', 2014, function(error, result){
      if(!error) {
        console.log(result); //debug
        Session.set('brockhausUnits', result['units']);
        Session.set('brockhausVolumes', result['volumes']);
      };          
    }),
    // second call
    Meteor.callWithReady('saleGetDataPerYear', 'nYWpgxR3kEY8kwBkA', 'Info3', 2014, function(error, result){
      if(!error) {
        console.log(result); //debug
        Session.set('brockhausUnits', result['units']);
        Session.set('brockhausVolumes', result['volumes']);
      };            
    }),
    // third call
    Meteor.callWithReady('saleGetDataPerYear', 'nYWpgxR3kEY8kwBkA', 'AVA', 2014, function(error, result){
      if(!error) {
        console.log(result); //debug
        Session.set('brockhausUnits', result['units']);
        Session.set('brockhausVolumes', result['volumes']);
      };            
    }),
    // fourth call
    Meteor.callWithReady('saleGetSumDataPerYear', 'nYWpgxR3kEY8kwBkA', 2014, function(error, result){
      if(!error) {
        console.log(result); //debug
        Session.set('sumUnits', result['units']);
        Session.set('sumVolumes', result['volumes']); 
      }           
    })   
  ];
},

并在/lib下添加了test.coffee

_.defaults Meteor,
  callWithReady: (method, options...) ->
    dep = new Deps.Dependency
    ready = false

    lastOption = _.last options
    if _.isFunction lastOption
      Meteor.apply method, _.initial(options), (err, result) ->
        lastOption err, result
        ready = true
        dep.changed()
    else
      Meteor.apply method, options, (err, result) ->
        ready = true
        dep.changed()

    ready: ->
      dep.depend()
      ready

结果是:我的通话循环。

我测试了@apendua 的答案。

function waitUntilDone (action) {
  var isReady = new ReactiveVar(false);
  action(function () {
    isReady.set(true);
  });
  return {
    ready: function () {
      return isReady.get();
    }
  };
}

waitOn: function () {
  return [
    // first call
    waitUntilDone(function(done) {
      Meteor.callWithReady('saleGetDataPerYear', 'nYWpgxR3kEY8kwBkA', 'Brockhaus', 2014, function(error, result){
        if(!error) {
          console.log(result); //debug
          Session.set('brockhausUnits', result['units']);
          Session.set('brockhausVolumes', result['volumes']);
        };
        done();             
      })
    }),
    // second call
    waitUntilDone(function(done) {
      Meteor.call('saleGetDataPerYear', 'nYWpgxR3kEY8kwBkA', 'Info3', 2014, function(error, result){
        if(!error) {
          console.log(result); //debug
          Session.set('brockhausUnits', result['units']);
          Session.set('brockhausVolumes', result['volumes']);
          done();
        };            
      })
    }),
    // third call
    waitUntilDone(function(done) {
      Meteor.call('saleGetDataPerYear', 'nYWpgxR3kEY8kwBkA', 'AVA', 2014, function(error, result){
        if(!error) {
          console.log(result); //debug
          Session.set('brockhausUnits', result['units']);
          Session.set('brockhausVolumes', result['volumes']);
          done(); 
        };            
      })
    }),
    // fourth call
    waitUntilDone(function(done) {
      Meteor.call('saleGetSumDataPerYear', 'nYWpgxR3kEY8kwBkA', 2014, function(error, result){
        if(!error) {
          console.log(result);
          Session.set('sumUnits', result['units']);
          Session.set('sumVolumes', result['volumes']); 
          done(); 
        }           
      })
    })   
  ];
},

waitOn: function () {
  return [
    // first call
    waitUntilDone(function(done) {
      Meteor.callWithReady('saleGetDataPerYear', 'nYWpgxR3kEY8kwBkA', 'Brockhaus', 2014, function(error, result){
        if(!error) {
          console.log(result); //debug
          Session.set('brockhausUnits', result['units']);
          Session.set('brockhausVolumes', result['volumes']);
        };
        done();             
      }),
      Meteor.call('saleGetDataPerYear', 'nYWpgxR3kEY8kwBkA', 'Info3', 2014, function(error, result){
        if(!error) {
          console.log(result); //debug
          Session.set('brockhausUnits', result['units']);
          Session.set('brockhausVolumes', result['volumes']);
          done();
        };            
      })
      [...]
    })   
  ];
},

两个结果都是:我的调用循环。

【问题讨论】:

  • waitOn 不能直接与Meteor.call 一起使用,您需要一些更详细的代码才能使其正常工作;你在哪里看到过这样的例子?
  • 你可以看到我的 Meteor.callWithReady here 的实现。它在早期版本的 IR 中对我们有用,但我没有用 1.x 测试它。您可以尝试用Meteor.callWithReady 替换您的Meteor.call,看看它是否有效。
  • @apendua 这不是一个例子。我想到了。我需要聚合函数来总结一些收集数据,但我不知道这将如何与订阅一起使用,所以我选择了调用...
  • 顺便说一句,在if (!error) 之后,您可能需要使用大括号来创建新块。缩进不会像python 那样创建新块。
  • @David 谢谢,我会测试你的解决方案并报告

标签: meteor iron-router


【解决方案1】:

您在这里缺少的是,在您的 waitOn 中,您需要返回一个对象列表,每个对象都有一个 ready 方法,该方法充当反应数据源。不幸的是,Meteor.call 不会返回这种对象,但例如 Meteor.subscribe 会。

您可以做的是使用以下包装器,但请确保首先将 reactive-var 包添加到您的项目中。

function waitUntilDone (action) {
  var isReady = new ReactiveVar(false);
  action(function () {
    isReady.set(true);
  });
  return {
    ready: function () {
      return isReady.get();
    }
  };
}

现在,而不是像这样返回Meteor.call 的结果列表

waitOn: function () {
  return [
    Meteor.call(..., function () { ... }),
    Meteor.call(..., function () { ... }),
    // ...
  ]
}

按以下方式使用上面的包装器

waitOn: function () {
  return [
    waitUntilDone(function(done) {
      Meteor.call(..., function () {
        // ...
        done();
      }),
    }),
    // ...
  ]
}

【讨论】:

  • 我对其进行了测试,但它开始循环我的通话。也许每个函数只能调用一次?
  • 每一个Meteor.call 应该单独包装,如果这是你的要求。如果我要为您提供更多线索,您需要显示更多代码。
  • 这就是我所做的。我还尝试将所有内容放在一个“waitUntilDone”函数中。
  • 您是否有理由无法将聚合结果插入到一个或多个集合服务器端然后发布它们?然后,在上述 cmets 的基础上,您只需将订阅推送到 waitOn 中的等待列表数组,并等待它们都准备好。
【解决方案2】:

更新:经过几天的测试,我将函数更改为 Meteor.publish 而不是 Meteor.method,因此 waitOn 函数现在可以工作了。 我没有意识到这也适用于发布。聚合 db-calls 的示例都带有Meteor.method

publications.js

Meteor.publish('saleGetAllDataPerYear', function(bookId, year) {
  self = this;

  var pipeBH = [];
  var resultBH = '';
  var unitsBH = [];
  var volumesBH = [];
  var monthBH = [];

  var pipeI3 = [];
  var resultI3 = '';
  var unitsI3 = [];
  var volumesI3 = [];
  var monthI3 = [];

  var pipeAVA = [];
  var resultAVA = '';
  var unitsAVA = [];
  var volumesAVA = [];
  var monthAVA = [];

  var pipeSum = [];
  var resultSum = '';
  var unitsSum = [];
  var volumesSum = [];
  var monthSum = [];

  // Set Brockhaus data  
  pipeBH = [
    { $match : { bookId: bookId, salesSeller: 'Brockhaus', salesYear: year } },
    { $group : { _id : { sale: { "salesMonth": "$salesMonth" } }, 
        units: { $sum: "$salesUnits" }, volumes: { $sum: "$salesVolumes" }, month: { $first: "$salesMonth" }, year: { $first: "$salesYear" }, seller: { $first: "$salesSeller" }   
      }
    },
    { $sort : { month: 1 } }
  ];
  resultBH = Sales.aggregate(pipeBH);

  if(resultBH != ''){
    unitsBH.push('Brockhaus');
    volumesBH.push('Brockhaus');
    resultBH.forEach(function(data){
      unitsBH.push(data.units);
      volumesBH.push(data.volumes);
      monthBH.push(data.month);
    });
    self.added('stats', Random.id(), {seller: 'Brockhaus', units: unitsBH, volumes: volumesBH, month: monthBH, year: year});
    self.ready();
  } else {
    self.ready();
  }

  // Set Info3 data
  pipeI3 = [
    { $match : { bookId: bookId, salesSeller: 'Info3', salesYear: year } },
    { $group : { _id : { sale: { "salesMonth": "$salesMonth" } }, 
        units: { $sum: "$salesUnits" }, volumes: { $sum: "$salesVolumes" }, month: { $first: "$salesMonth" }, year: { $first: "$salesYear" }, seller: { $first: "$salesSeller" }   
      }
    },
    { $sort : { month: 1 } }
  ];
  resultI3 = Sales.aggregate(pipeI3);

  if(resultI3 != ''){
    unitsI3.push('Info3');
    volumesI3.push('Info3');
    resultI3.forEach(function(data){
      unitsI3.push(data.units);
      volumesI3.push(data.volumes);
      monthI3.push(data.month);
    });
    self.added('stats', Random.id(), {seller: 'Info3', units: unitsI3, volumes: volumesI3, month: monthI3, year: year});
    self.ready();
  } else {
    self.ready();
  }

  // Set AVA data
  pipeAVA = [
    { $match : { bookId: bookId, salesSeller: 'AVA', salesYear: year } },
    { $group : { _id : { sale: { "salesMonth": "$salesMonth" } }, 
        units: { $sum: "$salesUnits" }, volumes: { $sum: "$salesVolumes" }, month: { $first: "$salesMonth" }, year: { $first: "$salesYear" }, seller: { $first: "$salesSeller" }   
      }
    },
    { $sort : { month: 1 } }
  ];
  resultAVA = Sales.aggregate(pipeAVA);

  if(resultAVA != ''){
    unitsAVA.push('AVA');
    volumesAVA.push('AVA');
    resultAVA.forEach(function(data){
      unitsAVA.push(data.units);
      volumesAVA.push(data.volumes);
      monthAVA.push(data.month);
    });
    self.added('stats', Random.id(), {seller: 'AVA', units: unitsAVA, volumes: volumesAVA, month: monthAVA, year: year});
    self.ready();
  } else {
    self.ready();
  }

  // Set Sum data
  pipeSum = [
    { $match : { bookId: bookId, salesYear: year } },
    { $group : { _id : { sale: { "salesMonth": "$salesMonth" } }, 
        units: { $sum: "$salesUnits" }, volumes: { $sum: "$salesVolumes" }, month: { $first: "$salesMonth" }, year: { $first: "$salesYear" }, seller: { $first: "$salesSeller" }   
      }
    },
    { $sort : { month: 1 } }
  ];
  resultSum = Sales.aggregate(pipeSum);

  if(resultSum != ''){
    unitsSum.push('Summe');
    volumesSum.push('Summe');
    resultSum.forEach(function(data){
      unitsSum.push(data.units);
      volumesSum.push(data.volumes);
      monthSum.push(data.month);
    });
    self.added('stats', Random.id(), {seller: 'Summe', units: unitsSum, volumes: volumesSum, month: monthSum, year: year});
    self.ready();
  } else {
    self.ready();
  }
});

router.js

waitOn: function () {
  year = Number(Session.get('year'));
  return [
    Meteor.subscribe('saleGetAllDataPerYear', this.params._id, year),
    Meteor.subscribe('getStats')
  ];
},

感谢@JeremyS 以另一种方式提供灵感。这听起来像是更好的解决方案,因为现在waitOn 函数可以工作,但是如果没有手动刷新我的模板,数据就不会呈现在我的图表中。

【讨论】:

    猜你喜欢
    • 2014-12-08
    • 2014-11-15
    • 1970-01-01
    • 2017-05-17
    • 2018-04-21
    • 1970-01-01
    • 2014-05-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多