Ожидание сбора метеор до конца следующего шага

У меня есть шаблон Meteor, который должен отображать некоторые данные.

Template.svg_template.rendered = function () {
  dataset_collection = Pushups.find({},{fields: { date:1, data:1 }}, {sort: {date: -1}}).fetch();

  a = moment(dataset_collection[0].date, "YYYY/M/D");
  //more code follows that is also dependent on the collection being completely loaded
};

Иногда это срабатывает, иногда я получаю эту ошибку:

Исключение из функции Deps afterFlush: TypeError: невозможно прочитать свойство 'date' of undefined

Я не использую Deps в любом контексте. Насколько я понимаю, коллекция ссылается прежде, чем она полностью закончит загрузку.

Поэтому я хотел бы выяснить, как просто сказать: "Подождите, пока коллекция не будет найдена, прежде чем двигаться дальше". Должно быть простым, но не может найти обновленное решение.

Ответ 1

Вы правы, вы должны убедиться, что код в зависимости от выбора содержимого подписанной коллекции на стороне клиента выполняется ПОСЛЕ того, как данные правильно загружены.

Вы можете достичь этого, используя новый шаблон, введенный в Meteor 1.0.4: https://docs.meteor.com/#/full/Blaze-TemplateInstance-subscribe

client/views/svg/svg.js

Template.outer.onCreated(function(){
  // subscribe to the publication responsible for sending the Pushups
  // documents down to the client
  this.subscribe("pushupsPub");
});

client/views/svg/svg.html

<template name="outer">
  {{#if Template.subscriptionsReady}}
    {{> svgTemplate}}
  {{else}}
    Loading...
  {{/if}}
</template>

В объявлении шаблона Spacebars мы используем инкапсулирующий шаблон outer для обработки шаблона подписки на уровне шаблона. Мы подписываемся на публикацию в событии lifecycle onCreated, и мы используем специальный реактивный помощник Template.subscriptionsReady, чтобы отображать только svgTemplate после того, как подписка будет готова (данные доступны в браузере). На этом этапе мы можем безопасно запросить коллекцию Pushups в событии lifecycle svgTemplate onRendered, потому что мы убедились, что данные сделали свой путь к клиенту:

Template.svgTemplate.onRendered(function(){
  console.log(Pushups.find().fetch());
});

В качестве альтернативы вы можете использовать iron:router (https://github.com/iron-meteor/iron-router), который предоставляет другой шаблон проектирования для достижения этой общей проблемы, связанной с Метеор, перемещая обработку подписки на уровень маршрута вместо уровня шаблона.

Добавьте пакет в свой проект:

meteor add iron:router

lib/router.js

Router.route("/svg", {
  name: "svg",
  template: "svgTemplate",
  waitOn: function(){
    // waitOn makes sure that this publication is ready before rendering your template
    return Meteor.subscribe("publication");
  },
  data: function(){
    // this will be used as the current data context in your template
    return Pushups.find(/*...*/);
  }
});

Используя этот простой фрагмент кода, вы получите то, что хотите, и добавьте много дополнительных функций. Вы можете взглянуть на руководство Iron Router, в котором подробно описываются эти функции.

https://github.com/iron-meteor/iron-router/blob/devel/Guide.md

РЕДАКТИРОВАТЬ 18/3/2015: переработал ответ, потому что он содержал устаревший материал и, тем не менее, все еще получил upvotes.

Ответ 2

Это одна из тех проблем, которые я действительно желаю, чтобы основная документация метеоров обращалась непосредственно. Это запутывает, потому что:

  • Вы сделали правильную вещь в соответствии с API.
  • Вы получаете ошибки для Deps, которые не указывают на корневую проблему.

Итак, как вы уже выяснили, ваши данные не готовы при визуализации шаблона. Какое самое простое решение? Предположим, что данные могут быть не готовы. examples многое сделать. Из leaderboard.js:

Template.leaderboard.selected_name = function () {
    var player = Players.findOne(Session.get("selected_player"));
    return player && player.name;
};

Только если player действительно найден, будет доступ к player.name. В coffeescript вы можете использовать soaks для достижения того же самого.

предложение saimeunt iron-router waitOn полезно для данного конкретного случая использования, но имейте в виду, что вы, скорее всего, столкнетесь с ситуациями в вашем приложении, где данных просто нет в базе данных, или требуемое свойство не существует на выбранном объекте.

Несчастная реальность заключается в том, что во многих из этих случаев требуется немного защитного программирования.

Ответ 3

Использование железа-маршрутизатора для ожидания работы подписки, но мне нравится поддерживать централизованное управление подписками в виде файла collections.js. Вместо этого я использую Meteor порядок загрузки файлов, чтобы загружать подписки перед всем остальным.

Вот что может выглядеть файл моих коллекций .js:

// ****************************** Collections **********************************
Groups = new Mongo.Collection("groups");

// ****************************** Methods **************************************
myGroups = function (userId) {
  return Groups.find({"members":{$elemMatch:{"user_id":userId}}});
};

// ****************************** Subscriptions ********************************
if(Meteor.isClient){
  Meteor.subscribe("groups");
}

// ****************************** Publications *********************************
if(Meteor.isServer){
  Meteor.publish("groups", function () {
    return myGroups(this.userId);
  });
}

Затем я помещаю коллекцию .js в папку lib/, чтобы она загружалась до моего типичного клиентского кода. Таким образом, подписка централизована в один файл collections.js, а не как часть моих маршрутов. Этот пример также централизует мои запросы, поэтому клиентский код может использовать тот же метод для извлечения данных:

var groups = myGroups(Meteor.userId());