Angular8 Reactive Form Validators Together With Async Validator - angular-reactive-forms

I have an error when i try to show an error in the DOM when validating validators & async validators
My error:
ERROR TypeError: Cannot read property 'required' of null at
Object.eval [as updateDirectives]
In component html:
<div class="form-group">
<label for="email">email</label>
<input
type="text"
id="email"
formControlName="email"
class="form-control">
<div *ngIf="!signupForm.get('userData.email').valid && signupForm.get('userData.email').touched ">
<span class="help-block"
*ngIf="signupForm.get('userData.email').errors['required'] && signupForm.get('userData.email').touched">Email
is required
</span>
<span class="help-block"
*ngIf="signupForm.get('userData.email').errors['email'] && signupForm.get('userData.email').touched">Email
is not valid
</span>
</div>
In component.ts :
ngOnInit() {
this.signupForm = new FormGroup({
'userData': new FormGroup({
'username': new FormControl(null, [Validators.required, this.forbiddenNames.bind(this)]),
'email': new FormControl(null, [Validators.required, Validators.email], this.forbiddenEmails),
}),
'gender': new FormControl('male'),
'hobbies': new FormArray([])
});
}
Note: when i remove the async validator, (this.forbiddenEmails) from the form control async validator, it works:
'email': new FormControl(null, [Validators.required, Validators.email], this.forbiddenEmails)
(Works when i have only validators without async validators
i get no error, but i want to show the error for the async validator as well .)

I fixed this by using "hasError('errorName')":
<div class="form-group">
<label for="email">email</label>
<input
type="text"
id="email"
formControlName="email"
class="form-control">
<div *ngIf="!signupForm.get('userData.email').valid && signupForm.get('userData.email').touched ">
<span class="help-block"
*ngIf="signupForm.get('userData.email').hasError('required') && signupForm.get('userData.email').touched">Email
is required
</span>
<span class="help-block"
*ngIf="signupForm.get('userData.email').hasError['email'] && signupForm.get('userData.email').touched">Email
is not valid
</span>
Email
is not valid

Related

Laravel inertia multi-part form data

I want to create a membership form with picture upload and other input data required. I have a form with input file and input text which to be considered as multi-part data. I am using the Laravel 8 and Inertia.js.
Here is what I've tried:
In my Form, I have like this:
<form action="/member" method="POST" #submit.prevent="createMember">
<div class="card-body">
<div class="form-group">
<label for="exampleInputFile">Picture</label>
<div class="input-group">
<div class="custom-file">
<input type="file" class="custom-file-input" id="exampleInputFile" #input="form.picture">
<label class="custom-file-label" for="exampleInputFile">Choose image</label>
</div>
<div class="input-group-append">
<span class="input-group-text">Upload</span>
</div>
</div>
</div>
<div class="form-group">
<label for="exampleInputEmail1">First name</label>
<input type="text" class="form-control" id="fname" placeholder="First name" v-model="form.firstname">
</div>
<div class="form-group">
<label for="exampleInputEmail1">Middle name</label>
<input type="text" class="form-control" id="mname" placeholder="Middle name" v-model="form.middlename">
</div>
<div class="form-group">
<label for="exampleInputEmail1">Last name</label>
<input type="text" class="form-control" id="lname" placeholder="Last name" v-model="form.lastname">
</div>
<div class="col-md-12">
</div>
<div class="form-group">
<label for="exampleInputEmail1">Name ext</label>
<input type="text" class="form-control" id="next" placeholder="Name extension" v-model="form.name_ext">
</div>
<div class="form-group">
<label>Membership Date:</label>
<div class="input-group date" id="reservationdate" data-target-input="nearest">
<input type="text" class="form-control datetimepicker-input" id="mem_date" data-target="#reservationdate" v-model="form.membership_date"/>
<div class="input-group-append" data-target="#reservationdate" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
</div>
</div>
</div>
<div class="form-group">
<label>Date of Birth:</label>
<div class="input-group date" id="reservationdate" data-target-input="nearest">
<input type="text" class="form-control datetimepicker-input" id="date_of_birth" data-target="#reservationdate" v-model="form.date_of_birth"/>
<div class="input-group-append" data-target="#reservationdate" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
</div>
</div>
</div>
<div class="form-group">
<label>Sex:</label>
<div class="radio-inline">
<label class="radio radio-solid">
<input type="radio" name="travel_radio" class="details-input" value="Male" v-model="form.sex"/> Male
<span></span>
</label>
<label class="radio radio-solid">
<input type="radio" name="travel_radio" class="details-input" value="Female" v-model="form.sex"/> Female
<span></span>
</label>
</div>
</div>
</div>
<!-- /.card-body -->
<div class="card-footer">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
then I have tried vue and inertia implementation like this:
export default {
props: [],
components: {
AppLayout,
},
data() {
return {
form: {
picture: '',
firstname: '',
middlename: '',
lastname: '',
name_ext: '',
date_of_birth: '',
sex: '',
membership_date: '',
}
}
},
methods: {
createMember() {
this.$inertia.post('/member', this.form, {
_method: 'put',
picture: this.form.picture,
onSuccess: () => {
console.log('success');
// $('.toastrDefaultSuccess').click(function() {
// toastr.success('Successfully Added');
// });
},
onError: (errors) => {
console.log(errors);
},
})
}
}
}
then from my backend, I tried to dd the request and got null from picture:
public function store(MemberPost $request) {
dd($request);
}
Assuming you are using inertia 0.8.0 or above, you can use the inertia form helper. This will automatically transform the data to a FormData object if necessary.
Change your data method to:
data() {
return {
form: this.$inertia.form({
picture: '',
firstname: '',
middlename: '',
lastname: '',
name_ext: '',
date_of_birth: '',
sex: '',
membership_date: '',
})
}
}
and the createMember function to:
createMember() {
// abbreviated for clarity
this.form.post('/member')
}
More info: docs. If you need to migrate FormData from inertia < 0.8.0 to a current version, see this page.
if you using multi-upload remove array from files
before: <input type="file" #input="form.image = $event.target.files[0]" multiple/>
after: <input type="file" #input="form.image = $event.target.files" multiple/>
add enctype="multipart/form-data" to form tag
and you need move the picture to directory. example
$name = $file->getClientOriginalName();
$file->move('uploads/posts/',$name);
sorry, i dont know how to implement this code to inertia.
This way works for me
this.$inertia.post('/member', this.form)

Why i am getting 'No Access-Control-Allow-Origin'

I am getting this error "Access to XMLHttpRequest at 'http://localhost/api/auth/register' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource."
<template>
<div class="container mt-2">
<form autocomplete="off" #submit.prevent="register" method="post">
<div class="form-group" v-bind:class="{ 'has-error': has_error && errors.email }">
<label for="email">E-mail</label>
<input type="email" id="email" class="form-control bg-light border-0 small" placeholder="user#example.com" v-model="email">
<span class="help-block" v-if="has_error && errors.email">{{ errors.email }}</span>
</div>
<div class="form-group" v-bind:class="{ 'has-error': has_error && errors.password }">
<label for="password">Password</label>
<input type="password" id="password" class="form-control bg-light border-0 small" v-model="password">
<span class="help-block" v-if="has_error && errors.password">{{ errors.password }}</span>
</div>
<div class="form-group" v-bind:class="{ 'has-error': has_error && errors.password }">
<label for="password_confirmation">Conform Password</label>
<input type="password" id="password_confirmation" class="form-control bg-light border-0 small" v-model="password_confirmation">
</div>
<div class="alert alert-danger" v-if="has_error && !success">
<p v-if="error == 'registration_validation_error'">Validation error</p>
<p v-else>Please fill all the fields to get registered</p>
</div>
<input type="hidden" name="_token" :value="csrf">
<div class="text-center pb-3">
<button type="submit" class="btn btn-success btn-sm" style="background-color:#00A7F5;border:none;">Register</button>
</div>
</form>
</div>
</template>
<script>
export default {
data() {
return {
email: '',
password: '',
password_confirmation: '',
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
has_error: false,
error: '',
errors: {},
success: false
}
},
methods: {
register() {
var app = this
this.$auth.register({
data: {
email: app.email,
password: app.password,
password_confirmation: app.password_confirmation
},
success: function () {
app.success = true
},
error: function (res) {
app.has_error = true
app.error = res.response.error
app.errors = res.response.errors || {}
}
})
}
}
}
</script>
if fails, response from controller
$a = Validator::make($request->all(), [
'email' => 'required|email|unique:users',
'password' => 'required|min:6|confirmed',
]);
if ($a->fails())
{
return response()->json([
'status' => 'error',
'errors' => $a->errors()
], 422);
}
if everything ok then
return response()->json(['status' => 'success'], 200);
Please let me know whats wrong with this and is there any better way to handle error. Please share link then, i am not able to handle error correctly.
All helps are appreciated.
CORS policy checks strictly on domain plus port. So make both the same will be the solution.
You need to add CORS Service Provider to your project, like this: https://github.com/barryvdh/laravel-cors

Cannot get updateOn 'blur' validation to work

I currently have a validation function to verify if the password and confirm password match. This seems to work just fine if I do not add updateOn blur, However when I add the blur it doesn't hit the match function which causes validation to not work. Not sure what I am doing wrong ?
Here is the function to check for matching passwords
```import { FormGroup } from '#angular/forms';
// custom validator to check that two fields match
export function MustMatch(controlName: string, matchingControlName: string) {
return (formGroup: FormGroup) => {
const control = formGroup.controls[controlName];
const matchingControl = formGroup.controls[matchingControlName];
if (matchingControl.errors && !matchingControl.errors.mustMatch) {
// return if another validator has already found an error on the matchingControl
return;
}
// set error on matchingControl if validation fails
if (control.value !== matchingControl.value) {
matchingControl.setErrors({ mustMatch: true });
} else {
matchingControl.setErrors(null);
}
};
}
```
Here is the HTML
``` <div class="card-block">
<p class="text-success" *ngIf="success">Your password has successfully been updated, you can now <a [routerLink]="['/login']">login</a> with your new password.</p>
<p class="text-danger" *ngIf="( resetPasswordForm.invalid && submitted)">Please complete all
required fields.</p>
<p class="text-danger" *ngIf="resetPasswordForm.controls['confirmPassword'].errors && resetPasswordForm.controls['confirmPassword'].errors.mustMatch">Passwords must match.</p>
<ng-container *ngIf="!success">
<p class="text-danger" *ngFor="let err of errorMessages">{{ err }}</p>
</ng-container>
<div class="form-group" [ngClass]="{'has-danger' :submitted && resetPasswordForm.controls.password.errors}">
<label class="form-control-label" for="password">New Password</label>
<input id="password" type="password" class="form-control" name="password" formControlName="password"
[ngClass]="{ 'is-invalid': submitted && resetPasswordForm.controls.password.errors}" required>
</div>
<div class="form-group" [ngClass]="{'has-danger' :submitted && resetPasswordForm.controls.confirmPassword.errors}">
<label class="form-control-label" for="confirmPassword">Confirm Password</label>
<input id="confirmPassword" type="password" class="form-control" name="confirmPassword" formControlName="confirmPassword"
[ngClass]="{ 'is-invalid': (submitted && resetPasswordForm.controls.confirmPassword.errors) ||
resetPasswordForm.controls['confirmPassword'].errors && resetPasswordForm.controls['confirmPassword'].error.mustMatch}" required>
</div>
</div>
<div class="card-footer">
<button type="submit" class="btn btn-primary" >Reset Password</button>
<small class="text-muted"> <a [routerLink]="['/login']">Back to Login</a></small>
</div>
</form>
</section>```
Here is the component code
``` private createForm() {
this.resetPasswordForm = this.fb.group({
password: ['', Validators.required],
confirmPassword: ['', Validators.required]
}, {
validator: MustMatch('password', 'confirmPassword'), updateOn: 'blur' // verify that passwords match
});
}```
I would expect to see the error message "Passwords must match." after the user moves off the confirm password input box if they do not match. If they do match never seeing the error message
Seems that the code above does work, Not sure why I things were not working correctly but after many changes and reverting back it seems to now work

Angular 2 - Validators are getting set to false too fast

I have 2 fields that get validated.
The issue is these fields are getting set with values by Chrome, such as the saved password and email.
So when the page loads the 2 fields (email + password) which have the right values as set via Chrome but the 2nd field is shown as invalid.
I am trying to detect changes after the page has loaded.
I have tried calling the changeDetectorRef from ngInit but it did not work.
When I click on the page the field then shows that it is valid.
Within the constructor:
this.form = fb.group({
'email': ['', Validators.compose([
Validators.required,
Validators.minLength(4),
EmailValidator.validate])],
'password': ['', Validators.compose([
Validators.required,
Validators.minLength(6)])]
});
this.email = this.form.controls['email'];
this.password = this.form.controls['password'];
I have tried:
ngOnInit() {
this.zone.run(() => {
this.email.updateValueAndValidity();
this.password.updateValueAndValidity();
});
}
But still no good.
I find it odd that the first field is shown as valid but the 2nd one is not.
It is a password field so that might have something to do with it?
Both fields are of type AbstractControl.
Here is the html:
<form [formGroup]="form" (ngSubmit)="onSubmit(form.value)" class="form-horizontal">
<div class="form-group row" [ngClass]="{'has-error': (!email.valid), 'has-success': (email.valid)}">
<label for="inputEmail3" class="col-sm-2 control-label">Email</label>
<div class="col-sm-10">
<input [formControl]="email" type="email" class="form-control" id="inputEmail3" placeholder="Email">
</div>
</div>
<div class="form-group row" [ngClass]="{'has-error': (!password.valid), 'has-success': (password.valid)}">
<label for="inputPassword3" class="col-sm-2 control-label">Password</label>
<div class="col-sm-10">
<input [formControl]="password" type="password" class="form-control" id="inputPassword3" placeholder="Password">
</div>
</div>
<div class="form-group row">
<div class="offset-sm-2 col-sm-10">
<button [disabled]="!form.valid" type="submit" class="btn btn-default btn-auth">Sign in</button>
</div>
</div>
</form>

stripe token is not passing

I have an application built in Laravel 4 and uses this package
I am following this tutorial
This is the error I am getting http://postimg.org/image/c4qwjysgp/
My issue is $token is not correctly passing or the $token is empty.
I have already done a var_dump($token); die(); and get nothing but a white screen so not data is passing.
Here is the view
#extends('layouts.main')
#section('content')
<h1>Your Order</h1>
<h2>{{ $download->name }}</h2>
<p>£{{ ($download->price/100) }}</p>
<form action="" method="POST" id="payment-form" role="form">
<input type="hidden" name="did" value="{{ $download->id }}" />
<div class="payment-errors alert alert-danger" style="display:none;"></div>
<div class="form-group">
<label>
<span>Card Number</span>
<input type="text" size="20" data-stripe="number" class="form-control input-lg" />
</label>
</div>
<div class="form-group">
<label>
<span>CVC</span>
<input type="text" size="4" data-stripe="cvc" class="form-control input-lg" />
</label>
</div>
<div class="form-group">
<label>
<span>Expires</span>
</label>
<div class="row">
<div class="col-lg-1 col-md-1 col-sm-2 col-xs-3">
<input type="text" size="2" data-stripe="exp-month" class="input-lg" placeholder="MM" />
</div>
<div class="col-lg-1 col-md-1 col-sm-2 col-xs-3">
<input type="text" size="4" data-stripe="exp-year" class="input-lg" placeholder="YYYY" />
</div>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-lg">Submit Payment</button>
</div>
</form>
#stop
Here is the route
Route::post('/buy/{id}', function($id)
{
Stripe::setApiKey(Config::get('laravel-stripe::stripe.api_key'));
$download = Download::find($id);
//stripeToken is form name, injected into form by js
$token = Input::get('stripeToken');
//var_dump($token);
// Charge the card
try {
$charge = Stripe_Charge::create(array(
"amount" => $download->price,
"currency" => "gbp",
"card" => $token,
"description" => 'Order: ' . $download->name)
);
// If we get this far, we've charged the user successfully
Session::put('purchased_download_id', $download->id);
return Redirect::to('confirmed');
} catch(Stripe_CardError $e) {
// Payment failed
return Redirect::to('buy/'.$id)->with('message', 'Your payment has failed.');
}
});
Here is the js
$(function () {
console.log('setting up pay form');
$('#payment-form').submit(function(e) {
var $form = $(this);
$form.find('.payment-errors').hide();
$form.find('button').prop('disabled', true);
Stripe.createToken($form, stripeResponseHandler);
return false;
});
});
function stripeResponseHandler(status, response) {
var $form = $('#payment-form');
if (response.error) {
$form.find('.payment-errors').text(response.error.message).show();
$form.find('button').prop('disabled', false);
} else {
var token = response.id;
$form.append($('<input type="hidden" name="stripeToken" />').val(token));
$form.get(0).submit();
}
}
Here is the stripe.php in package
<?php
return array(
'api_key' => 'sk_test_Izn8gXUKMzGxfMAbdylSTUGO',
'publishable_key' => 'pk_test_t84KN2uCFxZGCXXZAjAvplKG'
);
Seems like the Config::get might be wrong.
It would have to be written this way.
Stripe::setApiKey(Config::get('stripe.api_key'));
I figured out the problem. In the source for the external javascript file, the "/" was missing at the beginning of the relative path. That is why the javascript file for the homepage was rendering fine but the /buy page was not rendering the javascript file.

Resources