This is the original code snippet used for updating:
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Thing.findById(req.params.id, function (err, thing) {
if (err) { return handleError(res, err); }
if(!thing) { return res.send(404); }
var updated = _.merge(thing, req.body);
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, thing);
});
});
};
Note particularly the line of code that reads:
var updated = _.merge(thing, req.body);
. This line works well with Schema properties that are primitives but fails when dealing with a schema property that is an array.
The main issue stems from the fact that thing
is a mongoose document, whereas req.body
is a javascript object.
I have formulated two hypotheses to pinpoint the core problem:
- _.merge() mandates the first argument to be an object, leading to conflicts when passed a document.
- _.merge() only successfully merges objects with equivalent keys and types. Due to one being a document and the other an object, a mismatch arises.
An illustration can be seen in this example where name
updates accurately but arr
does not. The update operation works seamlessly on both properties when thing.set(req.body)
is utilized instead of
var updated = _.merge(thing, req.body).
thing.html
<div class="container">
<form ng-submit="update()">
<div class="form-group">
<label>Name</label>
<input class="form-control" ng-model="thing.name">
</div>
<div class="form-group">
<label>Arr</label>
<input
type="text"
class="form-control"
ng-repeat="el in thing.arr"
ng-model="thing.arr[$index]">
</div>
<button
class="btn btn-default"
type="submit">
Update
</button>
</form>
{{thing | json}}
</div>
thing.controller.js (front end)
.controller('MainCtrl', function ($scope, $http) {
$http.get('/api/things')
.success(function(things) {
$scope.thing = things[0];
});
$scope.update = function() {
$http.put('/api/things/' + $scope.thing._id, $scope.thing)
.success(function(newThing) {
console.log('updated thing: ', newThing);
})
.error(function() {
console.log('unable to update thing');
});
};
});
thing.model.js
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ThingSchema = new Schema({
name: String,
arr: []
});
module.exports = mongoose.model('Thing', ThingSchema);
thing.controller.js (backend)
'use strict';
var _ = require('lodash');
var Thing = require('./thing.model');
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Thing.findById(req.params.id, function (err, thing) {
if (err) { return handleError(res, err); }
if(!thing) { return res.send(404); }
var updated = _.merge(thing, req.body);
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, thing);
});
});
};
function handleError(res, err) {
return res.send(500, err);
}
These questions remain:
- What limitations arise from using _.merge()?
- Why did the developers of the angular-fullstack generator opt for _.merge()? Their choice likely hinges on a specific rationale given their knowledge of its behavior.