MEAN堆栈新手。可能是在问一个愚蠢的问题。
作为练习,我一直在尝试实现一个原型SPA,它在屏幕上显示了一系列任务卡(有点像Trello)。
目前,每张卡有4个字段:
- _id:ObjectId
- 内容:字符串
- 工作流:字符串
- state:字符串
我正在使用MongoDB作为数据库(使用Robomongo输入一些测试数据),我的机器上安装了node.js,以及Express.js.
我的server.js文件如下所示:
var express = require('express'),
cards = require('./routes/cards');
var app = express();
app.configure(function() {
app.use(express.logger('dev'));
app.use(express.bodyParser());
});
app.get('/cards', cards.findAll);
app.get('/cards/:id', cards.findById);
app.post('/cards', cards.addCard);
app.put('/cards/:id', cards.updateCard);
app.listen(3000);
console.log('Listening on port 3000...');
服务器端的路由/cads.js如下所示:
var mongo = require('mongodb');
var Server = mongo.Server,
Db = mongo.Db,
BSON = mongo.BSONPure;
var server = new Server('localhost', 27017, {auto_reconnect: true});
var db = new Db('mindr', server);
db.open(function(err, db) {
if(!err) {
console.log("Connected to 'mindr' database");
db.collection('cards', {strict:true}, function(err, collection) {
if (err) {
console.log("The 'cards' collection doesn't exist.");
}
});
}
});
exports.findById = function(req, res) {
var id = req.params.id;
console.log('Retrieving card: ' + id);
db.collection('cards', function(err, collection) {
collection.findOne({'_id':new BSON.ObjectID(id)}, function(err, item) {
res.send(item);
});
});
};
exports.findAll = function(req, res) {
db.collection('cards', function(err, collection) {
collection.find().toArray(function(err, items) {
res.send(items);
});
});
};
exports.addCard = function(req, res) {
var newCard = req.body;
console.log('Adding card: ' + JSON.stringify(newCard));
db.collection('cards', function(err, collection) {
collection.insert(newCard, {safe:true}, function(err, result) {
if (err) {
res.send({'error':'An error has occurred'});
} else {
console.log('Success: ' + JSON.stringify(result[0]));
res.send(result[0]);
}
});
});
}
exports.updateCard = function(req, res) {
var id = req.params.id;
var card = req.body;
console.log('Updating card: ' + id);
console.log(JSON.stringify(card));
db.collection('cards', function(err, collection) {
collection.update({'_id':new BSON.ObjectID(id)}, card, {safe:true}, function(err, result) {
if (err) {
console.log('Error updating card: ' + err);
res.send({'error':'An error has occurred'});
} else {
console.log('' + result + ' document(s) updated');
res.send(card);
}
});
});
}
exports.deleteCard = function(req, res) {
var id = req.params.id;
console.log('Deleting card: ' + id);
db.collection('cards', function(err, collection) {
collection.remove({'_id':new BSON.ObjectID(id)}, {safe:true}, function(err, result) {
if (err) {
res.send({'error':'An error has occurred - ' + err});
} else {
console.log('' + result + ' document(s) deleted');
res.send(req.body);
}
});
});
}
当我在AngularJS控制器中从DB中获得卡片时,一切都很好。所有卡片都正确显示在屏幕上。这是获得卡片的代码:
var mindrApp = angular.module('mindrApp', ['ngResource'])
mindrApp.controller('WorkflowController', function ($scope, $resource) {
var CardService = $resource("http://localhost:3000/cards/:cardId", {cardId:"@id"});
$scope.cards = CardService.query();
});
在每张卡上都有一些按钮,可用于将卡的状态更改为工作流中可用的下一个状态(由当前状态可用操作定义)。
当点击按钮时,卡id和下一个状态将传递给控制器中的一个功能:
<div class="btn-group btn-group-xs">
<button type="button" class="btn btn-default"
ng-repeat="currentAction in currentState.actions | filter:{default:true}"
ng-click="processCard(currentCard._id, currentAction.next)">
{{currentAction.name}}
</button>
</div>
这是控制器中的processCard功能:
$scope.processCard = function(id, nextState) {
var currentCard = CardService.get({cardId: id}, function(){
currentCard.state = nextState;
currentCard.$save();
});
};
发生的情况是,当我单击按钮时,没有更改当前卡的状态,而是创建了一个具有String类型的id字段的新卡。这是服务器的输出:
Retrieving card: 52910f2a26f1db6a13915d9f
GET /cards/52910f2a26f1db6a13915d9f 200 1ms - 152b
Adding card: {"_id":"52910f2a26f1db6a13915d9f","content":"this is some content for this really cool card","workflow":"simple","state":"completed"}
Success: {"_id":"52910f2a26f1db6a13915d9f","content":"this is some content for this really cool card","workflow":"simple","state":"completed"}
POST /cards 200 1ms - 150b
知道为什么会发生这种事吗?为什么它在服务器上调用addCard函数,而不是调用updateCard
$resource对象的$save()操作使用POST作为默认请求类型(在此处阅读更多信息)。因此,在您的情况下,调用了对路由/cards/:id
的POST请求,因此,创建了一个新卡。
在server.js 中创建一个新的路由条目来处理POST更新请求
app.post('/cards/:id', cards.updateCard);
或者在您的CardService中添加另一个使用PUT的操作,并在您想要更新您的卡时调用它
var CardService = $resource("http://localhost:3000/cards/:cardId", {cardId:"@id"},
{ update: { method: 'PUT' } }
);
// update the card
...
currentCard.$update();
好的,所以我想明白了。我遇到的两个问题是:
1) 它没有更新数据库中的现有项,而是创建了一个具有相同ID但采用字符串格式的新项,而不是使用ObjectId格式。
2) 每当我调用$update时,它都不会将ID附加到路径,而是始终PUT到/cads。
以下是每一个问题的解决方案。
1) 这实际上是一个假设所有id都是ObjectId格式的破解。我不喜欢这个解决方案,但目前它有效,我坚持它。我所要做的就是添加转换卡的行_id返回到服务器端cards.js文件中的updateCard函数的ObjectId格式。
exports.updateCard = function(req, res) {
var id = req.params.id;
var card = req.body;
console.log('Updating card: ' + id);
console.log(JSON.stringify(card));
card._id = new BSON.ObjectID.createFromHexString(card._id); // HACK!
db.collection('cards', function(err, collection) {
collection.update({'_id':new BSON.ObjectID(id)}, card, {safe:true}, function(err, result) {
if (err) {
console.log('Error updating card: ' + err);
res.send({'error':'An error has occurred'});
} else {
console.log('' + result + ' document(s) updated');
res.send(card);
}
});
});
}
2) 这是一个由两部分组成的修复。首先,我必须修改services.js文件,明确表示我想通过PUT使用update:
var mindrServices = angular.module('mindrServices', ['ngResource']);
mindrServices.factory("Card", ["$resource",
function($resource) {
return $resource("http://localhost:3000/cards/:cardId", {cardId:"@id"},
{
query: {method: "GET", isArray:true},
update: {method: "PUT"}
}
);
}]);
接下来,我假设简单地调用currentCard$update()将从调用实例获取ID,相反,我必须显式地传入ID,如下所示:
var mindrControllers = angular.module('mindrControllers', []);
mindrControllers.controller('CardsController', ["$scope", "Card",
function ($scope, Card) {
$scope.cards = Card.query();
console.log("cards populated correctly...");
$scope.processCard = function(currentCard, currentAction) {
console.log("processCard: card[" + currentCard._id + "] needs to be moved to [" + currentAction.next + "] state... ");
currentCard.state = currentAction.next;
currentCard.$update({cardId: currentCard._id}); // passing the ID explicitly
}
这是我在服务器端得到的输出:
Updating card: 52910eb526f1db6a13915d9c
{"_id":"52910eb526f1db6a13915d9c","content":"this is some content for this really cool card","workflow":"simple","state":"backlog"}
1 document(s) updated
PUT /cards/52910eb526f1db6a13915d9c 200 4ms - 111b