How to prevent repetition in Angular html? - html

I have an input in my html as follows:
<ng-container *ngIf="({required: <some_condition>, invalid: <some_condition>}) as emailErrors">
<input type="email" class="form-control" placeholder="Email" validate-onblur [class.is-invalid]="emailErrors.required || emailErrors.invalid" [attr.aria-invalid]="emailErrors.required || emailErrors.invalid" [attr.aria-describedby]="emailErrors.required || emailErrors.invalid ? 'email-error' : undefined">
<div *ngIf="emailErrors.required" id="email-error">
<p class="error-msg">{{messages.EMAIL_REQ}}</p>
</div>
</ng-container>
Here in my <input> tag I'm repeting this condition 3 times: emailErrors.required || emailErrors.invalid.
Can I store this condition here in a variable, so that I do not have to repeat it?
P.S. I'm new in Angular

I would recommend you to use the template form of Angular. These are fairly simpler to implement.
In the code below make sure you give name property on the input field otherwise an error will be thrown, while working on with [(ngModel)]
<form #f="ngForm">
<input type="email" class="form-control" placeholder="Email" name="mail" required [(ngModel)]="model.email" #mail="ngModel">
<span *ngIf="mail.invalid">
{{messages.EMAIL_REQ}}
</span>
</form>

Why not introduce an additional property in the wrapping <ng-container>? And seeing that emailErrors.invalid isn't used anywhere else, it could be removed if it's unneeded.
<ng-container *ngIf="({required: <condition_1>, reqOrInv: <condition_1> || <condition_2>}) as emailErrors">
<input
type="email"
class="form-control"
placeholder="Email"
validate-onblur
[class.is-invalid]="emailErrors.reqOrInv"
[attr.aria-invalid]="emailErrors.reqOrInv"
[attr.aria-describedby]="emailErrors.reqOrInv ? 'email-error' : undefined"
>
<div *ngIf="emailErrors.required" id="email-error">
<p class="error-msg">{{messages.EMAIL_REQ}}</p>
</div>
</ng-container>
But as #YashwardhanPauranik noted in their answer, you're better off using Angular template driven or reactive forms. They provide more granular control.

Related

How to use one condition at multiple places in Angular?

I have the following form in my angular project:
<input type="email" class="form-control" placeholder="Email" validate-onblur aria-label="email">
<div *ngIf="!loginForm.controls['username'].pristine && loginForm.controls['username'].dirty && loginForm.controls['username'].hasError('required')">
<p class="error-msg">{{messages.EMAIL_REQ}}</p>
</div>
The condition in the <div> tag in ngIf, I want to use this same condition in the <input> tag above, to add a class to that element, how can I achieve this?
Something like this (myCondition):
<input ngClass="{'error': myCondition}" type="email" class="form-control" placeholder="Email" validate-onblur aria-label="email">
<div *ngIf="myCondition">
<p class="error-msg">{{messages.EMAIL_REQ}}</p>
</div>
I am new to angular so don't know if it possible or not.
I thought of creating a function in the component and calling this function with onblur attribute, but with this approach I have to create several functions, because the form has several fields with their specific conditions.
So how can I do this in the html file itself?
Yes it's possible, you could wrap the elements in a <ng-container> tag and extract the condition using *ngIf directive. It's commented out in the final rendered DOM.
<ng-container *ngIf="({res: (!loginForm.controls['username'].pristine && loginForm.controls['username'].dirty && loginForm.controls['username'].hasError('required'))}) as cond">
<input type="email" [class.error]="cond.res" class="form-control" placeholder="Email" validate-onblur aria-label="email">
<div *ngIf="cond.res">
<p class="error-msg">{{messages.EMAIL_REQ}}</p>
</div>
</ng-container>
I've also replaced [ngClass] with [class.error].
Although it's tempting to ignore the object in the *ngIf like
<ng-container *ngIf="(!loginForm.controls['username'].pristine && loginForm.controls['username'].dirty && loginForm.controls['username'].hasError('required')) as cond">
...
</ng-container>
it would counter-intuitive as the <input> would not be rendered when the condition is false. Wrapping it in an object would ensure the elements inside would be rendered even if the outer *ngIf in the <ng-container> would be false.

Input Validation Template Reference ngFor

I am trying to validate an input that is required in a *ngFor loop.
But I cant get a unique template Reference.
On Submit all Input fields are required / or none if at least one is filled out.
I tried to declare Template Reference like #optionContent_{{i}} but i can fill that in [ngClass].
Any help?
<form name="form" (ngSubmit)="f.form.valid && addOption()" #f="ngForm" novalidate>
<div class="row" *ngFor="let size of optionModel.optionContent let i = index">
<div class="col-12">
<label for="optionContent_{{i}}>Name Option</label>
<input id="optionContent_{{i}}"
type="text"
#optionContent="ngModel"
[ngClass]="{ 'is-invalid': f.submitted && optionContent.invalid }"
required
name="optionContent"
[(ngModel)]="size.name" class="form-control">
</div>
</div>
</form>
the problem is in the name attribute they all have the same name, so ngForm gets confused try add dynamic name
<input ... name="optionContent{{i}}" ../>

Form validation when two inputs are in the same line in an Angular 6 application

I am implementing a form using Angular 6 and html. There, I have 2 input fields in the same line.
<div class="form-group" >
<label for="input-name" class="sr-only">First name</label>
<input class="registerInput2"
type="text"
nbInput
[(ngModel)]="user.firstName"
#firstName="ngModel"
id="input-fname"
name="firstName"
placeholder="First name"
autofocus
fullWidth
[required]="getConfigValue('forms.validation.firstName.required')"
[minlength]="getConfigValue('forms.validation.firstName.minLength')"
[maxlength]="getConfigValue('forms.validation.firstName.maxLength')">
<span class="iconSan"><i class="fa fa-user"></i></span>
<small class="form-text error" *ngIf="firstName.invalid && firstName.touched && firstName.errors?.required">
First name is required!
</small>
<small
class="form-text error"
*ngIf="firstName.invalid && firstName.touched && (firstName.errors?.minlength || firstName.errors?.maxlength)">
First name should contains
from {{getConfigValue('forms.validation.firstName.minLength')}}
to {{getConfigValue('forms.validation.firstName.maxLength')}}
characters
</small>
<label for="input-name" class="sr-only">Last name</label>
<input class="registerInput2"
type="text"
nbInput [(ngModel)]="user.lastName"
#lastName="ngModel"
id="input-lname"
name="lastName"
placeholder="Last name"
autofocus
fullWidth [required]="getConfigValue('forms.validation.lastName.required')"
[minlength]="getConfigValue('forms.validation.lastName.minLength')"
[maxlength]="getConfigValue('forms.validation.lastName.maxLength')"
[status]="email.dirty ? (email.invalid ? 'danger' : 'success') : ''">
<span class="iconSan"><i class="fa fa-user"></i></span>
<small class="form-text error" *ngIf="lastName.invalid && lastName.touched && lastName.errors?.required">
Last name is required!
</small>
<small class="form-text error" *ngIf="lastName.invalid && lastName.touched && (lastName.errors?.minlength || lastName.errors?.maxlength)">
Last name should contains from {{getConfigValue('forms.validation.lastName.minLength')}} to {{getConfigValue('forms.validation.lastName.maxLength')}}
characters
</small>
</div>
I have set style for the firstName as 'float-left' and for the lastName as 'float-right'. But when my input fields give validation messages it changes the UI. For example, If the 'firstName' field gives a validation message, 'lastName' field goes down. I searched the internet a lot about the problem but did not get any solution. The below image shows the error.
Error Message
While using small with input is pretty striaghtforward and easy, I would recommend you to use Angular Material library written specifically for angular, but only if you haven't created bootstrap as a dependency of your project yet because angular material and bootstrap doesn't work well together. With that being said and assuming you would go for angular material, the solution to your layout problems are over.
Instead of using div tag, you would use mat-form-field and instead of small, you would use mat-error, you don't have to change any logic for that, just the tags. The angular material dependency will take care of the layout and styling issues.
Take a look at the documentation, if you are an angular guy, I'm sure you'll love it:
https://material.angular.io/components/form-field/overview

input field or help text doesn't turn red when field is invalid

I used to implement an Angular 2/4 application with Bootstrap 3 and used the Reactive Forms approach. I had a field-validation where the border of the input-field turned red and an error message appeared under the field in red font color.
it looks like this:
<div class="form-group row"
[ngClass]="{'has-error': (sourcesForm.get('sourceName').touched ||
sourcesForm.get('sourceName').dirty) &&
!sourcesForm.get('sourceName').valid }">
<label class="col-md-2 col-form-label"
for="sourceNameId">Source Name</label>
<div class="col-md-8">
<input class="form-control"
id="sourceNameId"
type="text"
placeholder="Source Name (required)"
formControlName="sourceName" />
<span class="help-block" *ngIf="(sourcesForm.get('sourceName').touched ||
sourcesForm.get('sourceName').dirty) &&
sourcesForm.get('sourceName').errors">
<span *ngIf="sourcesForm.get('sourceName').errors.required">
Please enter the Source Name.
</span>
<span *ngIf="sourcesForm.get('sourceName').errors.minlength">
The Source Name must be longer than 3 characters.
</span>
<span *ngIf="sourcesForm.get('sourceName').errors.maxlength">
The Source Name is too long.
</span>
</span>
</div>
</div>
Now i have to use Bootstrap 4 and neither the error message or the input-field turns red. How do i realise this? I tried to change the class of the parent span-block to "form-text" but it didn't work.
For beta version of Bootstrap v4, you can check out Form validation docs. There you can read about the new way, supported by all modern browsers for HTML5 way of form-validation with valid/invalid css classes. There Bootstrap uses the .was-validated and .invalid-feedback classes for what you want to achieve (see code snippet).
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet"/>
<form class="container" id="needs-validation" novalidate>
<label for="validationCustom02">Last name</label>
<input type="text" class="form-control" id="validationCustom02" placeholder="Last name" value="Otto" required>
<label for="validationCustom03">City</label>
<input type="text" class="form-control" id="validationCustom03" placeholder="City" required>
<div class="invalid-feedback">
Please provide a valid city.
</div>
<button class="btn btn-primary" type="submit">Submit form</button>
</form>
<script>
// Example starter JavaScript for disabling form submissions if there are invalid fields
(function() {
"use strict";
window.addEventListener("load", function() {
var form = document.getElementById("needs-validation");
form.addEventListener("submit", function(event) {
if (form.checkValidity() == false) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add("was-validated");
}, false);
}, false);
}());
</script>
If you want something more similar to Bootstrap 3, you can use what they call server-side validation, as it is written:
As a fallback, .is-invalid and .is-valid classes may be used instead of the pseudo-classes for server side validation. They do not require a .was-validated parent class.
Previous answer for alpha version of Bootstrap V4 (if you must use this).
On Bootstrap V4 Form Validation Docs there is the following example:
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>
<div class="form-group has-danger">
<label class="form-control-label" for="inputDanger1">Input with danger</label>
<input type="text" class="form-control form-control-danger" id="inputDanger1">
<div class="form-control-feedback">Sorry, that username's taken. Try another?</div>
<small class="form-text text-muted">Example help text that remains unchanged.</small>
</div>
So i think you just need to change the has-error class to has-danger
This is the solution:
<div class="form-group row">
<label class="col-md-2 col-form-label"
for="sourceNameId">Source Name</label>
<div class="col-md-8">
<input class="form-control"
[ngClass]="{'is-invalid': (sourcesForm.get('sourceName').touched ||
sourcesForm.get('sourceName').dirty) &&
!sourcesForm.get('sourceName').valid }"
id="sourceNameId"
type="text"
placeholder="Source Name (required)"
formControlName="sourceName" >
<span class="invalid-feedback" *ngIf="(sourcesForm.get('sourceName').touched ||
sourcesForm.get('sourceName').dirty) &&
sourcesForm.get('sourceName').errors">
<span *ngIf="sourcesForm.get('sourceName').errors.required">
Please enter the Source Name.
</span>
<span *ngIf="sourcesForm.get('sourceName').errors.minlength">
The Source Name must be longer than 3 characters.
</span>
<span *ngIf="sourcesForm.get('sourceName').errors.maxlength">
The Source Name is too long.
</span>
</span>
</div>
</div>
i needed to put the [ngClass]into the input-tag. Then i had to define the class as is-invalid and set the parent span-class to invalid-feedback
i know that your question is for long time ago, but it is the best way to validate the form-control input field by reactive form technique and bootstrap 4 to display the validation. first you need to write some code for your form :
in html section:
<form [formGroup]="myForm">
<div class="form-group">
<label for="name">first Name: </label>
<input type="text" class="form-control" formControlName="firstName" id="name">
<div *ngIf="firstName.touched && firstName.invalid" class="alert alert-danger">
<div *ngIf="firstName.errors.required">filling name is required!</div>
</div>
</div>
in ts file, you should implement the logic to conduct the validation.
in ts file:
myForm = new FormGroup({
'firstName':new FormControl('',Validators.required)
})
//getter method
get firstName(){
this.myForm.get('firstName');
}
now you can see that the validation is working. now to give style to input field to show the red border around the invalid input, just go to css file of component and add this class to the css file:
.form-control.ng-touched.ng-invalid{border:2px solid red;}
and simply you can see the result.

Do HTML email validation and Angular 2 work together?

I'm currently working on validations with Angular 2. I have some problems with HTML email and website validators and valid ngModel's property.
For example:
<form #form="ngForm">
<input type="email" #email="ngModel" [(ngModel)]="contact.email" name="email" >
<button [disabled]="!form.form.valid" type="submit">Btn</button>
Every word i input is fine. #email.valid remains true as if no HTML5 validator existed:
{{#email.valid}} %%% true
So the form's button is enabled all the time. But when I hit the button the HTML warning comes out saying that the email field is invalid, so validation is working, but #email.valid it's still true.
Is it possible to use angular2's ngModel directive with HTML validators?
Yes you can use both together, you can use them like this:
<input id="email" pattern="[a-z0-9._%+-]+#[a-z0-9.-]+\.[a-z]{2,3}$" class="form-control" type="email" ng-model="loginctrl.user.email" name="email" placeholder="Enter Email Address" required/>
<span ng-show="myForm.email.$touched && myForm.email.$error.required"><b class="color">This is a required field</b></span> <span ng-show=" myForm.$dirty && myForm.email.$invalid"><b class="color">This field is invalid</b></span>