One thing that has been frustrating me in learning the MEAN stack has been how to extend the basic Article example (or a CRUD created module) beyond the basic data model. While you're trying to learn AngularJS and Mongoose/MongoDB interactions, you can find lots of literature on modelling, but little on how to modify your core CRUD views, model and controllers to work together. I'm sure that there may/are better and more elegant ways to accomplish this.
Most of the examples I was finding are actually setting up ObjectId relationships between collections of data. I'm still learning the Mongo terms, but I was trying to take advantage of not having to relate data, but rather to stick it into a document in a collection. We'll see if that was the right approach...
Here's what I needed to learn:
- how to create a nested object in the model
- how to name and reference fields in the angular form
- how to add the submitted POST data to the variable so that it gets inserted
- how to display the subdocument data in the angular form during an edit
I looked diligently and spent hours trying to figure out how to accomplish getting a simple nested object modeled in Mongoose, displayed in the Create form, inserted into MongoDB and then loaded back into the Update form.
Take a common pattern like contact information. I want to create a Mongo document for a department. The department has one or more contact people/phone/email addresses. So let's start with the model.
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var department = new Schema({
...
contact: [{
phone: {
type: String,
default: '',
trim: true
},
email: {
type: String,
default: '',
trim: true
}
}],
...
});
In the client controller, we're going to need extend the properties and populate the values coming from the POST'd form data. In the Create function, the generated CRUD boilerplate uses name = this.name. We're going to extend this.fieldname for our model. Note the contact: [] line. This is setting up an array of type mixed. I'm still researching how best to declare that, but it is working as written.
// Create new Department
$scope.create = function() {
// Create new Deparment object
var department = new Departments ({
name: this.name,
deptLoc: this.deptLoc,
abbreviation: this.abbreviation,
description: this.description,
itemType: this.itemType,
contact: []
});
// in order to populate subdocuments, we need to push data from form into model
// push our contact info into the model
department.contact.push({phone: $scope.contact.phone, email: $scope.contact.email});
Later in this same Create function, the boilerplate will have set a section for "clear form fields". We'll need to clear the form fields using the same pattern used above where the values have been set using this.formField.
// Clear form fields
$scope.name = '';
$scope.coordinates = '';
$scope.deptLoc = '';
$scope.abbreviation = '';
$scope.description = '';
$scope.itemtype = '';
$scope.contact.phone = '';
$scope.contact.email = '';
Finally, we'll need to look at the create and edit view partials and adjust the form fields. Here's the contact snippet from the create form.
<label class="control-label" for="contact">Department Contact</label>
<div class="controls">
<input type="text" name="phone" data-ng-model="contact.phone" id="contact" class="form-control" placeholder="Department Phone Number">
<input type="text" name="email" data-ng-model="contact.email" id="contact" class="form-control" placeholder="Department Email">
</div>
Here's the same snippet from the edit form. Note the data-ng-model reference and dot notation as we drill down into the contact array. Assuming more than one contact, we would setup a loop to output other contacts in this edit form as well as modifying the department.contact.push in the Create function.
<label class="control-label" for="contact">Department Contact</label>
<div class="controls">
<input type="text" name="contact.phone" data-ng-model="department.contact[0].phone" id="contact" class="form-control" placeholder="Department Phone Number">
<input type="text" name="contact.email" data-ng-model="department.contact[0].email" id="contact" class="form-control" placeholder="Department Email">
</div>