I have the following Schema:
Dates.attachSchema(new SimpleSchema({
description: {
type: String,
label: "Description",
max: 50
},
start: {
type: Date,
autoform: {
afFieldInput: {
type: "bootstrap-datepicker"
}
}
},
end: {
type: Date,
autoform: {
afFieldInput: {
type: "bootstrap-datepicker"
}
}
}
}));
How can I validate that the end date is not before start? I am using MomentJS to handle date types, however my main problem is how I can access other attributes in the custom function.
For instance:
end: {
type: Date,
autoform: {
afFieldInput: {
type: "bootstrap-datepicker"
}
},
custom: function() {
if (moment(this.value).isBefore(start)) return "badDate";
}
}
How can I access start?
Furthermore, how can I validate if the start + end date combination is unique, meaning there is no document saved in my database which has the exact same start and end date?
For the inter-field communication, you can do:
end: {
type: Date,
autoform: {
afFieldInput: {
type: "bootstrap-datepicker"
}
},
custom: function() {
// get a reference to the fields
var start = this.field('start');
var end = this;
// Make sure the fields are set so that .value is not undefined
if (start.isSet && end.isSet) {
if (moment(end.value).isBefore(start.value)) return "badDate";
}
}
}
You should of course declare the badDate error first
SimpleSchema.messages({
badDate: 'End date must be after the start date.',
notDateCombinationUnique: 'The start/end date combination must be unique'
})
Regarding the uniqueness, first of all simple schema itself does not provide uniqueness check. You should add aldeed:collection2 for that.
Furthermore, collection2 is capable of checking only a single field uniqueness. To accomplish compound indexes, you should use the ensureIndex syntax
Dates._ensureIndex( { start: 1, end: 1 }, { unique: true } )
Even after this, you will not be able to see the error from this compound index on your form because autoform needs to be aware that such error is existing.
AutoForm.hooks({
NewDatesForm: { // Use whatever name you have given your form
before: {
method: function(doc) {
var form = this;
// clear the error that gets added on the previous error so the form can proceed the second time
form.removeStickyValidationError('start');
return doc;
}
},
onSuccess: function(operation, result, template) {
if (result) {
// do whatever you want if the form submission is successful;
}
},
onError: function(operation, error) {
var form = this;
if (error) {
if (error.reason && error.reason.indexOf('duplicate key error')) {
// We add this error to the first field so it shows up there
form.addStickyValidationError('start', 'notDateCombinationUnique'); // of course you have added this message to your definition earlier on
AutoForm.validateField(form.formId, 'start');
}
}
}
}
});
Related
I attempted to create a model in sequelize (say has 3 attributes, attrA, B, and C) with some custom validation logic. This tutorial helped me get most of it set up:
const Model = Sequelize.define('model', {
attrA: { type: Sequelize.STRING },
attrB: { type: Sequelize.STRING },
attrC: { type: Sequelize.STRING },
}, {
validate: {
someValidationLogic() {
// Do something with attrA,B,C
// if (this.attrA ... this.attrB ... this.attrC) throw new Error..
}
}
})
In the application logic however, only say, 2 out of the 3 attributes (A and B) need to be updated:
Model.update(
{
attrA: 'foo',
attrB: 'bar'
}, {
where: {
id: 1,
},
returning: true,
})
This results in that when the custom validation logic being called, in the this object accessed in the function, only attrA and attrB are defined in this, and attrC remained undefined. This causes the validation logic to fail because attrC cannot be read. Is there any way I can get the object visible from someValidationLogic() to have all attributes populated? Or should this "validation" shouldn't have been validation logic at all and should've been done on the application level?
Your validation logic could take in account the possibility of attrC not being defined :
validate: {
someValidationLogic() {
if (this.attrA !== 'undefined' && this.attrA === 'forbidden value' ) {
// throw new Error
}
}
}
But if your validation includes checking the provided values against current database values, then you would better handle this in the application layer : first recover the current database record, manipulate it as needed, then save it to database.
So I'm trying to access this account by using the findOne function in mongoose, and I'm trying to console.log the error, but the error is just the correct model found.. once I find the correct model I want to access one of the nested objects in the schema so I can edit the value.
I'm not sure why this is happening, below I put the code as well as the error that was logged into the console, I can provide more if needed.
let accountSchema = mongoose.Schema({
username:{
type: String,
required: true,
index: true,
unique: true,
},
password:{
type: String,
required: true,
},
money:{
type: Number,
},
inventory: { type: [{
weed: { type: Number },
coke: { type: Number },
}]},
});
mp.events.addCommand('coke', (player) => {
console.log(player.name);
Account.findOne({username: 'a'}, function(acc, err) {
if(err) return console.log(err);
console.log(acc.username);
acc.inventory[1] = acc.inventory[1] + 1;
acc.save(function(err){
if(err) return player.outputChatBox('Not logged in');
player.outputChatBox('Added 1 coke');
});
});
});
(Console) {"_id":"5b6acbbbc285477e39514cb9","username":"a","password":"$2a$10$XABqooqFRINYVdJ79.i2E.5xdpitRrfZxUBmIPAZjjaXKvvLDc2y2","money":5000,"inventory":[{"_id":"5b6acbbbc285477e39514cbb","weed":0},{"_id":"5b6acbbbc285477e39514cba","coke":0}],"__v":0}
The callback function for the .findOne method has the following signature:
function (err, obj) {
}
You are using the arguments in the wrong order - the error object is the first argument and the object found is the second one.
The .findOne method callback must have the following parameters function (err, res). So you are setting them in a reversed order.
Check http://mongoosejs.com/docs/api.html#model_Model.findOne
In the Numeric Model for storing monetary values, the MongoDB docs state:
From the mongo shell decimal values are assigned and queried using the NumberDecimal() constructor.
Similarly, when using the Morphia Java library, BigDecimals are automatically inserted as BigDecimals.
I'm querying Mongo in Node with Mongoose and attempting to extract the numeric value of a field stored as a NumberDecimal. However, the value is oddly wrapped in query results and I'm not sure how to extract it through Mongo or Mongoose:
[
{
"openValue":{
"$numberDecimal":"119.931"
},
"timestamp":"2017-01-20T10:30:00.000Z"
},
{
"openValue":{
"$numberDecimal":"119.965"
},
"timestamp":"2017-01-20T10:31:00.000Z"
}
]
One post I read stated using parseFloat() in my application code will perform what I desire, however it's not efficient to iterate through the result to perform this transformation. Avoiding iterating and transforming would mean running the function on the NumberDecimals whenever I want their value every time, which would be annoying.
Is there a way I can use Mongo or Mongoose to convert the above JSON query-result into what's below?
[
{
"openValue": 119.931,
"timestamp":"2017-01-20T10:30:00.000Z"
},
{
"openValue": 119.965,
"timestamp":"2017-01-20T10:31:00.000Z"
},
{
"openValue": 119.975,
"timestamp":"2017-01-20T10:32:00.000Z"
}
]
I tried selecting the field as ...openValue.$numberDecimal, but this didn't work. Thank you!
Edit: Here's my Mongoose schema:
var EquityHistoryModel = new Schema({
_id: {
equityIdentifier: { type: {
exchange: { type: String, index: true },
symbol: { type: String, index: true }
}, index: true },
instant: { type: Date, index: true },
durationMinutes: { type: Number }
},
open: { type: mongoose.Schema.Types.Decimal },
high: { type: mongoose.Schema.Types.Decimal },
low: { type: mongoose.Schema.Types.Decimal },
close: { type: mongoose.Schema.Types.Decimal },
volume: { type: Number },
isDividendAdjusted: { type: Boolean },
isSplitAdjusted: { type: Boolean }
}, { collection: 'equityHistories', versionKey: false });
Here's the Mongoose query for the first JSON result above:
mongo.EquityHistoryModel.aggregate([
{
"$match":{
"_id.equityIdentifier.exchange":passed_in,
"_id.equityIdentifier.symbol":passed_in,
"_id.instant":passed_in
}
},
{
"$project":{
"_id":0,
"openValue":"$open",
"timestamp":"$_id.instant"
}
}
],
You can also overwrite the toJSON method:
// Never return '__v' field and return the 'price' as String in the JSON representation
// Note that this doesn't effect `toObject`
EquityHistoryModel.set('toJSON', {
getters: true,
transform: (doc, ret) => {
if (ret.price) {
ret.price = ret.price.toString();
}
delete ret.__v;
return ret;
},
});
Contrary to what I expected, it seems the values are automatically extracted. Stringifying the result automatically wraps the value in the NumberDecimal. See the code with the output manually placed below:
console.log(docs[0].openValue);
119.800
console.log(JSON.stringify(docs[0].openValue]);
{"$numberDecimal":"119.800"}
Also, I struggled with sending query results, due to res.json or res.send using stringify. Instead I wrote a replace function and set the property in Node.
im doing the "Intermediate Meteor Tutorial #8 - Insert Permissions, Publishing & Meteor Toys" by LevelUpTuts and my problem is that i cant submit the form i checked the code 5 times but in my opinion everything is right im running meteor 1.4 here is my code
my Recipes.js file
Recipes = new Meteor.Collection('recipes');
Recipes.allow({
insert: function(userId, doc) {
return !!userId;
}
});
RecipeSchema = new SimpleSchema ({
name: {
type: String,
label: "Name"
},
desc: {
type: String,
label: "Description"
},
author: {
type: String,
label: "Author",
autoValue: function() {
return this.userID
},
autoform: {
type: "hidden"
},
},
createdAt: {
type: Date,
label: "CreatedAt",
autoValue: function() {
return new Date()
},
autoform: {
type: "hidden"
},
},
});
Recipes.attachSchema( RecipeSchema);
my recipes.js
Meteor.subscribe('recipes');
my NewRecipe.js
<template name="NewRecipe">
<div class="new-recipe-container">
{{> quickForm collection="Recipes" id="insertRecipeForm" type="insert" class="new-recipe-form"}}
</div>
</template>
and the publis.js file
Meteor.publish('recipes', function(){
return Recipes.find({author: this.userId});
});
Please help me i dont know what i am doing wrong
i don't have the answer for you (at least not yet), but i'm posting this as an answer so i can provide some formatted code.
you posted some code under NewRecipe.js, but i assume that view code is in NewRecipe.html. Try 2 things:
first, put this code in NewRecipe.js onCreated():
SimpleSchema.debug = true;
AutoForm.addHooks(null, {
onError: function(name, error, template) {
console.log(name + " error:", error);
}
});
that will enable some debugging for the quickform.
second, in the schema definition, comment out the Recipes.allow() block to see if that's what's blocking saving your data.
then report back on how that goes.
I'm using the jQuery Validation Plugin and want to run my own code when the plugin detects a valid or invalid input.
I've figured out that the two .validate() options I need are success and showErrors and I can get them both to work on their own:
var validator = $('#form').validate({
rules: {
name: "required",
email: {
required: true,
email: true
}
},
success: function() {
console.log('success');
}
That logs success any time a valid input is made. And showErrors works correctly also:
var validator = $('#form').validate({
rules: {
name: "required",
email: {
required: true,
email: true
}
},
showErrors: function() {
console.log('error');
}
But when I try to combine the two, error is logged every time regardless of whether the input is valid:
var validator = $('#form').validate({
rules: {
name: "required",
email: {
required: true,
email: true
}
},
success: function() {
console.log('success');
},
showErrors: function() {
console.log('error');
}
The order of the options doesn't have any effect.
Does anyone know why the two options don't work together and how I can run my own functions on valid and invalid inputs?
"showErrors" is not called just when an error is detected, it's called everytime you change the input, regardless the value you typed.
"showErrors" receives two parameters: "errorMap" and "errorList". To verify if there really was an error you have to check one of those values:
showErrors: function(errorMap, errorList) {
if (errorsList.length > 0) {
console.log('error');
}
}
You can also handle the "success" event inside the showErrors function, since it's called in the current validator context.
showErrors: function(errorMap, errorList) {
if (errorsList.length == 0) {
this.currentElements.addClass("success");
}
}
Figured it out... sort of.
I replaced showErrors with highlight, which allows me to run a callback on either valid or invalid entries.
However, the plugin still displays the default error messages -- probably since I'm not doing anything with showErrors. So I had to hack that by setting an empty string for the message on each field:
var validator = $('#form').validate({
rules: {
name: "required",
email: {
required: true,
email: true
}
},
messages: {
name: '',
email: ''
},
success: function() {
console.log('success');
},
highlight: function() {
console.log('highlight');
}
}
Certainly not as clean as I would like, so if anyone has a better way that would be great.