I was trying to set some info in my database using the $child method in angularFire, only to realise that it had been completely removed in the 0.8 version update.
I then looked to see what the replacement would be for this by reading this:
https://www.firebase.com/blog/2014-07-30-introducing-angularfire-08.html
But that didn't shed any light on the situation.
If I got a situation like this:
$scope.validate = function() {
//--- Some validation here ---//
var userInfo = {
firstname: $('#firstname').val(),
lastname: $('#lastname').val(),
streetAddress: $('#street-address').val(),
city: $('#city').val(),
zip: $('#firstname').val(),
country: $('#country').val(),
email: $('#email').val(),
password: $('#password').val(),
confirmedPassword: $('#confirmed-password').val()
}
$scope.registerNewUser(userInfo);
},
$scope.registerNewUser = function(userInfo) {
var ref = new Firebase("https://pie-shop.firebaseio.com/");
ref.createUser({
email : userInfo.email,
password : userInfo.password
}, function(error, user) {
if (error === null) {
$scope.storeUserInfo(userInfo, user);
}
else {
console.log(error);
}
});
},
$scope.storeUserInfo = function(userInfo, user) {
var ref = new Firebase('https://pie-shop.firebaseio.com/users/' + user.uid);
var sync = $firebase(ref);
sync.$child(user.uid).set({
firstname: userInfo.firstname,
lastname: userInfo.lastname,
streetAddress: userInfo.streetAddress,
city: userInfo.streetAddress,
zip: userInfo.zip,
country: userInfo.country
});
}
Where I need to set a key that's named user.uid (which is the id of a user) and this key should contain the info from the userInfo object, how would I do it now since $child is removed? I've been searching for hours but can't find anything on this that explains how to do it in angularFire specifically.
Pre-posting note:
After checking the API documentation here it says that just using sync.$set({foo: 'bar'}) should work but in my case it doesn't do it for me either. Can someone tell me where I'm going wrong?
I am not entirely sure what AngularFire's pre-0.8 $child method did. But it likely was related to Firebase's regular child method.
In that case instead of this:
var ref = new Firebase(...);
var sync = $firebase(ref);
sync.$child(user.uid)...
You can also do:
var ref = new Firebase(...).child(user.uid);
var sync = $firebase(ref);
sync...
Related
I am trying to query my database like this
const Admin = require("./model/Admin");
module.exports = {
validateUser : (uName, pwd) => {
mongoose.connect("mongodb://127.0.0.1:27017/TeamDK")
//console.log("Uname: " + uName + ", Pwd: " + pwd)
res = Admin.findOne({ initial: "tt" }).exec()
}
}
In the case above, nothing is returned. The value of res is just null.
I also tried using a call back like this
res = Admin.findOne({ initial: "tt" }, (err, val) => {
console.log(res)
Using a callback, the output of res is different. I have added a screenshot of this, and only part of it is shown, as it is very big.
I'm sure there is a document in my Model collection. When using monosh like this
TeamDK> db.Admin.findOne({ initial: "tt" }), this is what I get
{
_id: ObjectId("63ebbd6c59097f4a25f23d31"),
firstName: 'Test',
initial: 'tt',
lastName: 'Unknown',
email: 'test#gg.com',
password: '123',
role: 4
}
If it's of any use, my model looks like this:
const adminSchema = mongoose.Schema({
firstName : {
type : String,
required : [true, "Why no name?"]
},
initial: {
type: String,
required: [true, "Why no initials?"],
index: {unique: true }
},
lastName : {
type : String,
required : [true, "Why no last name?"]
},
email : {
type : String,
required : [true, "Then how are we gonna contact you?"],
index: {unique: true }
},
password : {
type : String,
required : [true, "Come one man"]
},
dateCreate : {
type : Date,
default : Date.now
},
role : {
type : mongoose.Types.ObjectId,
ref : "role"
}
})
module.exports = mongoose.model("Admin", adminSchema);
I've spent almost a day trying to fix this, tried following guids on https://mongoosejs.com/ and tried looking for similar problems here on stackoverflow, but I just can't figure out what I'm doing wrong
------------------------- Update ---------------------------
I have also tried making the function async, and use await. I tried isolating the code in a new file and doing like this
const mongoose = require ("mongoose");
const Admin = require("./model/Admin");
async function testAdmin() {
await mongoose.connect("mongodb://127.0.0.1:27017/TeamDK")
res = await Admin.findOne({ initial: "tt" }).exec()
console.log(res)
}
testAdmin()
I also tried removing the exec(), doing
res = await Admin.findOne({ initial: "tt" })
I'm still getting null returned. Any other suggestions are most welcome
------------------------- Update ---------------------------
I have just noticed that if I do: res = await Admin.find() it works. Though it finds all the documents, and I only need a specific one, but it shows that it does contact the database. For some reason, it does not seem to accept the res = await Admin.findOne(...)
The .findOne is asynchronous, we must use await or callback to receive its result:
const Admin = require("./model/Admin");
module.exports = {
validateUser : async (uName, pwd) => {
await mongoose.connect("mongodb://127.0.0.1:27017/TeamDK") // It's better to put this connect function somewhere else, not in the Model
res = await Admin.findOne({ initial: "tt" });
return res;
}
}
Then use it a controller like this, example in Express
// The connect function is better here
// mongoose.connect("mongodb://127.0.0.1:27017/TeamDK")
app.post('/', async (req, res) => {
const result = await validateUser('username', 'password');
res.json(result);
})
I finally found out what the issue is. For some reason, it's as if the document that I was querying doesn't exist when I try to find it through node.js. I can find it when using mongosh though. I tried to query another document in the collection, and that one it found. So I tried yet another one, and that one it also finds. It seems to find all other, except this one (no matter which field I use as argument, it just returns null):
{
_id: ObjectId("63ebbd6c59097f4a25f23d31"),
firstName: 'Test',
initial: 'tt',
lastName: 'Unknown',
email: 'test#gg.com',
password: '123',
role: 4
}
Yet I created it exactly the same way as all the others. I have no idea why I can't query this in nodejs, and it's a problem if it should ever act like that in an application. Thankfully this is just practice. It's very likely that they error is at my side, but I haven't located it.
So should anyone experience the issue of not being able to access a document that you're certain is there (using nodejs), try see if you can access another document in the collection
I have a schema that has an id field that is set to a string. When I use collection.find({id: somenumber}) it returns nothing.
I've tried casting somenumber to a string and to a number. I've tried sending somenumber through as a regex. I've tried putting id in quotes and bare... I have no idea what's going on. Any help and input would be appreciated.
Toys.js
var Schema = mongoose.Schema;
var toySchema = new Schema( {
id: {type: String, required: true, unique: true},
name: {type: String, required: true},
price: Number
} );
My index.js is as such
app.use('/findToy', (req, res) => {
let query = {};
if (req.query.id)
query.id = req.query.id;
console.log(query);
// I've tried using the query variable and explicitly stating the object as below. Neither works.
Toy.find({id: '123'}, (err, toy) => {
if (!err) {
console.log("i'm right here, no errors and nothing in the query");
res.json(toy);
}
else {
console.log(err);
res.json({})
}
})
I know that there is a Toy in my mongoDB instance with id: '123'. If I do Toy.find() it returns:
[{"_id":"5bb7d8e4a620efb05cb407d2","id":"123","name":"Dog chew toy","price":10.99},
{"_id":"5bb7d8f7a620efb05cb407d3","id":"456","name":"Dog pillow","price":25.99}]
I'm at a complete loss, really.
This is what you're looking for. Visit the link for references, but here's a little snippet.
For the sake of this example, let's have a static id, even though Mongo creates a dynamic one [ _id ]. Maybe that what is the problem here. If you already a record in your DB with that id, there's no need for adding it manually, especially not the already existing one. Anyways, Drop your DB collection, and try out this simple example:
// Search by ObjectId
const id = "123";
ToyModel.findById(id, (err, user) => {
if(err) {
// Handle your error here
} else {
// If that 'toy' was found do whatever you want with it :)
}
});
Also, a very similar API is findOne.
ToyModel.findOne({_id: id}, function (err, toy) { ... });
I am trying to create a service that can be used to update nested fields in a Mongoose model. In the following example I am trying to set the field 'meta.status' to the value 2. This is the service:
angular.module('rooms').factory('UpdateSvc',['$http', function($http)
{
return function(model, id, args)
{
var url = '/roomieUpdate/' + id;
$http.put(url, args).then(function(response)
{
response = response.data;
console.log(response.data);
});
}
}]);
This is how it is called in the controller:
var newStatus = {'meta.$.status' : 2};
var update = UpdateSvc("roomie", sessionStorage.getItem('userID'), newStatus);
And this is the model:
var RoomieSchema = new Schema(
{
meta:
{
created:
{
type: Date,
default: Date.now
},
status:
{
type: Number,
default: '1',
}
}
}
And this is the route:
app.put('/roomieUpdate/:id', function(req,res)
{
var id = req.params.id;
Roomie.findOneAndUpdate(
{_id: mongoose.Types.ObjectId(id)},
req.body,
{ new : true },
function(err, doc)
{
if(err)
{
console.log(err);
}
res.json(doc);
console.log(doc);
});
});
The argument is received correctly, but I can't seem to get this to work. I am not even getting an error message. console.log(doc) simply prints out the object and the field meta.status remains '1'. I have done a direct Mongo search on the target object to make sure that I wasn't just reading the old document. I've tried a great many things like separating the key and value of req.body and use {$set:{key:value}}, but result is the same.
findOneAndUpdate() by default will return the old document, not the new (updated) document.
For that, you need to set the new option:
Roomie.findOneAndUpdate({
_id : mongoose.Types.ObjectId(id)
}, req.body, { new : true }, function(err, doc) {
...
});
As it turns out, var newStatus = {'meta.$.status' : 2}; should have been var newStatus = {'meta.status' : 2}; The document now updates correctly.
The reason the $ was there in the first place was probably based on this thread:
findOneAndUpdate - Update the first object in array that has specific attribute
or another of the many threads I read through about this issue. I had tried several solutions with and without it, but couldn't get anything to go right.
I have a userSchema that looks like this:
var userSchema = new Schema({
name: {
type: String
, required: true
, validate: [validators.notEmpty, 'Name is empty']
}
, username: {
type: String
, required: true
, validate: [validators.notEmpty, 'Username is empty']
}
, email: {
type: String
, required: true
, validate: [
{ validator: validators.notEmpty, msg: 'Email is empty' }
, { validator: validators.isEmail, msg: 'Invalid email' }
]
}
, salt: String
, hash: String
});
All of my validation is happening in the schema so far, and I'm wondering how to go about achieving this with password validation. The user inputs the password into two fields, and the model should check that they are the same as each other.
Does this kind of validation belong in the schema? I'm new to this sort of validation.
How should I validate the passwords?
I eventually discovered that you can use a combination of virtual paths and the invalidate function to achieve this, as shown in this gist, for the very same purpose of matching passwords: https://gist.github.com/1350041
To quote directly:
CustomerSchema.virtual('password')
.get(function() {
return this._password;
})
.set(function(value) {
this._password = value;
var salt = bcrypt.gen_salt_sync(12);
this.passwordHash = bcrypt.encrypt_sync(value, salt);
});
CustomerSchema.virtual('passwordConfirmation')
.get(function() {
return this._passwordConfirmation;
})
.set(function(value) {
this._passwordConfirmation = value;
});
CustomerSchema.path('passwordHash').validate(function(v) {
if (this._password || this._passwordConfirmation) {
if (!val.check(this._password).min(6)) {
this.invalidate('password', 'must be at least 6 characters.');
}
if (this._password !== this._passwordConfirmation) {
this.invalidate('passwordConfirmation', 'must match confirmation.');
}
}
if (this.isNew && !this._password) {
this.invalidate('password', 'required');
}
}, null);
I think password matching belongs in the client interface and should never get to the server (DB layer is already too much). It's better for the user experience not to have a server roundtrip just to tell the user that 2 strings are different.
As for thin controller, fat model... all these silver bullets out there should be shot back at the originator. No solution is good in any situation. Think everyone of them in their own context.
Bringing the fat model idea here, makes you use a feature (schema validation) for a totally different purpose (password matching) and makes your app dependent on the tech you're using now. One day you'll want to change tech and you'll get to something without schema validation at all... and then you'll have to remember that part of functionality of your app relied on that. And you'll have to move it back to the client side or to the controller.
I know the thread is old but if it could save someone's time...
My approach uses pre-validate hook and works perfectly for me
schema.virtual('passwordConfirmation')
.get(function() {
return this._passwordConfirmation;
})
.set(function(value) {
this._passwordConfirmation = value;
});
schema.pre('validate', function(next) {
if (this.password !== this.passwordConfirmation) {
this.invalidate('passwordConfirmation', 'enter the same password');
}
next();
});
I use express-validator before it ever gets down to the schema level in ./routes/signup.js:
exports.post = function(req, res){
req.assert('email', 'Enter email').notEmpty().isEmail();
req.assert('username', 'Enter username').notEmpty().isAlphanumeric().len(3,20);
req.assert('password', 'Enter password').notEmpty().notContains(' ').len(5,20);
res.locals.err = req.validationErrors(true);
if ( res.locals.err ) {
res.render('signup', { message: { error: 'Woops, looks like we need more info...'} });
return;
}
...//save
};
You can attach custom methods to your model instances by adding new function attributes to Schema.methods (you can also create Schema functions using Schema.statics.) Here's an example that validates a user's password:
userSchema.methods.checkPassword = function(password) {
return (hash(password) === this.password);
};
// You could then check if a user's password is valid like so:
UserModel.findOne({ email: 'email#gmail.com' }, function(err, user) {
if (user.checkPassword('secretPassword')) {
// ... user is legit
}
});
The second verification password doesn't need to be submitted for registration.
You could probably get away with validating the two fields are equal on the client-side.
It's kind of late but for the sake of people having similar issues. i ran into a similar problem lately, and here was how i went about it; i used a library called joi
const joi = require('joi');
...
function validateUser(user){
const schema = joi.object({
username: joi.string().min(3).max(50).required(),
email: joi.string().min(10).max(255).required().email(),
password: joi.string().min(5).max(255).required(),
password2: joi.string().valid(joi.ref('password')).required(),
});
return schema.validate(user);
}
exports.validate = validateUser;
I'm trying to add an embedded document to an existing document field. I found one fitting answer with the search but I'm running into errors. I'm using node.js, Express and Mongoose.
My database schemas:
var entry = new Schema({
name : { type : String, required : true},
description : { type : String, default: ""},
});
var compo = new Schema({
name : String,
description : String,
entries : [entry]
});
And I'm trying to update the entries array with the following code
var entry = new entryModel();
entry.name = "new name";
entry.description= "new description";
compoModel.findOne(query, function (err, item) {
if (item) {
item.entries.push(entry);
item.save(function (err) {
if (!err) {
log.debug('Entry added successfully.');
} else {
log.error("Mongoose couldn't save entry: " + err);
}
});
}
});
It yields an error: TypeError: Object.keys called on non-object
What have I missed?
So I managed to get it working via the Model.update method by simply adding a new object to the compo.entries list and calling compoModel.update.
My similar issue (same error) was solved by clearing the sub-document array. It was populated prior to the definition of the sub-document scheme. At least this is what i think happened.
E.g.:
var token = new Schema( { value: String, expires: Date } )
var user = new Schema( { username: String, tokens: [token] } )
.. and, prior to defining the 'token' scheme i had entries such as:
{ username: 'foo', tokens: ['123456'] }
.. so, clearing tokens did it for me.
user.tokens = []
user.save()