Meteor.js:如何延迟 Meteor.call() 直到拥有所有数据?这是范围问题还是异步问题?
Meteor.js: how to delay Meteor.call() until have all data? Is this a scoping issue or an async issue?
我有一个正在构建的 Meteor 应用程序,它是一个 CMS。这个想法是一个网页可以有很多行,每一行都有很多文章。文章有图像、header 和段落。
为了保存图像,我使用了 edgee:slingshot 包。由于 sendFile($img, articlesArray);
是异步的,因此 sendFile($img, articlesArray);
之前 Meteor.call()
运行 的代码完成。这会导致网页保存在 mongodb 中,而没有保存任何行数组。
我能够在数据库中创建没有行(及其文章)的网页,即使 console.log 显示网页设置正确(包含行和文章 - 请参阅底部屏幕截图)。就好像 webPageAttributes
没有传递给 Meteor.call()
。如果这是范围界定问题或异步问题,我会不知所措。
这里是生成的HTML:
这是 运行 表单提交中的 client-side 代码:
'submit form': function(e, template) {
e.preventDefault();
var articlesArray = [];
var rowDivsArray = template.findAll('.row');
_.map(rowDivsArray, function(div) {
if (div.getAttribute("id") != null) {
articlesArray.push(div);
}
});
var articles = _.map(articlesArray, function(rowDiv) {
var articlesArray = [];
_.each(rowDiv, function() {
var $rowDiv = $(rowDiv);
var $articles = $rowDiv.find('div.article-container');
$.each($articles, function(index, value) {
var $img = $(value).find('input:file')[0].files[0];
// console.log("$img: ", $img);
var $input = $(value).find('input.form-control');
var $text = $(value).find('textarea');
var $style = $(value).find('input:radio:checked');
var createArticle = function(downloadUrl){
console.log("createArticle() init");
var $article = {
img: downloadUrl,
header: $input.val(),
paragraph: $text.val(),
style: $style.val()
};
return $article;
};
var sendFile = function(img, articlesArray){
var articlesArray = articlesArray;
var upload = new Slingshot.Upload("articleImgUpload");
upload.send(img, function (error, downloadUrl) {
if (error) {
console.log("ERROR in upload: ", error);
} else {
createTheArticleAndAddToArray(downloadUrl);
return;
}
});
return upload;
};
// this is async, I need this to execute,
sendFile($img, articlesArray);
var createTheArticleAndAddToArray = function(downloadUrl) {
console.log("createTheArticleAndAddToArray() articlesArray before push: ", articlesArray);
var $article = createArticle(downloadUrl);
articlesArray.push($article);
console.log("createTheArticleAndAddToArray() articlesArray after push: ", articlesArray);
return articlesArray;
};
});
});
return articlesArray;
});
var makeWebPage = function(articles) {
var webPage = {
title: $(e.target).find('[name=title]').val(),
rows: articles
};
return webPage;
};
var webPage = makeWebPage(articles);
Meteor.call('newWebPageInsert', webPage, function(error, result) {
if (error) {
return alert(error.reason);
} else {
Router.go('/new-web-page');
console.log("result from Meteor.call: ", result);
}
});
}
这里是 server-side 将网页插入数据库的代码:
// this will create the webPage with all its attributes, but no rows array and no articles
Meteor.methods({
newWebPageInsert: function(webPageAttributes) {
console.log("webPageAttributes argument in newWebPageInsert: ", webPageAttributes);
check(Meteor.userId(), String);
check(webPageAttributes, {
title: String,
rows: Array
});
var user = Meteor.user();
var webPage = _.extend(webPageAttributes, {
userId: user._id,
submitted: new Date()
});
var webPageId = PublicWebPages.insert(webPage);
return {
_id: webPageId
};
}
目标是一个网页,有一个行数组,行数组有 sub-arrays 表示页面的行。 sub-array 包含文章 objects。
webPage: {
title: "Page Title",
rows: [
[{img: "path to AWS", header: "article header", paragraph: "article paragraphs"}, {img: "path to AWS", header: "article header", paragraph: "article paragraphs"}], // 1st row
[{img: "path to AWS", header: "article header", paragraph: "article paragraphs"}, {...}] // 2nd row
]
}
如果我将 Meteor.call()
移动到 upload.send()
的 else 子句,那么将为每篇文章保存一个具有所有文章属性的网页。如果我保留如图所示的代码,则会创建网页,但没有任何行或文章。如此混乱,因为 console.log 显示网页已正确设置文章和表单。
我是否暂停 Meteor.call()
等待?同样,webPage.rows 似乎没有正确传递给 newWebPageInsert
。如何将 webPage.rows 传递给 Meteor.methods()
中的 newWebPageInsert()
?这是应该遵循的正确逻辑还是应该在其他地方暂停一下?
更新 1:
看起来 Meteor.call()
在创建行和文章之前 运行,这就是它们没有被保存的原因。
证明console.log:
所以这似乎是一个异步问题....如何暂停 Meteor.call()
直到文章创建?
这是 objects 的 console.log 及其时间:
更新 2:
时间和范围随着代码的变化而保留:
'submit form': function(e, template) {
e.preventDefault();
var articlesArray = [];
var rowDivsArray = template.findAll('.row');
// console.log("rowDivsArray: ", rowDivsArray);
_.map(rowDivsArray, function(div) {
if (div.getAttribute("id") != null) {
articlesArray.push(div);
}
});
// console.log("articlesArray: ", articlesArray);
var articles = _.map(articlesArray, function(rowDiv) {
// console.log("rowDiv: ", rowDiv); // get row's articles
var articlesArray = []; // {img: "https://childrens-center.s3.amazonaws.com/undefined/full-style.png", header: "hell yeah", paragraph:"test 1"}
_.each(rowDiv, function() {
var $rowDiv = $(rowDiv);
var $articles = $rowDiv.find('div.article-container');
$.each($articles, function(index, value) {
var $img = $(value).find('input:file')[0].files[0];
// console.log("$img: ", $img);
var $input = $(value).find('input.form-control');
var $text = $(value).find('textarea');
var $style = $(value).find('input:radio:checked');
var createArticle = function(downloadUrl){
console.log("createArticle() init");
var $article = {
img: downloadUrl,
header: $input.val(),
paragraph: $text.val(),
style: $style.val()
};
return $article;
};
var sendFile = function(img, articlesArray){
console.log("articlesArray passed to sendFile line 250: ", articlesArray);
var articlesArray = articlesArray;
var upload = new Slingshot.Upload("articleImgUpload");
upload.send(img, function (error, downloadUrl) {
if (error) {
console.log("ERROR in upload: ", error);
} else {
// console.log("the articles array in sendfile before push line 257: ", articlesArray);
return createTheArticleAndAddToArray(downloadUrl);
}
});
return upload;
};
// this is async, I need this to execute,
sendFile($img, articlesArray);
// will need to create the $article and push into db
var createTheArticleAndAddToArray = function(downloadUrl) {
console.log("createTheArticleAndAddToArray() articlesArray before push: ", articlesArray);
var $article = createArticle(downloadUrl);
articlesArray.push($article);
console.log("createTheArticleAndAddToArray() articlesArray after push: ", articlesArray);
var articles = articlesArray;
var makeWebPage = function(articles) {
// console.log("articles within makeWebPage(): ", articles);
var webPage = {
title: $(e.target).find('[name=title]').val(),
rows: articles
};
Meteor.call('newWebPageInsert', webPage, function(error, result) {
console.log("webPage in the Meteor.call line 300: ", webPage);
console.log("webPage.rows in the Meteor.call line 300: ", webPage.rows);
if (error) {
return alert(error.reason);
} else {
Router.go('/new-web-page');
console.log("result from Meteor.call: ", result);
}
});
return webPage;
};
console.log("articles to insert line 294: ", articles);
var webPage = makeWebPage(articles);
return articlesArray;
};
});
});
return articlesArray;
});
}
现在只需要在收集完所有文章后控制循环停止,然后调用一次makeNewWebPage()
。
试试这个。
Meteor.subscribe('whateverDataYouAreWaiting', Session.get('whateverDataYouAreWaiting'), {
onError: function(){
console.log('Error');
},
onReady: function() {
//Here trigger a Session to flag the documents are ready, or call Meteor.call
}
});
我最终进行了重构,这样文章就不会嵌入到网页中,因此每篇文章都会对 Amazon Storage 进行单独的 post。它仍然需要更多的工作,例如错误处理,但核心功能在那里。
Template.addWebPageArticles.events({
'submit form': function(e, template) {
e.preventDefault();
var rowsArray = template.findAll('.row');
var allRowsWithNumberAssignedArray = [];
_.each(rowsArray, function(row) {
Session.set('rowNumber', (Session.get('rowNumber') + 1));
$(row).attr( "id", "row" + Session.get('rowNumber') );
allRowsWithNumberAssignedArray.push(row);
});
var rowsWithNumberAssignedThatContainArticlesArray = _.rest(allRowsWithNumberAssignedArray, 1);
_.each(rowsWithNumberAssignedThatContainArticlesArray, function(row) {
var articlesInRowInstanceArray = $(row).find('.article-container');
_.each(articlesInRowInstanceArray, function(articleContainerDiv) {
var article = {
page_id: template.data._id,
row_number: $(articleContainerDiv).parent().attr('id'),
place_in_row: String( _.indexOf(articlesInRowInstanceArray, articleContainerDiv) ),
image_path: $(articleContainerDiv).find('input:file')[0].files[0],
header: $(articleContainerDiv).find('input.form-control').val(),
paragraph: $(articleContainerDiv).find('textarea').val(),
style: $(articleContainerDiv).find('input:radio:checked').val()
}
var imgToUpload = function(article) {
var image = article.image_path;
return image;
}
var image = imgToUpload(article);
var sendFile = function(image){
var upload = new Slingshot.Upload("articleImgUpload");
upload.send(image, function (error, downloadUrl) {
if (error) {
console.log("ERROR in upload: ", error);
} else {
var updateArticle = function(downloadUrl) {
article.image_path = downloadUrl;
return article;
}
var articleReturned = updateArticle(downloadUrl);
Meteor.call('newWebPageArticleInsert', articleReturned, function(error, result) {
if (error) {
return alert(error.reason);
} else {
Router.go('/new-web-page');
}
});
}
});
return upload;
};
sendFile(image);
});
});
}
});
我有一个正在构建的 Meteor 应用程序,它是一个 CMS。这个想法是一个网页可以有很多行,每一行都有很多文章。文章有图像、header 和段落。
为了保存图像,我使用了 edgee:slingshot 包。由于 sendFile($img, articlesArray);
是异步的,因此 sendFile($img, articlesArray);
之前 Meteor.call()
运行 的代码完成。这会导致网页保存在 mongodb 中,而没有保存任何行数组。
我能够在数据库中创建没有行(及其文章)的网页,即使 console.log 显示网页设置正确(包含行和文章 - 请参阅底部屏幕截图)。就好像 webPageAttributes
没有传递给 Meteor.call()
。如果这是范围界定问题或异步问题,我会不知所措。
这里是生成的HTML:
这是 运行 表单提交中的 client-side 代码:
'submit form': function(e, template) {
e.preventDefault();
var articlesArray = [];
var rowDivsArray = template.findAll('.row');
_.map(rowDivsArray, function(div) {
if (div.getAttribute("id") != null) {
articlesArray.push(div);
}
});
var articles = _.map(articlesArray, function(rowDiv) {
var articlesArray = [];
_.each(rowDiv, function() {
var $rowDiv = $(rowDiv);
var $articles = $rowDiv.find('div.article-container');
$.each($articles, function(index, value) {
var $img = $(value).find('input:file')[0].files[0];
// console.log("$img: ", $img);
var $input = $(value).find('input.form-control');
var $text = $(value).find('textarea');
var $style = $(value).find('input:radio:checked');
var createArticle = function(downloadUrl){
console.log("createArticle() init");
var $article = {
img: downloadUrl,
header: $input.val(),
paragraph: $text.val(),
style: $style.val()
};
return $article;
};
var sendFile = function(img, articlesArray){
var articlesArray = articlesArray;
var upload = new Slingshot.Upload("articleImgUpload");
upload.send(img, function (error, downloadUrl) {
if (error) {
console.log("ERROR in upload: ", error);
} else {
createTheArticleAndAddToArray(downloadUrl);
return;
}
});
return upload;
};
// this is async, I need this to execute,
sendFile($img, articlesArray);
var createTheArticleAndAddToArray = function(downloadUrl) {
console.log("createTheArticleAndAddToArray() articlesArray before push: ", articlesArray);
var $article = createArticle(downloadUrl);
articlesArray.push($article);
console.log("createTheArticleAndAddToArray() articlesArray after push: ", articlesArray);
return articlesArray;
};
});
});
return articlesArray;
});
var makeWebPage = function(articles) {
var webPage = {
title: $(e.target).find('[name=title]').val(),
rows: articles
};
return webPage;
};
var webPage = makeWebPage(articles);
Meteor.call('newWebPageInsert', webPage, function(error, result) {
if (error) {
return alert(error.reason);
} else {
Router.go('/new-web-page');
console.log("result from Meteor.call: ", result);
}
});
}
这里是 server-side 将网页插入数据库的代码:
// this will create the webPage with all its attributes, but no rows array and no articles
Meteor.methods({
newWebPageInsert: function(webPageAttributes) {
console.log("webPageAttributes argument in newWebPageInsert: ", webPageAttributes);
check(Meteor.userId(), String);
check(webPageAttributes, {
title: String,
rows: Array
});
var user = Meteor.user();
var webPage = _.extend(webPageAttributes, {
userId: user._id,
submitted: new Date()
});
var webPageId = PublicWebPages.insert(webPage);
return {
_id: webPageId
};
}
目标是一个网页,有一个行数组,行数组有 sub-arrays 表示页面的行。 sub-array 包含文章 objects。
webPage: {
title: "Page Title",
rows: [
[{img: "path to AWS", header: "article header", paragraph: "article paragraphs"}, {img: "path to AWS", header: "article header", paragraph: "article paragraphs"}], // 1st row
[{img: "path to AWS", header: "article header", paragraph: "article paragraphs"}, {...}] // 2nd row
]
}
如果我将 Meteor.call()
移动到 upload.send()
的 else 子句,那么将为每篇文章保存一个具有所有文章属性的网页。如果我保留如图所示的代码,则会创建网页,但没有任何行或文章。如此混乱,因为 console.log 显示网页已正确设置文章和表单。
我是否暂停 Meteor.call()
等待?同样,webPage.rows 似乎没有正确传递给 newWebPageInsert
。如何将 webPage.rows 传递给 Meteor.methods()
中的 newWebPageInsert()
?这是应该遵循的正确逻辑还是应该在其他地方暂停一下?
更新 1:
看起来 Meteor.call()
在创建行和文章之前 运行,这就是它们没有被保存的原因。
证明console.log:
所以这似乎是一个异步问题....如何暂停 Meteor.call()
直到文章创建?
这是 objects 的 console.log 及其时间:
更新 2: 时间和范围随着代码的变化而保留:
'submit form': function(e, template) {
e.preventDefault();
var articlesArray = [];
var rowDivsArray = template.findAll('.row');
// console.log("rowDivsArray: ", rowDivsArray);
_.map(rowDivsArray, function(div) {
if (div.getAttribute("id") != null) {
articlesArray.push(div);
}
});
// console.log("articlesArray: ", articlesArray);
var articles = _.map(articlesArray, function(rowDiv) {
// console.log("rowDiv: ", rowDiv); // get row's articles
var articlesArray = []; // {img: "https://childrens-center.s3.amazonaws.com/undefined/full-style.png", header: "hell yeah", paragraph:"test 1"}
_.each(rowDiv, function() {
var $rowDiv = $(rowDiv);
var $articles = $rowDiv.find('div.article-container');
$.each($articles, function(index, value) {
var $img = $(value).find('input:file')[0].files[0];
// console.log("$img: ", $img);
var $input = $(value).find('input.form-control');
var $text = $(value).find('textarea');
var $style = $(value).find('input:radio:checked');
var createArticle = function(downloadUrl){
console.log("createArticle() init");
var $article = {
img: downloadUrl,
header: $input.val(),
paragraph: $text.val(),
style: $style.val()
};
return $article;
};
var sendFile = function(img, articlesArray){
console.log("articlesArray passed to sendFile line 250: ", articlesArray);
var articlesArray = articlesArray;
var upload = new Slingshot.Upload("articleImgUpload");
upload.send(img, function (error, downloadUrl) {
if (error) {
console.log("ERROR in upload: ", error);
} else {
// console.log("the articles array in sendfile before push line 257: ", articlesArray);
return createTheArticleAndAddToArray(downloadUrl);
}
});
return upload;
};
// this is async, I need this to execute,
sendFile($img, articlesArray);
// will need to create the $article and push into db
var createTheArticleAndAddToArray = function(downloadUrl) {
console.log("createTheArticleAndAddToArray() articlesArray before push: ", articlesArray);
var $article = createArticle(downloadUrl);
articlesArray.push($article);
console.log("createTheArticleAndAddToArray() articlesArray after push: ", articlesArray);
var articles = articlesArray;
var makeWebPage = function(articles) {
// console.log("articles within makeWebPage(): ", articles);
var webPage = {
title: $(e.target).find('[name=title]').val(),
rows: articles
};
Meteor.call('newWebPageInsert', webPage, function(error, result) {
console.log("webPage in the Meteor.call line 300: ", webPage);
console.log("webPage.rows in the Meteor.call line 300: ", webPage.rows);
if (error) {
return alert(error.reason);
} else {
Router.go('/new-web-page');
console.log("result from Meteor.call: ", result);
}
});
return webPage;
};
console.log("articles to insert line 294: ", articles);
var webPage = makeWebPage(articles);
return articlesArray;
};
});
});
return articlesArray;
});
}
现在只需要在收集完所有文章后控制循环停止,然后调用一次makeNewWebPage()
。
试试这个。
Meteor.subscribe('whateverDataYouAreWaiting', Session.get('whateverDataYouAreWaiting'), {
onError: function(){
console.log('Error');
},
onReady: function() {
//Here trigger a Session to flag the documents are ready, or call Meteor.call
}
});
我最终进行了重构,这样文章就不会嵌入到网页中,因此每篇文章都会对 Amazon Storage 进行单独的 post。它仍然需要更多的工作,例如错误处理,但核心功能在那里。
Template.addWebPageArticles.events({
'submit form': function(e, template) {
e.preventDefault();
var rowsArray = template.findAll('.row');
var allRowsWithNumberAssignedArray = [];
_.each(rowsArray, function(row) {
Session.set('rowNumber', (Session.get('rowNumber') + 1));
$(row).attr( "id", "row" + Session.get('rowNumber') );
allRowsWithNumberAssignedArray.push(row);
});
var rowsWithNumberAssignedThatContainArticlesArray = _.rest(allRowsWithNumberAssignedArray, 1);
_.each(rowsWithNumberAssignedThatContainArticlesArray, function(row) {
var articlesInRowInstanceArray = $(row).find('.article-container');
_.each(articlesInRowInstanceArray, function(articleContainerDiv) {
var article = {
page_id: template.data._id,
row_number: $(articleContainerDiv).parent().attr('id'),
place_in_row: String( _.indexOf(articlesInRowInstanceArray, articleContainerDiv) ),
image_path: $(articleContainerDiv).find('input:file')[0].files[0],
header: $(articleContainerDiv).find('input.form-control').val(),
paragraph: $(articleContainerDiv).find('textarea').val(),
style: $(articleContainerDiv).find('input:radio:checked').val()
}
var imgToUpload = function(article) {
var image = article.image_path;
return image;
}
var image = imgToUpload(article);
var sendFile = function(image){
var upload = new Slingshot.Upload("articleImgUpload");
upload.send(image, function (error, downloadUrl) {
if (error) {
console.log("ERROR in upload: ", error);
} else {
var updateArticle = function(downloadUrl) {
article.image_path = downloadUrl;
return article;
}
var articleReturned = updateArticle(downloadUrl);
Meteor.call('newWebPageArticleInsert', articleReturned, function(error, result) {
if (error) {
return alert(error.reason);
} else {
Router.go('/new-web-page');
}
});
}
});
return upload;
};
sendFile(image);
});
});
}
});