我正在尝试使用Sails.js和MongoDB构建一个支持多级别类别的简单应用程序。类别可以有许多子类别。所以在我的Category
模型中,我在其内部设置了一对多关系类别:
module.exports = {
adapter: 'mongo',
attributes: {
categoryTitle: {
type: 'string',
required: true
},
//owner or parent of the one-to-many relationship
parentCat: {
model: 'category'
},
//sub categories
subCategories: {
collection: 'category',
via: 'parentCat'
},
}};
使用AngularJS中的ng-repeat指令在选择下拉列表中显示类别,例如
<select class="selector3" id="categoryParent" ng-model="category.parent">
<option>Choose a category or none</option>
<option ng-repeat="cat in categories" value="{{cat.categoryTitle}}">{{cat.categoryTitle}}</option>
</select>
在 AngularJS CategoryController 中,我使用$scope.category.parent
来捕获所选类别的值(作为父类别)。这一步有效,因为我能够通过console.log($scope.category.parent);
看到所选的值。
但是,当我将新类别及其父类别一起保存到MongoDB时,即使正确保存其他字段,父类别也为空,如Mongo终端所示。 我想问题出在&#39;创建&#39;我写的用于在Sails中保存新类别的API 。您能否在以下代码中找出可能出错的地方?
create: function(req, res, next) {
var params = req.allParams();
// set parent category if exists
if (params.parentCat) {
var parentCategory = Category.findOne({categoryTitle : params.parentCat})
.exec(function(err, category) {
if (err) {
return null; //not found
}
return category; //found, return the category
});
params.parentCat = parentCategory; //set the parent category in params
}
//insert the new category to MongoDB
Category.create(params, function(err, category) {
if (err) {
console.log('category addition failed');
return next(err);
}
console.log('successfully added the category: ' + category.categoryTitle);
res.redirect('/category');
});
} //create
答案 0 :(得分:3)
你在这里混淆了一些概念。
首先
var parentCategory = Category.findOne({categoryTitle : params.parentCat})
请注意,水线方法findOne不会从数据库返回文档。因此,上述声明不会为您实现您的目的。重要的是你在exec函数中提供的回调函数。可以这样想:
Node.js事件循环将只执行Category.findOne并继续前进到下一个语句,而无需等待findOne的完成。这里的下一个陈述是:
params.parentCat = parentCategory
请注意,从MongoDB获取尚未完成。即使它已经完成,findOne也不会像我上面所说的那样返回对数据库中文档的引用。所以,这不是在Node.js中完成任务的方式。相反,如果要访问数据库对象,它将出现在exec中回调函数的category变量中。
现在第二个问题是下面的陈述
return category;
如你所知,返回将完全让你退出创建行动。此外,这里需要注意的一点是,此语句与底部的重定向语句之间存在竞争。这就是为什么
Node.js事件循环执行它遇到的每个语句。如果该语句是阻塞语句(如var x = <some computation without callback function>
),那么它将保留在该语句中并等待其完成。否则,如果语句是异步的,具有回调函数(如Category.findOne({}).exec(function (err, category){})
),则事件循环将移至下一个语句,并且当findOne完成时将执行回调。
现在,在这种情况下,将执行findOne并将事件循环移至
params.parentCat = parentCategory;
这不是异步语句,但它立即完成,事件循环转移到
Category.create()
现在,您可以将其视为findOne和create将在mongoDB中并行执行。完成数据库操作后,将执行回调函数。无论哪个回调首先执行,它的return语句都将被执行。所以,这种行为是不可预测的。 return category
可以执行,也可以res.redirect('/category');
执行。
我在下面重写了创建函数,保留了上面提到的所有要点
create: function(req, res, next) {
var params = req.params.all();
// set parent category if exists
if (params.parentCat) {
Category.findOne({categoryTitle : params.parentCat}).exec(function (err, parentCategory) {
if (err) {
return res.json(500, 'Sever error'); //not found
}
else{
params.parentCat = parentCategory.id; //set the parent category in params
//insert the new category to MongoDB
Category.create(params, function (err, category) {
if (err) {
console.log('category addition failed');
return next(err);
}
console.log('successfully added the category: ' + category.categoryTitle);
res.redirect('/category');
});
}
});
}
else{
return res.json(500,'Server error. Parent category required');
}
}