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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15312812/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 00:20:09  来源:igfitidea点击:

How to execute helper function after DOM is ready in meteor

javascriptmeteor

提问by George Katsanos

I have a list of <li>'s which gets populated with a find() using Meteor.startup as you see below. Then I'm getting all the data attributes of these <li>'s using data() and putting it in an object and trying to return/console.log it so I can see if it works. But I'm getting nullas a result.

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

I tried the same in my console directly and it works - I get an object back - so I'm guessing that the DOM is not ready/populated before this function is executed.

我直接在我的控制台中尝试了同样的方法并且它有效 - 我得到了一个对象 - 所以我猜测 DOM 在执行这个函数之前没有准备好/填充。

I am having a hard time understanding the difference between things like Meteor.startup and Template.mytemplate.rendered. In this case it seems that none of them works as I want?

我很难理解 Meteor.startup 和 Template.mytemplate.rendered 之类的东西之间的区别。在这种情况下,它们似乎都没有像我想要的那样工作?

What's the right way/place to do stuff with the DOM (traversing,getting attributes,manipulating)?

使用 DOM(遍历、获取属性、操作)做事的正确方法/地点是什么?

edit

编辑

as the code changed a lot in order to do what I wanted I post the whole thing.

为了做我想做的事情,代码发生了很大变化,所以我发布了整件事。

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

The above code creates a new google map (using the GMaps.js plugin) inside Meteor.Startup, and then in a nested Subscribe fetchs all documents from a collection, forEaches the results and gets the latitude and longitude values, then goes on to add markers in the google map...

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

edit 2

编辑 2

I made my 'map' variable a global one this way no need to nest .subscribe and .startup. :

我通过这种方式将我的“地图”变量设为全局变量,无需嵌套 .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;
}

回答by Akshat

Meteor.startup

流星启动

Meteor.startup()runs only once, its run on the client and server. So when the browser loads and the initial DOM is ready or the server starts. As Sohel Khalifa said you place initialization functions here. Don't define templates in here because the the templates need to be ready before this function can be fired.

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

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

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

This is run when meteor has finished and rendered the DOM. Additionally is run eachtime the HTML changes within the template. So for each item in your list in a subtemplate/a change in an item/update,etc as well as the list you will see the console.log return something if you use it to check. It will return null/undefinedwhen calling for data sometimes (which i'll explain):

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

Does this mean all the DOM is ready? NO!:

这是否意味着所有 DOM 都已准备就绪?不!

I think this is what might be causing you a bit of trouble. If you use external APIs such as Google maps, they might still render the map. the Template.myTemplate.rendered()means Meteorhas finished rendering the template with the reactive variables necessary. So to find out when your Google maps might be ready you need to hook into the Google maps API. Have a look at this question

我认为这可能会给你带来一些麻烦。如果您使用 Google 地图等外部 API,它们可能仍会呈现地图。这Template.myTemplate.rendered()意味着Meteor已经完成了具有必要反应变量的模板渲染。因此,要了解您的 Google 地图何时准备就绪,您需要连接到 Google 地图 API。有一个看看这个问题

Using Meteor.subscribe

使用 Meteor.subscribe

The reason you might get null/undefinedwhile using renderedis because this is the process meteor usually renders data into templates

您在使用时可能会得到null/的原因是因为流星通常将数据渲染到模板中undefinedrendered

You are basically calling console.log(getMarkers());before the subscription is complete, which is why you get null/undefined

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

Meteor uses this summarized process with templates & reactive data:

Meteor 将这个汇总过程与模板和反应数据一起使用:

  1. Build Templates with no data & render - NO data yet at this stage
  2. Ask server for data in collections
  3. Rebuild templates with new data & render
  1. 构建没有数据和渲染的模板 - 在这个阶段还没有数据
  2. 向服务器询问集合中的数据
  3. 使用新数据和渲染重建模板

So if at process 1) for a very short time you will have no data yet, which is why you might get null(such as in your code) & at the first render. To get past this you should use Meteor.subscribe's callback which is run when all the data is downloaded from the server: e.g

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

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

Note: Before you use this you should read the docs on using subscriptionsas you need to remove the autopublishpackage, as well as make a corresponding Meteor.publishfunction on the server. While this may seem tedious you may end up doing it anyway to give your users their own lists &/or implement some kind of security.

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

Suggested edits to your code:

对您的代码的建议编辑:

You are doing DOM traversing in the right place, Template.mytemplate.onRendered(function()..but you also need to hook into Google Maps' API to capture when their map is finished drawing. You should also use Meteor.subscribeto make sure you get the timing right and not get null/undefined.

您在正确的位置进行 DOM 遍历,Template.mytemplate.onRendered(function()..但您还需要挂钩 Google Maps 的 API 以在他们的地图绘制完成时进行捕获。您还应该使用Meteor.subscribe来确保时机正确,而不是获取null/ undefined

Make sure you put your Template helpers in a Meteor.isClientbut not in a Meteor.startupbecause Meteor.startup is fired after your initial DOM is ready (the intitial is the first but before its changed by reactive variables or a router) so your template definitions need to run beforethis stage.

确保你把你的模板助手放在 aMeteor.isClient而不是 aMeteor.startup因为 Meteor.startup 在你的初始 DOM 准备好后被触发(初始是第一个,但在它被反应性变量或路由器改变之前)所以你的模板定义需要在之前运行这个阶段。

回答by sohel khalifa

The reason why it returns nullas a result is, you have placed it in Meteor.startup(). It actually runs before the data is loaded from the server. So the lists.find()returns null.

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

The Meteor.startup()is a place for initializing your global variables, reative sessions and subscribing to the primary bunch of data from the server. Everything you write there will be executed once, right after the client starts up.

Meteor.startup()是一个用于初始化全局变量、响应会话和从服务器订阅主要数据的地方。您在那里编写的所有内容都将在客户端启动后立即执行。

The Template.myTemplate.rendered()is a special helper provided by meteor that runs everytime when the corresponding data has changed, it is mainly used for getting attributes or manipulating DOM elements contained within that template.

Template.myTemplate.rendered()是通过流星的是,当相应的数据已经改变的运行,每次提供一个特殊的辅助,它主要用于获取属性或操纵包含在该模板内的DOM元素。

So, place your helper code outside in common isClient()area. And use .rendered()helper to traverse the DOM, and getting or manipulating attributes of DOM elements.

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

回答by Gleb Dolzikov

Many thanks Akshat for detailed answer)

非常感谢 Akshat 的详细回答)

I have more complicated case of using Meteor.subscribe, i have template which includes images from DB. So i need to wait for data from two collection iamges and news(all other data here).

我有更复杂的使用 Meteor.subscribe 的案例,我有包含来自 DB 的图像的模板。所以我需要等待来自两个集合图像和新闻的数据(这里的所有其他数据)。

I get my DOM ready in this way:

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

模板结构:

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

回答by Swadq

The best way to do this is to put code into Template.x.rendered() and use a Session reactive variable to track if the code has been run or not. For example, you could go about this like so:

最好的方法是将代码放入 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;
}

If you ever want to rerun that part of the code, you only have to call:

如果您想重新运行该部分代码,只需调用:

Session.set('doneMarkers', null);