validate form inputs bootstrap4 - vue.js

I have this code in my vue template
<div class="form-row" :class="{'was-validated': this.checkPassword()}">
<div class="col-6">
<label>Password</label>
<input :type="showPassword ? 'text' : 'password'" class="form-control" ref="password" required v-model="password">
<div class="valid-feedback" v-if="!error">
Password match
</div>
<div class="invalid-feedback" v-else>
Password not match
</div>
</div>
<div class="col-6">
<label>Confirm password</label>
<div class="input-group">
<input :type="showPassword ? 'text' : 'password'" class="form-control" ref="passwordCheck" required v-model="passwordCheck" #change="checkPassword()">
<div class="input-group-append">
<button class="btn btn-secondary" #click.prevent="copyToClipboard()"><i class="fas fa-clipboard"></i></button>
</div>
</div>
</div>
</div>
I want to show like the bootstrap4 documentation a green input field if the password matches or a red one if the password aren't matching. I'm trying by adding the was-validated class to the form-row if the demanded method return true but when the view where the password inputs are rendered the two input fields are always red. How I can fix this to give the correct feedback to the user?

Please always share all the relevant parts of the component otherwise, it's hard to tell where the problem resides. Here, you haven't shared your <script> section. Anyway, I guess this should put you on the right track.
<template>
<div class="form-row" :class="{'was-validated': this.checkPassword()}">
<div class="col-6">
<label>Password</label>
<label>
<input :type="showPassword ? 'text' : 'password'" class="form-control" :class="getPasswordClass()"
ref="password" required v-model="password">
</label>
</div>
<div class="col-6">
<label>Confirm password</label>
<div class="input-group">
<label>
<input :type="showPassword ? 'text' : 'password'" class="form-control" :class="getPasswordClass()"
ref="passwordCheck" required v-model="passwordCheck" #input="checkPassword()">
</label>
<div class="input-group-append">
<button class="btn btn-secondary" #click.prevent="copyToClipboard()"><i
class="fas fa-clipboard"></i></button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
password: null,
passwordCheck: null,
error: true,
showPassword: true,
}
},
methods: {
checkPassword() {
this.error = this.password !== this.passwordCheck;
},
getPasswordClass() {
if (!this.password || !this.passwordCheck) {
return '';
}
return this.error ? 'is-invalid' : 'is-valid'
},
copyToClipboard() {
//
}
}
}
</script>
So, in my opinion, you should only set the dynamic class (is-valid or is-invalid) when both inputs are provided. In this example I've added that to both password and passwordCheck fields but I think it's enough to just apply it to the passwordCheck because that's the one checked against the password.
If you only want to check after user leaves the field you could adjust the code like this:
In the template remove:
:class="{'was-validated': this.checkPassword()}"
And update:
#blur="checkPassword()
In the data() add:
blurred: false,
In the methods update:
methods: {
checkPassword() {
this.blurred = true;
this.error = this.password !== this.passwordCheck;
},
getPasswordClass() {
if (!this.blurred) {
return '';
}
return this.error ? 'is-invalid' : 'is-valid'
},
...
}

Related

Laravel + vue: Error message only showing the first letter of the sentence

I'm just starting to learn laravel+vue. I was able to follow a tutorial from this yt: https://www.youtube.com/watch?v=JZDmBWRPWlw. Though it seems outdated, I was still able to follow his steps. I'm using the laravel-mix 6.0.6 and vue 2.6.12.
Using inspect element>network, I can see that I'm throwing the correct error message in array.
{"component":"Users\/Create","props":{"app":{"name":"Laravel"},"errors":{"name":"The name field is required.","email":"The email field is required."}},"url":"\/users\/create","version":"207fd484b7c2ceeff7800b8c8a11b3b6"}
But somehow it is not displaying the complete error message. Right now it just show the first letter of the sentence. LOL. Sample error message is: The email field is required and it will just display the letter "T". Below is my Create.vue. Basically it is just a user create form with simple validation.
Create.vue
<template>
<layout>
<div class="container">
<div class="col-md-6">
<div v-if="Object.keys(errors).length > 0" class="alert alert-danger mt-4">
{{ errors[Object.keys(errors)[0]][0] }}
</div>
<form action="/users" method="POST" class="my-5" #submit.prevent="createUser">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name" placeholder="Name" v-model="form.name">
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="text" class="form-control" id="email" placeholder="Email" v-model="form.email">
</div>
<div class="form-group">
<label for="name">Password</label>
<input type="password" class="form-control" id="password" placeholder="Password" v-model="form.password">
</div>
<button type="submit" class="btn btn-primary">Create User</button>
</form>
</div>
</div>
</layout>
</template>
<script>
import Layout from '../../Shared/Layout'
export default {
props: ['errors'],
components: {
Layout,
},
data() {
return {
form: {
name: '',
email: '',
password: '',
}
}
},
methods: {
createUser() {
this.$inertia.post('/users', this.form)
.then(() => {
// code
})
}
}
}
</script>
Edit:
I have this error on my console
[Vue warn]: Error in v-on handler: "TypeError: Cannot read property
'then' of undefined"
found in
---> at resources/js/Pages/Users/Create.vue
Your error call is probably getting only the first letter due to [0]. Try to change to:
{{ errors[Object.keys(errors)[0]] }}
Strings can also be read as arrays. If you do this:
$a = "TEST";
echo $a[0];
That would print only T.
That is probably the problem.

Method Updating Data Twice on Form Submission

I have a component that is a form that I input a name and it updates a value on a field on the backend based on which input the name is. Right now I have two inputs (host and scout) and they work fine if I just fill one input. My problem is, when I fill both inputs, the name on the host input will always get updated twice while the name on the scout field will work just fine. Not sure if I was clear enough.
Here is the code for the component so far
<template>
<div class="add-wave">
<h3>Add Wave</h3>
<div class="row">
<form #click.prevent="addwave()" class="col s12">
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="host" />
<label class="active">Host</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input type="text" v-model="scout" />
<label class="active">Scout</label>
</div>
</div>
<button type="submit" class="btn">Submit</button>
<router-link to="/member" class="btn grey">Cancel</router-link>
</form>
</div>
</div>
</template>
<script>
import { db, fv } from "../data/firebaseInit";
export default {
data() {
return {
host: null,
scout: null
};
},
methods: {
addwave() {
this.addhost();
db.collection("members")
.where("name", "==", this.scout)
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
doc.ref.update({
scout: fv.increment(1),
total: fv.increment(1)
});
});
});
},
addhost() {
db.collection("members")
.where("name", "==", this.host)
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
doc.ref.update({
host: fv.increment(1),
total: fv.increment(1)
});
});
});
}
}
};
</script>
I'm not sure why is it updating twice only when I fill both input fields.

Add invalid class to form-group using VueValidate to bootstrap CSS

How to add invalid class to form-group if the validation fails on input. By default VueValidate adds to the input.
<div class="form-group">
<label for="mobile" v-bind:class="{'text-danger': errors.has('mobile')}">Mobile</label>
<input type="text" v-validate="validation.mobile" class="form-control" v-model="user.mobile" name="mobile" id="mobile" />
<span class="invalid-feedback">{{ errors.first('mobile') }}</span>
</div>
Currently i am using v-bind:class="{'text-danger': errors.has('mobile')}" on the label and i get red colored label on field error.
If i could add invalid to form-group, it would be better to control with css. Below is my VueValidate Settings
Vue.use(VeeValidate, {
aria: true,
classes: true,
classNames: {
invalid: 'is-invalid'
}
});
You can bind a computed function to check errors and return the div's classes
{
computed: {
formGroupClass: function () {
if (this.error.has('mobile') ){
return ['form-group', 'invalid']
}
return ['form-group']
}
}
}
<div :class="formGroupClass">
<label for="mobile" v-bind:class="{'text-danger': errors.has('mobile')}">Mobile</label>
<input type="text" v-validate="validation.mobile" class="form-control" v-model="user.mobile" name="mobile" id="mobile" />
<span class="invalid-feedback">{{ errors.first('mobile') }}</span>
</div>

How to add custom validator to reactive forms in Angular5

I have the following passwordvalidator which I don't know how to attach into the html. The way I am invoking it now it's not working loginForm.controls.password.errors.passwordValidator
See below in the actual code.
import { FormControl } from "#angular/forms";
export class PasswordValidator {
static getPasswordValidator() {
return function passwordValidator(control: FormControl): { [s: string]: boolean } {
// Write code here..
if (!control.value.match(/^((?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{6,})/)) {
return { invalidPassword: true };
}
return null;
}
}
}
Then this is how I am using it in login.ts
ngOnInit() {
this.loginForm = this.fb.group({
username: ['', [Validators.required, Validators.email]],
password: ['',
Validators.compose([
Validators.required,
PasswordValidator.getPasswordValidator()
]
)]
});
}
But can't find out how to add it to the formcontrol in login.html
<mat-form-field class="example-full-width">
<input id="password" formControlName="password" matInput placeholder="Password">
</mat-form-field>
<br>
<div *ngIf="loginForm.controls.password.invalid && (loginForm.controls.password.dirty || loginForm.controls.password.touched)"
class="alert alert-danger">
<div class="error mat-body-2" *ngIf="loginForm.controls.password.errors.required">
You must fill out your password
</div>
<div class="error mat-body-2" *ngIf="loginForm.controls.password.errors.passwordValidator && !loginForm.controls.password.errors.required">
Invalid email password
</div>
You should check if the key invalidPassword exist in errors of that controls or not like that
<mat-form-field class="example-full-width">
<input id="password" formControlName="password" matInput placeholder="Password">
</mat-form-field>
<br>
<div *ngIf="loginForm.controls.password.invalid && (loginForm.controls.password.dirty || loginForm.controls.password.touched)"
class="alert alert-danger">
<div class="error mat-body-2" *ngIf="loginForm.controls.password.errors.required">
You must fill out your password
</div>
<div class="error mat-body-2" *ngIf="loginForm.controls.password.errors.invalidPassword && !loginForm.controls.password.errors.required">
Invalid email password
</div>

Angular2: How to enable save button if any model value changed on edit page

I am new for angular 2. I have a page where we can edit details of customer profile. How to enable save button if any property of has been changed. I know it is possible in angular1 by using $watch.
It is simple. dirty check your form if you are using #angular/forms.
create form
export class HeroDetailComponent4 {
heroForm: FormGroup;
states = states;
constructor(private fb: FormBuilder) {
this.createForm();
}
createForm() {
this.heroForm = this.fb.group({
name: ['', Validators.required ],
street: '',
city: '',
state: '',
zip: '',
power: '',
sidekick: ''
});
}
}
HTML:
<h2>Hero Detail</h2>
<h3><i>A FormGroup with multiple FormControls</i></h3>
<form [formGroup]="heroForm" novalidate>
<button (click)="submit()" [disabled]="!heroForm.dirty" type="button">Submit</button>
<div class="form-group">
<label class="center-block">Name:
<input class="form-control" formControlName="name">
</label>
</div>
<div class="form-group">
<label class="center-block">Street:
<input class="form-control" formControlName="street">
</label>
</div>
<div class="form-group">
<label class="center-block">City:
<input class="form-control" formControlName="city">
</label>
</div>
<div class="form-group">
<label class="center-block">State:
<select class="form-control" formControlName="state">
<option *ngFor="let state of states" [value]="state">{{state}}</option>
</select>
</label>
</div>
<div class="form-group">
<label class="center-block">Zip Code:
<input class="form-control" formControlName="zip">
</label>
</div>
<div class="form-group radio">
<h4>Super power:</h4>
<label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>
<label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>
<label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>
</div>
<div class="checkbox">
<label class="center-block">
<input type="checkbox" formControlName="sidekick">I have a sidekick.
</label>
</div>
</form>
use heroForm.dirty to check whether form data is changed. it will set to true if any control inside heroForm has been changed.
<button (click)="submit()" [disabled]="!heroForm.dirty" type="button">Submit</button>
Refer angular docs for more info
you can use form control validation for it.
some thing like this in html template:
<form fxLayout="column" [formGroup]="form">
<mat-form-field class="mb-1">
<input matInput [(ngModel)]="userProfileChangeModel.firstName" placeholder="نام"
[formControl]="form1.controls['fname']">
<small *ngIf="form1.controls['fname'].hasError('required') && form1.controls['fname'].touched"
class="mat-text-warn">لطفا نام را وارد نمایید.
</small>
<small *ngIf="form1.controls['fname'].hasError('minlength') && form1.controls['fname'].touched"
class="mat-text-warn">نام باید حداقل 2 کاراکتر باشد.
</small>
<small *ngIf="form1.controls['fname'].hasError('pattern') && form1.controls['fname'].touched"
class="mat-text-warn">لطفا از حروف فارسی استفاده نمائید.
</small>
</mat-form-field>
<mat-card-actions>
<button mat-raised-button (click)="editUser()" color="primary" [disabled]="!form1.valid" type="submit">
ذخیره
</button>
</mat-card-actions>
</form>
and like this in ts file:
this.form = this.bf.group({
fname: [null, Validators.compose([
Validators.required,
Validators.minLength(2),
Validators.maxLength(20),
Validators.pattern('^[\u0600-\u06FF, \u0590-\u05FF]*$')])],
});
if:
[disabled]="!form1.valid"
is valid save button will be active
bast regards.
You can use disabled option like below :
<button [disabled]="isInvalid()" type="button" (click) = "searchClick()" class="button is-info">
<span class="icon is-small">
<i class="fa fa-search" aria-hidden="true"></i>
</span>
<span>Search</span>
</button>
you can create isInvalid() in your ts file and check if that property is empty or not and return that boolean value
and for hide button on a state you can use *ngIf in line directive.
This worked for me, pls try.
In your html,
<input type="text" [ngModel]="name" (ngModelChange)="changeVal()" >
<input type="text" [ngModel]="address" (ngModelChange)="changeVal()" >
<input type="text" [ngModel]="postcode" (ngModelChange)="changeVal()" >
<button [disabled]="noChangeYet" (click)="clicked()" >
<span>SUBMIT</span>
</button>
In your component
export class customer implements OnInit {
name: string;
address: string;
postcode: string;
noChangeYet:boolean = true;
constructor() {}
changeVal(){ // triggers on change of any field(s)
this.noChangeYet = false;
}
clicked(){
// your function data after click (if any)
}
}
Hope this is what you need.
Finally I resolved this issue.
import { Component, Input, Output, OnInit, AfterViewInit, EventEmitter, ViewChild } from '#angular/core';
#Component({
selector: 'subscribe-modification',
templateUrl: './subscribe.component.html'
})
export class SampleModifyComponent implements OnInit, AfterViewInit {
disableSaveSampleButton: boolean = true;
#ViewChild('sampleModifyForm') sampleForm;
ngAfterViewInit() {
setTimeout(() => {
this.sampleForm.control.valueChanges.subscribe(values => this.enableSaveSampleButton());
}, 1000);
}
enableSaveSampleButton() {
console.log('change');
this.disableSaveSampleButton = false;
}
}
HTML
<button id="btnSave" class="btn btn-primary" (click)="save()" />