How to use one condition at multiple places in Angular? - html

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.

Related

How to prevent repetition in Angular 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.

required field validation only for the first item in a list

I am using Template Driven for my Angula'rs form and I have a div that repeats several times (according to a counter variable).
The thing is , I need the required validation only for the first item in this list and I 'm not sure how to do that.
<div class="form-group required margin-left" *ngFor="let hore of horim;let i = index">
<label class="control-label translate-label" [id]="'lblShemPratiHore'+i">{{selectedLanguage.shemPrati}}</label>
<!-- <img src="../../../assets/images/parent.png" alt="shem prati"> -->
<input
[id]="'shemPratiHore'+i"
[(ngModel)]="hore.shemPrati"
class="form-control input-lg"
[name]="'shemPratiHore'+i"
[attr.aria-describedby]="'lblShemPratiHore'+i"
#shemPrati="ngModel"
required
[ngModelOptions]="{ updateOn: 'blur' }"/>/>
<div *ngIf="shemPrati.errors?.required && shemPrati.touched" class="alert alert-danger">
Required Field
</div>
</div>
try binding to the required attribute if index is equal 0.
[required]="index == 0"
The anwer for this post is as follow:
[required]="i==0"

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}}" ../>

Boolean variable for attribute required in angular

Hi I am trying to make one input form field required based on one boolean variable in my component.
Component:
public myDivHidden: boolean = true;
In my html
<form #railForm="ngForm" (ngSubmit)="executeRailForm(railForm.value);" *ngIf="railFormData" ngNativeValidate>
...
<div [hidden]="myDivHidden">
...
<div class="form-group">
<label for="myName">My Name</label>
<input type="text" name="myName" [(ngModel)]="railFormData.myName" required="!myDivHidden">
</div>
...
</div>
...
</form>
I am trying to avoid required if div is hidden and make it required if div is visible. I am getting error: An invalid form control with name='myName' is not focusable.
How I can make required field required when div is visible and not required when div is invisible in html page?
try [attr.required]="!myDivHidden" or required="{{!myDivHidden}}"

Angular js - ng-model in ng-repeat

Different 'ng-model name' in ng repeat - Possible?
<div ng-repeat="todo in todos">
<input type="text" ng-model="tag">
<button type="submit"ng-click="addTodo(todo._id)">Add</button>
</div>
In this case, there are some repeat todo item (based on todos json data) will show on frontend
My Problem: What i type on any input field , all input field showing same data
I need different ng-model name on each input field , I guess like this ng-model="tag($index)"
You could place the newly created model inside tag array by its $index, while declaring model inside the tag you should use array notation [] instead of ()
ng-model="tag($index)"
should be
ng-model="tag[$index]"
Markup
<div ng-repeat="todo in todos">
<input type="text" ng-model="tag[$index]">
<button type="submit"ng-click="addTodo(todo._id)">Add</button>
</div>
It is possible like below
<div ng-repeat="todo in todos">
<input type="text" ng-model="tag[todo]"> <--todo.key-->
<button type="submit"ng-click="addTodo(todo._id)">Add</button>
</div>
You can do it like this:
<input type="text" ng-model="todo.tag">