[英]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()
只运行一次,它在客户端和服务器上运行。 因此,当浏览器加载并且初始DOM准备就绪或服务器启动时。 正如Sohel Khalifa所说,你在这里放置初始化函数。 不要在此处定义模板,因为在激活此函数之前,模板需要准备就绪。
Template.myTemplate.onRendered(function(){...})
这在meteor完成并渲染DOM时运行。 另外, 每次 HTML在模板中更改时都会运行。 因此,对于列表中的每个项目,如子项目/更改项目/更新等,以及列表,您将看到console.log返回一些内容,如果您使用它来检查。 它有时会在调用数据时返回null
/ undefined
(我将解释):
这是否意味着所有DOM都准备好了? 没有! :
我认为这可能会给你带来一些麻烦。 如果您使用外部API(例如Google地图),他们仍可能会渲染地图。 Template.myTemplate.rendered()
表示Meteor已经完成了使用必要的反应变量渲染模板。 因此,要了解您的Google地图何时准备就绪,您需要加入Google地图API。 看看这个问题
使用rendered
可能会出现null
/ undefined
的原因是因为这是流程通常将数据渲染到模板中的过程
你基本上是在调用console.log(getMarkers());
在订阅完成之前,这就是为什么你得到null
/ undefined
Meteor将此汇总过程与模板和反应数据结合使用:
因此,如果在进程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.