mongoose set: toLower not working on nested properties - mongodb

I'm trying to normalize email addresses to lowercase before they are saved in the database. Setters on mongoose models are great for that and they work on simple models. However, when I try to set the toLower setter on a nested object where email is stored inside "owner" I get TypeError: Cannot call method 'toLowerCase' of undefined
function toLower (v) {
return v.toLowerCase();
}
This crashes:
var BusinessSchema = new mongoose.Schema({
owner: {
email: { type: String, required: 'Email adres mag niet leeg zijn.', set: toLower, get: toLower, index: { unique: true } },
password: { type: String, required: 'Wachtwoord mag niet leeg zijn.' }
}
});
This works:
var UserSchema = new Schema({
email: { type: String, set: toLower }
});

Because you're also using toLower as a getter, it's first called with a value of undefined when creating a new doc, then called again with the actual value. Regardless, your toLower function needs to protect itself so that it can be called with any value, not just strings.
So something like:
function toLower (v) {
if ('string' != typeof v) v = '';
return v.toLowerCase();
}
If you don't need the getter side of this, you could also just use the built-in lowercase setter:
var BusinessSchema = new mongoose.Schema({
owner: {
email: {
type: String,
required: 'Email adres mag niet leeg zijn.',
lowercase: true,
index: { unique: true }
},
password: { type: String, required: 'Wachtwoord mag niet leeg zijn.' }
}
});

Related

why does this populate give me a not found function error?

How can I execute this populate so that I can get the username of the person that does the tweet? I've tried with the function getusername() but it is not working as it gives me a tweetsSchema.findOne is not a function error.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var tweetsSchema = new Schema(
{
tweets: { type: String, required: true }, //reference to the associated book
replies: { type: String, required: false },
username: { type: Schema.Types.ObjectId, ref: 'users' }
}
);
// Virtual for bookinstance's URL
function getUsername(tweets){
return tweetsSchema.findOne({ tweets: tweets })
.populate('username').exec((err, posts) => {
console.log("Populated User " + username);
})
}
getUsername()
//Export model
module.exports = mongoose.model('tweets', tweetsSchema);
currently the function you have defined is just a function and haven't defined on the Schema of yours and not exported.
you might want to use static in mongoose
tweetsSchema.static('getUsername', async function(tweets){
return await tweetsSchema.findOne({ tweets: tweets })
.populate('username').exec((err, posts) => {
console.log("Populated User " + username);
})
})

mongoose set a default field to take value of 2 other fields

Is there a way to let one field take value of two other fields merged as default.
I have a user schema as follows:
const UserSchema = mongoose.Schema({
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},});
I want to add a third field called fullName that defaults to merging firstName + lastName
Is that possible in mongoose?
try this :
fullName:{
type:String,
required:true,
default: function(){
return this.firstName + " " + this.lastName
}}
on doc update :
yourSchema.pre("updateOne", function (next) {
this.set({ fullName: this.get("firstName") + " " + this.get("lastName") });
next();
});
I solved this using Virtual Setters (Virtuals) as documented by Mongoose
Make sure to add this to your Schema to include virtuals when you convert a document to JSON
const opts = { toJSON: { virtuals: true } };

Angular 2 Reactive Form nested group passed null on reset throw error

I have this form group:
Id: [null],
Nominativo: [null, [Validators.required, Validators.maxLength(100)]],
Area: fb.group({
Id: [null]
})
When i try to reset the form with this model:
Id: 1,
Nominativo: 'Test'
Area: null
the area null value throw exception "TypeError: Cannot read property 'Id' of null". My expected result is all Area value become null. I can avoid this problem? My server response with all nested model null when they are all null
I don't think there is a simple clean solution for this. You need to iterate the object properties and set the values manually. So something like this:
resetVal = {Id:1, Nominativo: 'Test', Area: null }
and when you receive the data, you first check if Area is null, if so, set the formcontrols to null in that formgroup. If not, then set the values you get from backend to your formgroup:
this.areaCtrl = this.myForm.controls.Area as FormGroup;
this.myForm.patchValue({
Id: this.resetVal.Id,
Nominativo: this.resetVal.Nominativo,
})
if(this.resetVal.Area === null) {
this.areaCtrl.patchValue({
Id: null
})
}
else {
for(let a in this.resetVal.Area) {
this.areaCtrl.patchValue({
[a]:this.resetVal.Area[a]
})
}
}
DEMO
I suggest to do something like this:
area.model.ts
export class Area {
id: string;
}
data.model.ts
export class DataModel {
id: string;
nominativo: string;
area: Area;
}
form.model.ts
export class FormModel {
id: string;
nominativo: string;
area?: Area;
}
data-model.service.ts
#Injectable()
export class DataModelService {
toFormModel(dataModel: DataModel): FormModel {
const form: FormModel = new FormModel();
form.id = dataModel.id;
form.nominativo = dataModel.nominativo;
if (dataModel.area) {
form.area = dataModel.area;
}
return form;
}
}
form.component.ts
constructor(dataModelService: DataModelService,
dataApi: dataApiService,
fb: FormBuilder) {
this.form = this.fb.group({
id: '',
nominativo: '',
area: this.fb.group({
id: ''
})
});
this.dataApi.getData()
.map((data: DataModel) => this.dataModelService.toFormModel(data))
.subscribe((formData) => {
this.form.patchValue(formData);
});
}

Virtual field AND real field

Is it possible to have a virtual field that is also a field in a model?
var exampleSchema = new Schema({
name : {type: String, required: true}
slug:: {type: String}
});
exampleSchema.virtual('slug').get(function() {
if(this.slug && this.slug.length){
return this.slug;
}
return this.name.toLowerCase().replace(/ /g, '');
});
If slug is set I want to return the slug. If not, I want to return a computed value from name.
I don't want to use a static method, it needs to be a part of the result when pulled a record.
You can create a custom getter function and return the value if it exists, or the computed value if it doesn't.
var exampleSchema = new Schema({
name: {type: String, required: true}
slug: {
type: String,
get: function(value) {
if (value) {
return value;
} else {
return this.name.toLowerCase().replace(/ /g, '');
}
}
}
});

Storing values that aren't defined in the schema (dynamic schema?)

Suppose I have a Schema definition
var Users = mongoose.model('Users', new mongoose.Schema({
username: String,
salt: String,
hash: String,
facebook: {
id: String
}
}));
But I want to later
user = new Users({
username: 'myusername',
facebook: {
id: '3141592653',
displayName: 'mydisplayname' // <- wasn't in the schema ^
}
});
Then displayName simply doesn't gets stored. Is this not allowed in mongoose? Because I would imagine since MongoDB is "schema-less" there ought to be a way to do this?
You can disable the strict option on the schema to allow fields not in the schema to be saved:
var Users = mongoose.model('Users', new mongoose.Schema({
username: String,
salt: String,
hash: String,
facebook: {
id: String
}
}, { strict: false }));