我有一个<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中创建了一个新的Google地图(使用GMaps.js插件),然后在嵌套的Subscribe中获取集合中的所有文档,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;
}
答案 0 :(得分:29)
Meteor.startup()
只运行一次,它在客户端和服务器上运行。因此,当浏览器加载并且初始DOM准备就绪或服务器启动时。正如Sohel Khalifa所说,你在这里放置初始化函数。不要在这里定义模板,因为在激活此函数之前,模板需要准备就绪。
Template.myTemplate.onRendered(function() {... })
这是在meteor完成并渲染DOM时运行的。此外,HTML会在模板中更改每次时间。因此,对于列表中的每个项目,如子项目/更改项目/更新等,以及列表,您将看到console.log返回一些内容,如果您使用它来检查。有时会调用数据时会返回null
/ undefined
(我会解释):
这是否意味着所有DOM都准备好了? !NO 强>:
我认为这可能会给你带来一些麻烦。如果您使用外部API(例如Google地图),他们仍可能会渲染地图。 Template.myTemplate.rendered()
表示 Meteor 已完成使用必要的反应变量渲染模板。因此,要了解您的Google地图何时准备就绪,您需要加入Google地图API。有一个look at this question
使用null
时获得undefined
/ rendered
的原因是因为流星通常会将数据转换为模板
在订阅完成之前,您基本上是在调用console.log(getMarkers());
,这就是您获得null
/ undefined
Meteor使用这个汇总过程和模板&amp;反应数据:
因此,如果在过程1)很短的时间内你还没有数据,这就是为什么你可能得到null
(例如在你的代码中)&amp;在第一次渲染。为了解决这个问题,您应该使用Meteor.subscribe
的回调函数,该函数在从服务器下载所有数据时运行:例如
Meteor.subscribe("lists", function() {
//Callback fired when data received
});
注意:在使用之前,您应该阅读有关使用subscriptions的文档,因为您需要删除autopublish
包,并在服务器上创建相应的Meteor.publish
函数。虽然这可能看起来很乏味,但最终还是可以为用户提供他们自己的列表和/或实现某种安全性。
建议修改代码:
您正在正确的位置Template.mytemplate.onRendered(function()..
进行DOM遍历,但您还需要连接到Google Maps的API,以便在地图完成绘制时捕获。您还应该使用Meteor.subscribe
确保时机正确,而不是null
/ undefined
。
确保将模板帮助器放在Meteor.isClient
但不放在Meteor.startup
中,因为在初始DOM准备就绪后触发了Meteor.startup(初始化是第一次,但在被反应改变之前)变量或路由器)所以你的模板定义需要在这个阶段之前运行。
答案 1 :(得分:2)
结果返回null
的原因是,您已将其放在Meteor.startup()
中。它实际上是在从服务器加载数据之前运行的。所以lists.find()
返回null。
Meteor.startup()
是一个初始化全局变量,reative会话和从服务器订阅主数据集的地方。你在那里写的所有东西都会在客户启动后立即执行一次。
Template.myTemplate.rendered()
是meteor提供的一个特殊助手,每当相应的数据发生变化时它就会运行,它主要用于获取属性或操纵该模板中包含的DOM元素。
因此,请将您的帮助程序代码放在公共isClient()
区域之外。并使用.rendered()
帮助程序遍历DOM,获取或操作DOM元素的属性。
答案 2 :(得分:2)
非常感谢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>
答案 3 :(得分:1)
执行此操作的最佳方法是将代码放入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);