繁体   English   中英

如何在流星期间准备好DOM后执行辅助函数

[英]How to execute helper function after DOM is ready in meteor

我有一个<li>的列表,使用Meteor.startup填充find(),如下所示。 然后我使用data()获取这些<li>的所有数据属性并将其放入一个对象并尝试返回/ 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()); 
        } 
      }); 
    });
  });
});

上面的代码在Meteor.Startup中创建了一个新的谷歌地图(使用GMaps.js插件),然后在嵌套的订阅中获取集合中的所有文档,forEaches结果并获取纬度和经度值,然后继续添加谷歌地图中的标记......

编辑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;
}

Meteor.startup

Meteor.startup()只运行一次,它在客户端和服务器上运行。 因此,当浏览器加载并且初始DOM准备就绪或服务器启动时。 正如Sohel Khalifa所说,你在这里放置初始化函数。 不要在此处定义模板,因为在激活此函数之前,模板需要准备就绪。

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

这在meteor完成并渲染DOM时运行。 另外, 每次 HTML在模板中更改时都会运行。 因此,对于列表中的每个项目,如子项目/更改项目/更新等,以及列表,您将看到console.log返回一些内容,如果您使用它来检查。 它有时会在调用数据时返回null / undefined (我将解释):

这是否意味着所有DOM都准备好了? 没有!

我认为这可能会给你带来一些麻烦。 如果您使用外部API(例如Google地图),他们仍可能会渲染地图。 Template.myTemplate.rendered()表示Meteor已经完成了使用必要的反应变量渲染模板。 因此,要了解您的Google地图何时准备就绪,您需要加入Google地图API。 看看这个问题

使用Meteor.subscribe

使用rendered可能会出现null / undefined的原因是因为这是流程通常将数据渲染到模板中的过程

你基本上是在调用console.log(getMarkers()); 在订阅完成之前,这就是为什么你得到null / undefined

Meteor将此汇总过程与模板和反应数据结合使用:

  1. 构建没有数据和渲染的模板 - 此阶段尚无数据
  2. 向服务器询问集合中的数据
  3. 使用新数据和渲染重建模板

因此,如果在进程1)很短的时间内你将没有数据,这就是为什么你可能会得到null (例如在你的代码中)和第一次渲染。 为了解决这个问题,您应该使用Meteor.subscribe的回调Meteor.subscribe ,该函数在从服务器下载所有数据时运行:例如

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

注意:在使用此之前,您应该阅读有关使用订阅的文档,因为您需要删除autopublish包,以及在服务器上创建相应的Meteor.publish功能。 虽然这可能看起来很乏味,但最终可能会为您的用户提供他们自己的列表和/或实现某种安全性。

建议编辑代码:

你正在正确的地方进行DOM遍历, Template.mytemplate.onRendered(function()..但你也需要挂钩到谷歌地图的API来捕捉他们的地图完成绘图。你还应该使用Meteor.subscribe来制作确保你得到正确的时间,而不是null / undefined

确保将模板助手放在Meteor.isClient但不放在Meteor.startup因为在初始DOM准备就绪后触发了Meteor.startup(初始化是第一次,但在被反应变量或路由器更改之前),所以你的模板定义需要此阶段之前运行。

结果返回null的原因是,您将它放在Meteor.startup() 它实际上是在从服务器加载数据之前运行的。 所以lists.find()返回null。

Meteor.startup()用于初始化全局变量,reative会话以及从服务器订阅主数据集。 在客户启动后,您在那里写的所有内容都将被执行一次。

Template.myTemplate.rendered()是meteor提供的一个特殊助手,每当相应的数据发生变化时它就会运行,它主要用于获取属性或操纵该模板中包含的DOM元素。

因此,将您的帮助程序代码放在公共的isClient()区域之外。 并使用.rendered()帮助程序遍历DOM,获取或操作DOM元素的属性。

非常感谢Akshat的详细解答)

我有更复杂的使用Meteor.subscribe的情况,我有模板,其中包括来自DB的图像。 所以我需要等待来自两个集合iamges和news(这里的所有其他数据)的数据。

我以这种方式准备好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>

执行此操作的最佳方法是将代码放入Template.x.rendered()并使用Session反向变量来跟踪代码是否已运行。 例如,你可以像这样:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM