Validate duplicate input based on formArray in Angular by rxweb - html

How to validate duplicate OwnerId in formArray. I am trying install this #rxweb/reactive-form-validators but it not working. This my demo code Stackblitz
HTML:
<div class="col-12 col-sm-12 col-md-12 col-lg-12 col-xl-12 d-flex" [formGroupName]="i"
*ngFor="let driver of nameDriverControl.controls; let i=index">
<label>{{i+1}}.Name</label>
<input class="form-control" type="text" id="name" name="name{{i}}" formControlName="name" ><br/>
<label>{{i+1}}.Owner Id</label>
<input class="form-control" type="text" id="ownerId" name="ownerId{{i}}" inputmode="numeric" dashFormat formControlName="ownerId" maxLength="14">
<div class="col-2 col-sm-2 col-md-2 col-lg-2 col-xl-2">
<div class="form-group mb-0" *ngIf="i !== 0">
<button class="btn btn-danger" (click)="removeDriver(i)">
Delete
</button>
</div>
<div class="form-group mb-2">
<button *ngIf="i >= 0 && i < 1" type="button" [hidden]="nameDriver.length > 3" (click)="addDriver()" class="btn btn-primary">
Add
</button>
</div>
</div>
</div>
Component
createDriver () {
return new FormGroup({
name: new FormControl(null, Validators.required),
ownerId: new FormControl(null, Validators.required)
})
}

You can use unique validator of last version of #rxweb/reactive-form-validators (2.1.3).
After installation you need to import #rxweb like this:
import { RxwebValidators } from '#rxweb/reactive-form-validators';
Then in your createDriver methods add unique validator for ownerId like this:
createDriver () {
return new FormGroup({
name: new FormControl(null, Validators.required),
ownerId: new FormControl("", RxwebValidators.unique(
{ message: 'You must enter a unique OwnerId' }
))
})
}
And in your HTML template you can add error message like this:
<small class="form-text text-danger" *ngIf="driver.controls.ownerId.errors">{{driver.controls.ownerId.errors.unique.message}}<br/></small>
Here is working sample that I've created for you: StackBlitzLink
And the result:
See this for more information about unique validtion.

Related

Displaying Textbox Information and updating in Angular and TS

Fairly new to stack overflow but I needed assistance.
I did a backend and the API is working fine after tests from postman.
I can add employee and I can retrieve employee. However, what I am trying to do is, when I click on the card, I want the card I clicked on to display information about the employee in the textboxes and allow me to change and update. I am failing to retrieve and update my HTML. Please help.
In MY TS, I have this:
updateEmployee(employee: Employee){this.load = true;this.users.updateEmployee(employee).subscribe(res => {this.load = false;this.onPrimaryOutline("Employee has been updated");this.getEmployee();},err=>{console.log(err);this.load = false;this.onErrorOutline("Failed to update Employee, please try again");});}
onOpenModal(employee: Employee, mode: string): void {const containerUpdate = document.getElementById('update-container');const button = document.createElement('button');button.type = 'button';button.style.display = 'none';button.setAttribute('data-toggle', 'modal');if (mode === 'save'){this.updateEmployee = employee;button.setAttribute('data-target', '#update-container');}if (mode === 'delete'){button.setAttribute('data-target', '#deleteEmployeeMasterModal');} containerUpdate.appendChild(button);
button.click()}
service:
public updateEmployee(employee: Employee): Observable<Employee>{ return this.http.put<Employee>(${this.updateEmployee_url}/employee/update, employee) }
HTML:
<div class="card" id="update-container" [hidden]="!showForm" data-toggle="modal" data-target="#updateEmployeeModal"><span aria-hidden="true">×</span><ng-template #template><ngx-loading-x [show]="load"></ngx-loading-x><div NgxLoadingXBlur [show]="load"><div class="modal-header">
<div>
<button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
<span aria-hidden="true">×</span> </button>
</div>
<div class="modal-body">
<div class="card mb-4">
<div class="card-body">
<h6 class="mb-4">{{ 'forms.external-components' | translate }}
</h6>
<form [formGroup]="commonForm" #editForm="ngForm" (ngSubmit)="updateEmployee()" novalidate class="tooltip-label-right">
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" name="name" ngModel="{{editEmployee?.name}}" formControlName="name">
<small>Only letters and at least 2 characters</small>
<div *ngIf="commonForm.get('name')?.errors?.required && !form.submitted" class="invalid-tooltip">Name is required!</div>
</div>
<div class="form-group">
<label>Surname</label>
<input type="text" class="form-control" name="airlineCode" ngModel="{{editEmployee?.surname}}" formControlName="surname">
<small>Only letters and at least 2 characters</small>
<div *ngIf="commonForm.get('surname')?.errors?.required && !form.submitted" class="invalid-tooltip">Code is required!</div>
</div>
<div class="form-group">
<label>Number</label>
<input type="text" class="form-control" name="phonenumber" ngModel="{{editEmployee?.phonenumber}}" formControlName="phonenumber">
<div *ngIf="commonForm.get('phonenumber')?.errors?.required && !form.submitted" class="invalid-tooltip">Number is required!</div>
</div>
<button class="btn btn-primary" type="submit" [disabled]="!commonForm.valid" (click)="updateEmployee(employee, Employee)" (click)="modalRef.hide()">Update</button>
<button type="button" class="btn btn-danger" style="display: inline-block;margin-left: 20px;" (click)="delete()" (click)="modalRef.hide()">Delete</button>
</form>
</div>
</div>
</div>
</div>
I just need to retrieve the values for the specific card when I click on it and be able to update it. But I cannot see what I am omitting. Inspect element says:
ngModel cannot be used to register form controls with a parent formGroup directive. Try using
formGroup's partner directive "formControlName" instead

Property 'emailId' does not exist on type 'LoginPageComponent'

I am trying to validate a html form in angular using Formcontrol validators. But in html file I am getting an error saying "Property 'emailId' does not exist on type 'LoginPageComponent'."
Here's my html and ts code:
<form [formGroup]="formGroupLogin" id="contact" (ngSubmit)="checkuser()" method="post">
<div class="form-group">
<label for="Inputemail"><i class="fas fa-user mr-1"></i>Email </label>
<input type="email" formControlName="emailId" class="form-control" id="emailId" aria-describedby="emailHelp" required>
<span [ngClass]="'error'" *ngIf="emailId.invalid && emailId.touched">Email is Invalid</span>
</div>
<div class="form-group">
<label for="InputPassword"><i class="fas fa-lock prefix mr-1"></i>Password</label>
<input type="password" formControlName="password" class="form-control" id="InputPassword" required>
</div>
<button class="btn btn-primary" name="submit" type="submit" id="contact-submit">Submit</button>
</form>
Typescript file:
initForm() {
this.formGroupLogin = new FormGroup({
emailId: new FormControl('', [Validators.required]),
password: new FormControl('', Validators.required)
})
}
Can anyone help me out?
emailId is not a object of your component but of your FormGroup.
So you should be using
formGroupLogin.emailId.invalid
The problem is that in your HTML code you have this:
<form [formGroup]="formGroupLogin"
and in yout .ts code you have this
this.formGroup = new FormGroup({
It is not the same form name. formGroupLogin != formGroup
Use same name in both files.
Let me know if it worked for you.
The error is completely correct, such a variable does not exist in your component, it is a formcontrol in your form, so you should refer to it accordingly. I like to use a getter or then call get on the parentform:
<span *ngIf="formGroupLogin.get('emailId').invalid && formGroupLogin.get('emailId').invalid">

How to submit multiple forms on one route using handlebars

I'm having trouble with posting information on two forms using handlebars. I just started learning it so I'm not to comfortable with it yet. Basically Im trying to display information from two tables in my database onto two forms by searching for the project id and returning that information. I have it working for the first form. Whenever I search the project ID, the relevant information is returned. But when I search for the project ID on the next form, it populates data on the first form and completely ignores the second form. Ill post all the code I think is relevant to this question.
This is the controller and the route that handles the first form
findOne: async (req, res) => {
const { project_id } = req.body
const metadata = await Prjt_metadata.findOne({
where: {
project_id
}
});
return res.render('allForms', {
metadata
})
}
}
router.post('/find', metadataController.findOne)
This is the controller and the route that handles the second form
getOneCostsHours: async (req, res) => {
try {
const { project_id } = req.body
const costsHours = await Prjt_costs_hours.findOne({
where: {
project_id
}
});
return res.render('allForms', {
costsHours
})
} catch (error) {
console.error(error.message);
return res.status(500).json(error);
}
}
router.post('/find', costsHoursController.getOneCostsHours);
This is the html code for the first form
<form action='/find' method='POST'>
<section class="row">
<div class="col-md-4">
<div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroup-sizing-default">Project ID:</span>
</div>
<input id="disabled" name="project_id" type="text" class="form-control">
</div>
</div>
<div class="input-group mb-3">
<label class="input-group-text" for="inputGroupSelect01">Building:</label>
<select disabled name="building" class="form-select" id="inputGroupSelect01">
{{#with metadata}}
<option selected>{{building}}</option>
{{/with}}
</select>
</div>
<div class="input-group mb-3">
<label class="input-group-text" for="inputGroupSelect01">Measure Type:</label>
<select disabled name="measure_type" class="form-select" id="inputGroupSelect01">
{{#with metadata}}
<option selected>{{measure_type}}</option>
{{/with}}
</select>
</div>
<div class="text-end">
<button id="search" type="submit" style="background-color: #bf5700;"
class="btn text-light btn-warning mt-3">Search</button>
</div>
</form>
This is the html code for the second form
<form action='/find' method="POST">
<div class="card border-secondary text-light mb-3" style="background-color: #333f48;">
<h5 class="card-header">Costs & Hours</h5>
<div class="card-body">
<div class="input-group mb-3">
<span class="input-group-text" id="inputGroup-sizing-default">Project ID:</span>
<input name="project_id" type="text" class="form-control"
aria-label="Sizing example input" aria-describedby="inputGroup-sizing-default">
</div>
<div class="input-group mb-3">
<label class="input-group-text" for="inputGroupSelect01">Implementation or
Annual:</label>
<select disabled name="imp_or_ann" class="form-select" id="inputGroupSelect01">
{{#with costsHours}}
<option>{{imp_or_ann}}</option>
{{/with}}
</select>
</div>
<div class="input-group mb-3">
<label class="input-group-text" for="inputGroupSelect01">Category:</label>
<select disabled name="category" class="form-select" id="inputGroupSelect01">
{{#with costsHours}}
<option>{{category}}</option>
{{/with}}
</select>
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="inputGroup-sizing-default">Costs: $</span>
{{#with costsHours}}
<input disabled value={{cost}} name="cost" type="text" class="form-control" aria-label="Sizing example input"
aria-describedby="inputGroup-sizing-default">
{{/with}}
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="inputGroup-sizing-default">Hours:</span>
{{#with costsHours}}
<input disabled value={{hours}} name="hours" type="text" class="form-control" aria-label="Sizing example input"
aria-describedby="inputGroup-sizing-default">
{{/with}}
</div>
</div>
<button type="submit" style="background-color: #bf5700;"
class="btn btn-warning text-light">Search</button>
</div>
</form>
The value for project_id comes from the input with name "project_id" on both forms. Like I said above, the first form works fine. It gets the relevant info based on the project_id. But When I search on the second form, it gets ignored and the data from the first form gets posted again.
Hopefully my question makes sense. I can explain it better if anyone needs me to.
Thanks for any help in advance! I'm hoping to learn more about handlebars in the future
I figured it out by just combining the two controllers into a separate controller and route
New controller and route
const { Prjt_metadata, Prjt_costs_hours } = require('../models');
module.exports = {
findOne: async (req, res) => {
try {
const { project_id } = req.body
const metadata = await Prjt_metadata.findOne({
where: {
project_id
}
});
const costsHours = await Prjt_costs_hours.findOne({
where: {
project_id
}
});
return res.render('allForms', {
metadata,
costsHours,
})
} catch (error) {
console.error(error.message);
return res.status(500).json(error);
}
}
}
const router = require('express').Router();
const getOneByIdController = require('../controllers/getDataById');
router.post('/find', getOneByIdController.findOne)
module.exports = router

ERROR TypeError: Cannot read property ' ' of undefined

I'm trying to make a form field to add new hotel and specify the address.
I have the hotel class:
export class Hotel {
public constructor (
public name:string,
public address: Address,
){}
}
export class Address {
country:string;
town :string;
street: string;
}
this's the function addHotel in the component:
addHotel(hotel:any, address:any){
if (this.form.valid) {
let newHotel = new Hotel(hotel["name"], (hotel.address["country"], hotel.address["town"], hotel.address["street"]));
the html form is very simple:
<div class="tab-pane" >
<div class="form-group">
<label class="text-gray">Name :</label>
<div class="input-group">
<div class="input-group-addon"><i class="fa fa-envelope-o"></i></div>
<input formControlName="name" class="form-control" type="text">
</div>
</div>
</div>
<div class="tab-pane" id="address">
<div class="form-group">
<label class="text-gray">Country :</label>
<div class="input-group" >
<div class="input-group-addon"><i class="fa fa-map-marker"></i></div>
<input class="form-control" type="text">
</div>
<label class="text-gray">Town :</label>
<div class="input-group" >
<div class="input-group-addon"><i class="fa fa-map"></i></div>
<input class="form-control" type="text">
</div>
<label class="text-gray">Street :</label>
<div class="input-group" >
<div class="input-group-addon"><i class="fa fa-map-signs"></i></div>
<input class="form-control" type="text">
</div>
</div>
</div>
I even tried to add new function, addAddress:
addAddress(hotel,address):any{
//let newAddress = new Address(hotel.address["country"], ,hotel.address["town"], hotel.address["street"]);
this.address.push(hotel.address["country"], hotel.address["town"], hotel.address["street"]);
console.log(this.address);
return this.address;
}
but it always return empty array {country:null, town:null, street:null}
or the famous error: ERROR TypeError: Cannot read property 'country' of undefined
Use the safe navigation operator (?)
look like this
<p>Employer: {{address?.country}}</p>
The safe navigation operator (?) means that the employer field is
optional and if undefined, the rest of the expression should be
ignored.
Reference
https://angular.io/guide/cheatsheet
even when I add console.log(address.town); to the addAddress function, I get undefined. what I have to do??
Read this post it having all details you want : https://angular.io/guide/reactive-forms#nested-formgroups
Please define binding between template and typescript for getting value from template form control example :
export class etailComponent5 {
hotelForm: FormGroup;
constructor(private fb: FormBuilder) {
this.createForm();
}
createForm() {
this.hotelForm= this.fb.group({ // <-- the parent FormGroup
name: [''],
address: this.fb.group({ // <-- the child FormGroup
street: '',
town: '',
country: ''
})
});
}
saveform() {
const hotel:Hotel = hotelForm.value;
//or to get individual control you need to do like this
// const street= hotelForm.get('address.street').value
// const town= hotelForm.get('address.town').value
// const coutnry= hotelForm.get('address.country').value
}
}
<form [formGroup]="hotelForm" >
<div class="form-group">
<label class="center-block">Name:
<input class="form-control" formControlName="name">
</label>
</div>
<div formGroupName="address" class="well well-lg">
<h4>Secret Lair</h4>
<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">Town:
<input class="form-control" formControlName="town">
</label>
</div>
<div class="form-group">
<label class="center-block">Country:
<input class="form-control" formControlName="country">
</label>
</div>
</div>
</form>
Working :
const address = new Address();
address.country='abc';
address.street= 'abc';
address.town ='abc';
let newHotel = new Hotel('abc', address);
console.log(newHotel.name);
console.log(`${newHotel.address.country},${newHotel.address.street},${newHotel.address.town}`);
As per you structure address - property is class used in hotel, so you need to initialized it and then you can assign to address property. as given in above code..
In your code you are trying to set value without initializing it
I suggest make use of interface instead of class
export interface Hotel {
name:string;
address: Address;
}
export interface Address {
country:string;
town :string;
street: string;
}
const hotel: Hotel = { name: 'name of hotel',
address: { country:'abc', town : 'abc', city:'abc' }
};

Error: Cannot find control with path: 'x ' angular 2

I am trying to make a specific form field in my overall form dynamic so that x amount of objects can be added to the array of that field.
However, every time the page inits I get a
Error: Cannot find control with path: 'media -> '
This is my form.ts
this.cardForm = new FormGroup({
'title': new FormControl(cardTitle),
media: this._fb.array([
this.initMedia(),
]),
'links': new FormGroup({
'news': new FormControl(news),
}),
initMedia() {
return this._fb.group({
type: new FormControl(),
raw: new FormControl(),
primary: new FormControl(),
thumbs: this._fb.group({
default: new FormControl()
})
})
}
addMedia(){
const control = <FormArray>this.cardForm.controls['media'];
control.push(this._fb.control(['']));
}
removeMedia(i: number){
const control = <FormArray>this.cardForm.controls['media'];
control.removeAt(i);
}
this is my form.html:
<div class="row">
<div class="col-xs-12">
<form [formGroup]="cardForm" (ngSubmit)="onSubmit(cardForm.value)">
<div class="row">
<div class="col-xs-12">
<button
type="submit"
class="btn btn-success">
Update Card</button>
<button
type="button"
class="btn btn-danger"
(click)="onCancel()">
Cancel</button>
</div>
</div>
<div formArrayName="media">
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<div *ngFor= "let media of cardForm.controls.media.controls; let i=index">
<span>Media {{i + 1}}</span>
<span *ngIf="cardForm.controls.media.controls.length > 1" (click)="removeMedia(i)"></span>
</div>
<div [formGroupName]="i">
<div>
<label>Url</label>
<md-input-container class="mdcontainer">
<input mdInput placeholder="Media Url" type="text" formControlName="raw">
</md-input-container>
</div>
</div>
</div>
</div>
</div>
</div>
And the media[] looks like this:
media: [
{
raw:'string',
primary: boolean,
type: 'string',
thumb: {
default: 'string'
{
}
]
What am I missing/doing wrong here for that error to come up?
Any help/tips/suggestions would be much appreciated.
[formGroupName]="i" should be inside of *ngFor. In this case i variable will have non undefined value
<div *ngFor="let media of cardForm.controls.media.controls; let i=index">
<span>Media {{i + 1}}</span>
<span *ngIf="cardForm.controls.media.controls.length > 1" (click)="removeMedia(i)"></span>
<div [formGroupName]="i">
<div>
<label>Url</label>
<md-input-container class="mdcontainer">
<input mdInput placeholder="Media Url" type="text" formControlName="raw">
</md-input-container>
</div>
</div>
</div>
Plunker Example
You could try the get method instead. I think it is a bit more user-friendly:
cardForm.get('media')
Check it out in the docs here: https://angular.io/guide/reactive-forms#inspect-formcontrol-properties