Подтвердить что ты не робот

Как выполнить вспомогательную функцию после того, как DOM готов в метеор

У меня есть список <li>, который заполняется с помощью find(), используя Meteor.startup, как вы видите ниже. Затем я получаю все атрибуты данных этих <li> с помощью data() и помещаю их в объект и пытаюсь вернуть /console.log, чтобы я мог видеть, работает ли он. Но я получаю null в результате.

    Meteor.startup(function () {
    Template.messages.lists = function () {
        var allitems = lists.find();
        return allitems;
    };
    var map;
    map = new GMaps({
        div: '#map_canvas',
        lat: -12.043333,
        lng: -77.028333
    });
    var lat = map.getCenter().lat();
    var lng = map.getCenter().lng();
    map.addMarker({
        lat: lat,
        lng: lng,
        draggable: true,
        title: 'Test',
        dragend: function (e) {
            $('#lat').val(this.getPosition().lat());
            $('#lng').val(this.getPosition().lng());
        }
    });
    console.log(getMarkers());
});


function getMarkers() {
    var coordinates = {};
    coordinates = $('li.message').data();
    return coordinates;
}

Я попробовал то же самое в своей консоли напрямую, и он работает - я возвращаю объект обратно, поэтому я предполагаю, что DOM не готов/не заполнен до того, как эта функция будет выполнена.

Мне трудно понять разницу между такими вещами, как Meteor.startup и Template.mytemplate.rendered. В этом случае кажется, что никто из них не работает так, как я хочу?

Каков правильный способ/место для работы с DOM (перемещение, получение атрибутов, манипулирование)?

изменить

так как код сильно изменился, чтобы сделать то, что я хотел, я опубликовал все это.

Meteor.startup(function () {
  var map;
  map = new GMaps({
    div: '#map_canvas',
    lat: 50.853642,
    lng: 4.357452
  });
  Meteor.subscribe('AllMessages', function() {
    var allitems = lists.find().fetch();
    console.log(allitems);
    allitems.forEach(function(item) { 
      var lat = item.location.lat; 
      var lng = item.location.lng;
      console.log('latitude is: ' + lat);
      console.log('longitude is: ' + lng);
      map.addMarker({ 
        lat: lat, 
        lng: lng, 
        draggable: true, 
        title: 'Test', 
        dragend: function(e) { 
          $('#lat').val(this.getPosition().lat()); 
          $('#lng').val(this.getPosition().lng()); 
        } 
      }); 
    });
  });
});

Вышеприведенный код создает новую карту google (с использованием плагина GMaps.js) внутри Meteor.Startup, а затем во вложенной подписке извлекает все документы из коллекции, для получения результатов и получения значений широты и долготы, затем идет для добавления маркеров в карту google...

изменить 2

Я сделал свою переменную "map" глобальной, поэтому не нужно встраивать .subscribe и .startup.

Meteor.subscribe('AllMessages', function() {
  var allitems = lists.find().fetch();
  console.log(allitems);
  allitems.forEach(function(item) { 
    var lat = item.location.lat; 
    var lng = item.location.lng;
    console.log('latitude is: ' + lat);
    console.log('longitude is: ' + lng);
    map.addMarker({ 
      lat: lat, 
      lng: lng, 
      draggable: true, 
      title: item.description, 
      dragend: function(e) { 
        $('#lat').val(this.getPosition().lat()); 
        $('#lng').val(this.getPosition().lng()); 
      } 
    }); 
  });
});

Meteor.startup(function () {
  map = new GMaps({
    div: '#map_canvas',
    lat: 50.853642,
    lng: 4.357452
  });
});

Template.messages.lists = function () {
  var allitems = lists.find().fetch();
  return allitems;
}
4b9b3361

Ответ 1

Meteor.startup

Meteor.startup() выполняется только один раз, его запуск выполняется на клиенте и сервере. Поэтому, когда браузер загружается и исходный DOM готов или сервер запускается. Как сказал Сохель Халифа, здесь вы выполняете функции инициализации. Не определяйте шаблоны здесь, потому что шаблоны должны быть готовы до того, как эта функция может быть запущена.

Template.myTemplate.onRendered(function() {...})

Это выполняется, когда метеор закончил и отобразил DOM. Кроме того, выполняется каждый время изменения HTML в шаблоне. Поэтому для каждого элемента в вашем списке в подтеме/изменении элемента/обновления и т.д., А также в списке вы увидите, что console.log возвращает что-то, если вы используете его для проверки. Он будет возвращать null/undefined при вызове данных иногда (что я объясню):

Означает ли это, что все DOM готовы? НЕТ!

Я думаю, что это то, что может вызвать у вас неприятности. Если вы используете внешние API, такие как карты Google, они могут отображать карту. Template.myTemplate.rendered() означает, что "Метеор" закончил визуализацию шаблона с необходимыми реактивными переменными. Поэтому, чтобы узнать, когда ваши карты Google будут готовы, вам нужно подключиться к API карт Google. Попросите рассмотреть этот вопрос

Использование Meteor.subscribe

Причина, по которой вы можете получить null/undefined при использовании rendered, состоит в том, что это процесс, который метеор обычно передает данные в шаблоны

В основном вы вызываете console.log(getMarkers()); до завершения подписки, поэтому вы получаете null/undefined

Meteor использует этот обобщенный процесс с шаблонами и реактивными данными:

  • Создание шаблонов без данных и рендеринга - пока нет данных на этом этапе
  • Запросить сервер для данных в коллекциях
  • Перестроить шаблоны с новыми данными и рендерить

Итак, если в процессе 1) в течение очень короткого времени у вас пока нет данных, поэтому вы можете получить null (например, в вашем коде) и при первом рендеринге. Чтобы пройти мимо этого, вы должны использовать обратный вызов Meteor.subscribe, который запускается, когда все данные загружаются с сервера: например,

Meteor.subscribe("lists", function() {
    //Callback fired when data received
});

Примечание. Прежде чем использовать это, вы должны прочитать документы об использовании subscriptions, поскольку вам нужно удалить пакет autopublish, а также как сделать соответствующую функцию Meteor.publish на сервере. Хотя это может показаться утомительным, вы можете в конечном итоге сделать это в любом случае, чтобы предоставить своим пользователям собственные списки и/или реализовать какую-то безопасность.

Предлагаемые изменения для вашего кода:

Вы делаете DOM в нужном месте, Template.mytemplate.onRendered(function().., но вам также нужно подключиться к API Google Maps, чтобы захватить, когда их карта завершена. Вы также должны использовать Meteor.subscribe, чтобы убедиться в правильности выбора времени и не получить null/undefined.

Удостоверьтесь, что вы помещаете свои помощники Template в Meteor.isClient, но не в Meteor.startup, потому что Meteor.startup запускается после того, как ваша исходная DOM готова (intitial является первой, но до ее изменения реактивными переменными или маршрутизатором), поэтому ваши шаблонные определения должны выполняться до этого этапа.

Ответ 2

Причина, по которой он возвращает null, в результате, вы поместили ее в Meteor.startup(). Он выполняется до загрузки данных с сервера. Таким образом, lists.find() возвращает null.

Meteor.startup() - это место для инициализации ваших глобальных переменных, повторных сеансов и подписки на основную группу данных с сервера. Все, что вы там пишете, будет выполняться один раз, сразу после запуска клиента.

Template.myTemplate.rendered() - специальный помощник, предоставляемый метеоритом, который запускается каждый раз, когда соответствующие данные изменяются, в основном используется для получения атрибутов или манипулирования элементами DOM, содержащимися в этом шаблоне.

Итак, разместите свой вспомогательный код снаружи в общей области isClient(). И используйте .rendered() помощник для перемещения DOM и получения или управления атрибутами элементов DOM.

Ответ 3

Большое спасибо Akshat за подробный ответ)

У меня более сложный случай использования Meteor.subscribe, у меня есть шаблон, который включает в себя изображения из БД. Поэтому мне нужно ждать данных из двух коллекций iamges и новостей (все остальные данные здесь).

Я получаю свою DOM в таком виде:

imageIsLoaded = new Promise(function(resolve){
    Meteor.subscribe('images',function(){
        resolve()
    });
});

newsIsLoaded = new Promise(function(resolve){
    Meteor.subscribe('news',function(){
        resolve()
    });
});


Template.newsList.onRendered(function(){
    Promise.all([imageIsLoaded, newsIsLoaded]).then(function() {
        // DOM IS READY!!!
        newsServices.masonryInit();
    })
});

Структура шаблона:

<template name="newsList">
         {{#each news}}
            {{> news_item}}
        {{/each}}
</template>

Ответ 4

Лучший способ сделать это - поместить код в Template.x.rendered() и использовать переменную для сеанса, чтобы отслеживать, был ли код запущен или нет. Например, вы можете сделать это так:

Template.x.rendered = function () {
  if (Session.get('doneMarkers') == null) {
    // Do your stuff
    if (getMarkers() != null) {
      Session.set('doneMarkers', 'yes');
    }
  }
});

function getMarkers() {
  var coordinates = {};
  coordinates = $('li.message').data();
  return coordinates;
}

Если вы хотите повторить эту часть кода, вам нужно только позвонить:

Session.set('doneMarkers', null);