How to set validation for UNIQUE fields in MongoDB, Vuejs? - mongodb

I'm new to MongoDB and Vuejs. I'm not sure if it is possible that I was trying to set validation for Unique value, but somehow it does not work.
My defined schema from back-end:
const uuid = require("uuid");
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
let informationSchema = new Schema ({
_id: {type: String, default: uuid.v1},
firstName: {
type: String, required:true, trim: true, unique: true
},
lastName: {
type: String, required:true, trim: true, unique: true
}
});
module.exports = mongoose.model('informations', informationSchema)
My action form in Vuejs from Front-end:
<template>
<div class="row justify-content-center">
<div class="col-md-6">
<h3 class="text-center">Create Name</h3>
<form #submit.prevent="handleSubmitForm">
<div class="form-group">
<label>First Name</label>
<input type="text" class="form-control" v-model="name.firstName" required>
</div>
<div class="form-group">
<label>Last Name</label>
<input type="text" class="form-control" v-model="name.lastName" required>
</div>
<button class="btn btn-danger mt-3">Create</button>
</form>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
name: {
firstName: '',
lastName: ''
}
}
},
methods: {
handleSubmitForm() {
let apiURL = 'http://localhost:3000/name';
axios.post(apiURL, this.student).then(() => {
console.log(res)
}).catch(error => {
console.log(error)
});
}
}
}
</script>
My 1st inserted data: [{"firstName":"ABC", "lastName": "DCF"}]
I want to insert the 2nd one with data : firstName = "ABC", but last name = "XYZ". The error for validation occured because of validation (Unique) set in the defined schema from back-end).
My expected output:
Is there any possible ways to do:
If I insert the 1st data, it will work.
If I insert the 2nd data same as the 1st data, the error for validation will occur.
If I insert the 3rd data that is different from lastName only, it will work.
Thank you so much.

First of all, Mongo and Vue are completly separated, there is no relation.
Answering your question, the validation fail because you are telling mongo "Set firstName and lastName as unique fields". But not "Set these fields unique TOGETHER". So when you add a firstname, the second attemp try to add an existing firstName and fails.
So you can create a unique index:
informationSchema.index({ firstName: 1, lastName: 1 }, { unique: true });
And remove unique constraint in the schema.

Related

How to prefill data in vue form?

I am using vue form to edit one item, but I cannot conclude in which method should I assign a values to categoryName and description to make those values ​​appear in form?
I tried to make axios call and assign response to categoryName and description in the methods: created, mounted, beforeMouned, but it does not show values of categoryName and description in input fields, although values are obtained from the backend. Also v-model.lazy does not give results.
This is my code:
<template>
<div class="pt-5">
<form #submit.prevent="submitCategory">
<div class="form-group">
<label for="categoryName">Category name</label>
<input v-model.lazy="categoryName" value="categoryName" type="text" class="form-control" id="categoryName" aria-describedby="categoryNameHelp" placeholder="Enter category name">
</div>
<br>
<div class="form-group">
<label for="description">Description</label>
<input v-model="description" type="text" class="form-control" id="description" placeholder="Enter description">
</div>
<br>
<button type="submit" class="btn btn-primary mt-2">Submit</button>
</form>
</div>
</template>
<script>
export default {
name: "EditCategory",
data() {
return {
categoryName: '',
description: '',
}
},
beforeMount () {
this.$axios.get(`/api/categories/${this.$route.params.id}`,{
id: this.$route.params.id
}).then((response) => {
console.log(response.data)
this.categoryName = response.categoryName;
this.description = response.description;
}); },
methods: {
submitCategory() {
this.$axios.post('/api/categories', {
categoryName: this.categoryName,
description: this.description,
}).then(response => {
console.log(response)
this.$router.push({name: 'Categories'});
})
},
initialize () {
},
},
}
</script>
<style scoped>
</style>
Axios Response object
When we send a request to a server, it returns a response. The Axios response object consists of:
data - the payload returned from the server
status - the HTTP code returned from the server
headers - headers sent by server
You did console.log(response.data) in BeforeMount Hook. You will get your required data under response.data object
this.categoryName = response.data.categoryName;
this.description = response.data.description;

How to use array of objects for controls in Reactive Forms

I need to dynamic create textarea for forms. I have the following model:
this.fields = {
isRequired: true,
type: {
options: [
{
label: 'Option 1',
value: '1'
},
{
label: 'Option 2',
value: '2'
}
]
}
};
And form:
this.userForm = this.fb.group({
isRequired: [this.fields.isRequired, Validators.required],
//... here a lot of other controls
type: this.fb.group({
options: this.fb.array(this.fields.type.options),
})
});
Part of template:
<div formGroupName="type">
<div formArrayName="options">
<div *ngFor="let option of userForm.controls.type.controls.options.controls; let i=index">
<textarea [formControlName]="i"></textarea>
</div>
</div>
</div>
So, as you can see I have an array of objects and I want to use label property to show it in a textarea. Now I see [object Object]. If I change options to a simple string array, like: ['Option 1', 'Option 2'], then all works fine. But I need to use objects. So, instead of:
<textarea [formControlName]="i"></textarea>
I have tried:
<textarea [formControlName]="option[i].label"></textarea>
But, it doesn't work.
How can I use an array of objects?
This is Plunkr
You need to add a FormGroup, which contains your label and value. This also means that the object created by the form, is of the same build as your fields object.
ngOnInit() {
// build form
this.userForm = this.fb.group({
type: this.fb.group({
options: this.fb.array([]) // create empty form array
})
});
// patch the values from your object
this.patch();
}
After that we patch the value with the method called in your OnInit:
patch() {
const control = <FormArray>this.userForm.get('type.options');
this.fields.type.options.forEach(x => {
control.push(this.patchValues(x.label, x.value))
});
}
// assign the values
patchValues(label, value) {
return this.fb.group({
label: [label],
value: [value]
})
}
Finally, here is a
Demo
The answer from AJT_82 was so useful to me, I thought I would share how I reused his code and built a similar example - one that might have a more common use case, which is inviting several people to sign-up at once. Like this:
I thought this might help others, so that's why I am adding it here.
You can see the form is a simple array of text inputs for emails, with a custom validator loaded on each one. You can see the JSON structure in the screenshot - see the pre line in the template (thanks to AJT), a very useful idea whilst developing to see if your model and controls are wired up!
So first, declare the objects we need. Note that 3 empty strings are the model data (which we will bind to the text inputs):
public form: FormGroup;
private control: FormArray;
private emailsModel = { emails: ['','','']} // the model, ready to hold the emails
private fb : FormBuilder;
The constructor is clean (for easier testing, just inject my userService to send the form data to after submit):
constructor(
private _userService: UserService,
) {}
The form is built in the init method, including storing a reference to the emailsArray control itself so we can check later whether its children (the actual inputs) are touched and if so, do they have errors:
ngOnInit() {
this.fb = new FormBuilder;
this.form = this.fb.group({
emailsArray: this.fb.array([])
});
this.control = <FormArray>this.form.controls['emailsArray'];
this.patch();
}
private patch(): void {
// iterate the object model and extra values, binding them to the controls
this.emailsModel.emails.forEach((item) => {
this.control.push(this.patchValues(item));
})
}
This is what builds each input control (of type AbstracControl) with the validator:
private patchValues(item): AbstractControl {
return this.fb.group({
email: [item, Validators.compose([emailValidator])]
})
}
The 2 helper methods to check if the input was touched and if the validator raised an error (see the template to see how they are used - notice I pass the index value of the array from the *ngFor in the template):
private hasError(i):boolean {
// const control = <FormArray>this.form.controls['emailsArray'];
return this.control.controls[i].get('email').hasError('invalidEmail');
}
private isTouched(i):boolean {
// const control = <FormArray>this.form.controls['emailsArray'];
return this.control.controls[i].get('email').touched;
}
Here's the validator:
export function emailValidator(control: FormControl): { [key: string]: any } {
var emailRegexp = /[a-z0-9._%+-]+#[a-z0-9.-]+\.[a-z]{2,3}$/;
if (control.value && !emailRegexp.test(control.value)) {
return { invalidEmail: true };
}
}
And the template:
<form [formGroup]="form" (ngSubmit)="onSubmit(form.value)" class="text-left">
<div formArrayName="emailsArray">
<div *ngFor="let child of form.controls.emailsArray.controls; let i=index">
<div class="form-group" formGroupName="{{i}}">
<input formControlName="email"
class="form-control checking-field"
placeholder="Email" type="text">
<span class="help-block" *ngIf="isTouched(i)">
<span class="text-danger"
*ngIf="hasError(i)">Invalid email address
</span>
</span>
</div>
</div>
</div>
<pre>{{form.value | json }}</pre>
<div class="form-group text-center">
<button class="btn btn-main btn-block" type="submit">INVITE</button>
</div>
</form>
For what it's worth, I had started with this awful mess - but if you look at the code below, you might more easily understand the code above!
public form: FormGroup;
public email1: AbstractControl;
public email2: AbstractControl;
public email3: AbstractControl;
public email4: AbstractControl;
public email5: AbstractControl;
constructor(
fb: FormBuilder
) {
this.form = fb.group({
'email1': ['', Validators.compose([emailValidator])],
'email2': ['', Validators.compose([emailValidator])],
'email3': ['', Validators.compose([emailValidator])],
'email4': ['', Validators.compose([emailValidator])],
'email5': ['', Validators.compose([emailValidator])],
});
this.email1 = this.form.controls['email1'];
this.email2 = this.form.controls['email2'];
this.email3 = this.form.controls['email3'];
this.email4 = this.form.controls['email4'];
this.email5 = this.form.controls['email5'];
}
and the above used 5 of these divs in the template - not very DRY!
<div class="form-group">
<input [formControl]="email1" class="form-control checking-field" placeholder="Email" type="text">
<span class="help-block" *ngIf="form.get('email1').touched">
<span class="text-danger" *ngIf="form.get('email1').hasError('invalidEmail')">Invalid email address</span>
</span>
</div>
I guess it's not possible with FormControlName.
You could use ngModel .. take a look at your modified plunker:
http://plnkr.co/edit/0DXSIUY22D6Qlvv0HF0D?p=preview
#Component({
selector: 'my-app',
template: `
<hr>
<form [formGroup]="userForm" (ngSubmit)="submit(userForm.value)">
<input type="checkbox" formControlName="isRequired"> Required Field
<div formGroupName="type">
<div formArrayName="options">
<div *ngFor="let option of userForm.controls.type.controls.options.controls; let i=index">
<label>{{ option.value.label }}</label><br />
<!-- change your textarea -->
<textarea [name]="i" [(ngModel)]="option.value.value" [ngModelOptions]="{standalone: true}" ></textarea>
</div>
</div>
</div>
<button type="submit">Submit</button>
</form>
<br>
<pre>{{userForm.value | json }}</pre>
`,
})
export class App {
name:string;
userForm: FormGroup;
fields:any;
ngOnInit() {
this.fields = {
isRequired: true,
type: {
options: [
{
label: 'Option 1',
value: '1'
},
{
label: 'Option 2',
value: '2'
}
]
}
};
this.userForm = this.fb.group({
isRequired: [this.fields.isRequired, Validators.required],
//... here a lot of other controls
type: this.fb.group({
// .. added map-function
options: this.fb.array(this.fields.type.options.map(o => new FormControl(o))),
})
});
}
submit(value) {
console.log(value);
}
constructor(private fb: FormBuilder) { }
addNumber() {
const control = <FormArray>this.userForm.controls['numbers'];
control.push(new FormControl())
}
}
You may try this
Typescript:
ngOnInit(): void {
this.user = this.fb.group({
Title: ['1'],
FirstName: ['', Validators.required],
LastName: ['', Validators.required],
ContactNumbers: this.fb.array([
this.fb.group({
PhoneNumber: ['', [Validators.required]],
IsPrimary: [true],
ContactTypeId: [1]
})
]),
Emails: this.fb.array([
this.fb.group({
Email: ['', [Validators.required, Validators.email]],
IsPrimary: [true]
})
]),
Address: this.fb.group({
Address1: ['', Validators.required],
Address2: [''],
Town: [''],
State: ['UP'],
Country: [{ value: 'India', disabled: true }],
Zip: ['', Validators.required]
})
});
}
get Emails() {
return this.landlord.get('Emails') as FormArray;
}
Add and remove
addMoreEmail(index: number) {
if (index == 0) {
this.Emails.push(this.fb.group({ Email: ['', [Validators.required, Validators.email]], IsPrimary: [false] }));
} else {
this.Emails.removeAt(this.Emails.value.indexOf(index));
}
}
HTML
<form [formGroup]="user"
<div formArrayName="Emails">
<div *ngFor="let email of Emails.controls; let i=index">
<div class="row" [formGroup]="email">
<div class="col-sm-10">
<div class="form-group">
<label for="i" class="label">Email</label>
<input type="email" nbInput fullWidth id="i" placeholder="Email"
formControlName="Email" required>
</div>
</div>
<div class="col-sm-2 position-relative">
<nb-icon icon="{{i==0?'plus':'minus'}}-round" pack="ion"
(click)="addMoreEmail(i)">
</nb-icon>
</div>
</div>
</div>
</div></div>

React updating state in two input fields from form submission

I am trying to make a simple contact form using React. Eventually I will send the data collected from the state to a database, but for right now I am trying to just get it to console log the correct values.
Right now, the email field overrides the name field and when I console log both states, name shows up and email is undefined. Here is my React Component
import React, { Component, PropTypes } from 'react';
import ContactData from '../data/ContactData.js';
class FormContact extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
email: '',
textArea: ''
}
}
handleChange(event) {
event.preventDefault();
this.setState({
name: event.target.value,
email: event.target.email
})
}
handleSubmit(event) {
event.preventDefault();
console.log(this.state.name + ' ' + this.state.email);
}
render() {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<label> Name:
<input type="text" placeholder="Name" value={this.state.name} onChange={this.handleChange.bind(this)} />
</label><br />
<label> Email:
<input type="text" placeholder="Email" value={this.state.email} onChange={this.handleChange.bind(this)}/>
</label><br />
<input className="btn btn-primary" type="submit" value="Submit" />
</form>
)
}
}
FormContact.PropTypes = {
subName: PropTypes.string,
subEmail: PropTypes.string
}
FormContact.defaultProps = {
subName: 'Sam',
subEmail: ''
}
class Contact extends Component {
render() {
return (
<div>
<h1>CONTACT PAGE</h1>
<FormContact />
</div>
)
}
}
export default Contact;
If I understand what you want, you could do it as follows :
Add an empty object in your state for the form values
formValues: {}
Add the name attribute to your fields
<input name="name" .... />
<input name="email" .... />
then depending on that name update your state in handleChange function
let formValues = this.state.formValues;
let name = event.target.name; // Field name
let value = event.target.value; // Field value
formValues[name] = value;
this.setState({formValues})
And if the values go one level deeper, you could use
value={this.state.formValues["name"]} instead of value={this.state.name} - where name is the value of the name attribute of your input field
Thus, everything together should be as follows :
class Test extends React.Component {
constructor(props) {
super(props)
this.state = {
formValues: {}
}
}
handleChange(event) {
event.preventDefault();
let formValues = this.state.formValues;
let name = event.target.name;
let value = event.target.value;
formValues[name] = value;
this.setState({formValues})
}
handleSubmit(event) {
event.preventDefault();
console.log(this.state.formValues);
}
render(){
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<label> Name:
<input type="text" name="name" placeholder="Name" value={this.state.formValues["name"]} onChange={this.handleChange.bind(this)} />
</label><br />
<label> Email:
<input type="text" name="email" placeholder="Email" value={this.state.formValues["email"]} onChange={this.handleChange.bind(this)}/>
</label><br />
<input className="btn btn-primary" type="submit" value="Submit" />
</form>
)
}
}
React.render(<Test />, document.getElementById('container'));
Here is fiddle.
Hope this helps.
The reference to event.target.email does not exist on the event element. The value of a text input from an inline-event handler would be event.target.value for both email and name. The quick solution is to create a separate handler for each input:
handleChangeName(event) {
event.preventDefault();
this.setState({ name: event.target.value }); //<-- both use the same reference
} // to get the contextual value
handleChangeEmail(event) { // from the inputs v
event.preventDefault(); // |
this.setState({ email: event.target.value }); //<--------------------
}

Show current date in correct format in angular 2

I want to show current date in correct format
Example is: 12/19/2016.
Now i am using Date.now(). But it show garbage value, I want to show only date.
I am also using pipes but pipes are not bind with database. Now i am using formcontrol, which get the current date using Date.now(). and later i must show this date in grid in html.
My current code is.
Date.js file:
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
PurchaseOrderSchema = new Schema({
PurchaseOrderNo: {
type: Number,
required: true,
index: {
unique: true
}
},
Status:String,
OrderDate: {
type: Date,
"default": Date.now()
}
}
{ collection: 'PurchaseOrder' });
PurchaseOrderSchema.set('toObject', { getters: true });
PurchaseOrderSchema.set('toJSON', { getters: true });
PurchaseOrderSchema.virtual('locationname').get(function() {
return this.Locations[0].ParentId.LocationName;
});
PurchaseOrderSchema.virtual('Tempvendor').get(function() {
return this.Vendors[0].ParentId.VendorName;
});
PurchaseOrderSchema.statics.delete_by_name = function(name, cb_succ, cb_fail) {};
var PurchaseOrder = mongoose.model('PurchaseOrder', PurchaseOrderSchema);
module.exports = PurchaseOrder;
newpurchaseorder.ts file:
export class NewPurchaseOrderComponent implements OnInit {
private PurchaseOrderNo = new FormControl("", Validators.required);
private OrderDate = new FormControl("");
this.OrderDate=Date.now();
ngOnInit() {
this.addClassForm = this.formBuilder.group({
PurchaseOrderNo:this.PurchaseOrderNo,
OrderDate: this.OrderDate,
});
}
}
newpurchageorder.html file:
<section class="page-form-ele page">
<section class="panel panel-default">
<div class="panel-heading"><span class="glyphicon glyphicon-th"></span>Purchase Order</div>
<div class="panel-body" data-ng-controller="TabsDemoCtrl">
<div class="row">
<div class="col-sm-12">
<div class="heading-container">
<div class="row">
<div class="col-sm-6 margin-top-13">
<h3 class="h3-color">New Purchase Order</h3>
</div>
<div class="col-sm-6" align="right">
<form class="form-horizontal margin-bottom-0 margin-top-6" [formGroup]="addClassForm" (ngSubmit)="submitAdd()" role="form">
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">Date:</label>
<div class="col-sm-4">
<input type="number" class="form-control" [(ngModel)]="OrderDate" id="orderdate" placeholder="Order Date..." formControlName="OrderDate" required readonly="true" >
</div>
</div>
</div>
</div>
</form>
</section>
the server side will usually generate a json response with a ISO8601 date 2016-12-19 or a unix timestamp 1482140260
When this gets sent over the wire to angular it's not a date. It's either a string or a number which needs converting to a javascript date.
This is because the json standard doesn't automatically deserialize dates.
You will need to do this manually.
this.http.get('/api/mydata')
// Convert to json first
.map(r => r.json())
// Convert the items dates to javascript dates
.map(items => {
// Loop over the javascript array and convert the dates...
items.forEach(i => {
// before parse
// i.date = '2016-12-19';
i.date = new Date(i.date);
// i.date now is a correct javascript Date object
});
return items;
})

Create User with Avatar in Sails.js fails when Avatar is an image/png

I generated & implemented a new api called User using the sails generate
command. When I try to uploading an svg or jpg image, it works perfectly, the file is uploaded and the user is created in the DB. However, when I try using a png file instead, the saving to the DB fails! Although the png file would be uploaded as expected, the user wouldn't be saved. When debugging, the Object Param is empty except for the field "company".
Generating the api
$ sails generate api user
User Controller (api/controller/UserController.js)
'create': function (req, res, next) {
var params = req.params.all();
var format = req.param('format');
delete params['format'];
req.file('avatar').upload({
maxBytes: 10000000,
dirname: sails.config.appPath + '/assets/images/avatar/'
}, function (err, uploadedFiles) {
if (err) return res.negotiate(err);
console.log(uploadedFiles.length + ' file(s) uploaded successfully!');
console.log(uploadedFiles);
params["avatar"] = uploadedFiles[0].fd;
User.create(params, function userCreated(err, user) {
if (err) {
req.session.flash = {
err: err
}
return res.redirect("/user/new");
}
if (format == 'json') {
res.json(user);
} else {
res.redirect('user/show/' + user.id);
}
});
});
},
my views/user/new.ejs:
<form id="register-form" action="/user/create" method="post" enctype="multipart/form-data">
<input id="icon_prefix" type="text" name="company" class="validate">
<input name="avatar" type="file">
<input class="file-path validate" type="text">
<input id="icon_prefix" type="text" name="first_name" class="validate">
<input id="icon_prefix" type="text" name="last_name" class="validate">
<input id="icon_telephone" type="tel" name="phone"
<input id="icon_mail" type="email" name="email" class="validate" required="" aria-required="true">
<input name="type" value="Auftraggeber" type="radio" id="auftraggeber" checked/>
<input name="type" value="Auftragnehmer" type="radio" id="auftragnehmer" disabled/>
<input id="password" type="password" name="password" pattern=".{6,}" title="6 characters minimum"
<input id="confirmation" type="password" class="validate">
<input type="hidden" value="<%= _csrf %>">
<input type="checkbox" id="accept"/>
<input type="checkbox" id="not_robot"/>
<button class="btn-large waves-effect waves-light" type="submit">Submit
</button>
</form>
User model (api/models/User.js)
module.exports = {
schema: true,
attributes: {
avatar: {
type: "String"
},
first_name: {
type: "String",
required: true
},
last_name: {
type: "String",
required: true
},
type: {
type: "String",
enum: ['Auftraggeber', 'Auftragnehmer']
},
phone: {
type: "String",
unique: true
},
email: {
type: "String",
email: true,
unique: true,
required: true
},
password: {
type: "String",
required: true
},
company: {
type: "String",
required: true
},
toJSON: function () {
var obj = this.toObject();
delete obj.password;
return obj;
}
}
};
you can use skipper package to handle different types of images
run this command in terminal
npm install skipper --save
at the top of your controller app.use(require('skipper')());
now can use this package
req.file('image').upload({
// ...any other options here...
}, ...);
I searched over 2 weeks and found this:
https://github.com/balderdashy/sails/issues/2326
You don't have to install skipper and require('skipper') => Sails comes automatically with skipper! Also don't use it as a policy, it won't work ( didn't for me).
What you have to pay attention to for a multipart/form-data form with text input and file input, where you want to upload the image/file and also save the data (the file name) in the DB at once is: the order of your input fields in your html form matters! Place the text input fields on the top and the file input field at the bottom of your html form.