I have a page with an account number and a button to block the user's account. The webService interacts perfectly. When I click on the blocking button, the account is blocked!
Here is a small picture illustration.
I click on the button
The card displays that the account is blocked
Now, I want to add an input. The user can block the account only if he enters the password.
I added the input in HTML...
To test, the webService with the password, I run the cURL and it works.
{
"HEADER":{
"USER":"TOTO",
"CHANNEL":"B",
"LANGUAGE":"FR",
"SESSION":"",
"PASSWORD":"TOTO",
"NUMTRX":0,
"VERSION":"",
"LADACO": 0,
"LAHECO": 0,
"LADAPWD": 0,
"LAHEPWD": 0,
"IDENTIF": 0,
"AGDE": 0,
"TIERS": 0,
"EXPIRED": 0,
"EXPDATE": "0001-01-01",
"EXPDAYS": 0,
"CDEACC": ""
},
"CUSTOMER":{
"REFERENCE":"NA0052",
"ID":"",
"INTITULE1":"",
"INTITULE2":"",
"ACCESS":0,
"SERVICE":0,
"DEFAULTPTF":0
},
"MODE": "ETAT",
"PTF_IN": {
"ETAT": 4,
"MONTANT": 0
}
}
The return works
{"RETURNCODE":"OKK00","RETURNLIST":[]}
Thus, the webService works with the password. Now I need to integrate this into the front-end. But I don't understand how I have to integrate the password input?!
The page HTML is like this:
<form #formulaire="ngForm" (ngSubmit)="formulaire.form.valid && locking()">
<div class="row row-cols-3 pt-3">
<div class="col text-end">
<!-- Account number -->
<p for="account" class="form-label">Account number</p>
</div>
<div class="col-4">
<label class="fw-bold">{{ ((currentPortfolio$ | async)?.DEPO | accountDash) }} ({{
((currentPortfolio$ | async)?.NO_PTF) }})</label>
</div>
</div>
<div class="row row-cols-3 pt-3">
<div class="col text-end">
<!-- Password -->
<label for="passwordCurrent" class="form-label">Password</label>
</div>
<div class="col-4">
<input id="passwordCurrent" name="passwordCurrent" type="password" class="form-control" maxlength="30">
</div>
</div>
<div *ngIf="!show" class="row row-cols-3 pt-3">
<div class="col"></div>
<div class="col text-start">
<!-- Blocking -->
<button type="submit" class="btn-green" [disabled]="formulaire.form.invalid">Blocking</button>
</div>
</div>
</form>
Service.ts
#Injectable()
export class LockingService {
private readonly api: string = environment.api;
constructor(
private http: HttpClient,
private router: Router,
private store: Store
) {}
getLocking(): Observable<ApiResponse> {
return this.http.post<ApiResponse>(
this.api + `/WBBLOCKING`,
{
PTF_IN: {
ETAT: 4, // blocking
MONTANT: 0,
},
MODE: "ETAT",
},
{ headers: { AddCustomer: "" } }
);
}
getPortfolios(): Observable<SelectPortfolioResponse> {
return this.http.post<SelectPortfolioResponse>(
this.api + `/WBPORTFOLIO`,
{},
{ headers: { AddCustomer: "" } }
);
}
selectPortfolio(selectPortfolio: SelectPortfolio): Observable<void> {
return this.store.dispatch(
new SetCurrentPortfolioAction(
SelectPortfolio_To_Portfolio(selectPortfolio)
)
);
}
}
In the locking.component.ts I have this:
How do I interact the html input password in typescript?
Thank you very much for your help and explanation.
export class LockingComponent implements OnInit {
private unsubscribe$ = new Subject<void>();
show = false;
buttonName = "Show";
hide: any;
#Select(ProfileState.currentPortfolio)
currentPortfolio$!: Observable<Portfolio>;
#Select(AuthState.user) user$!: Observable<string>;
constructor(
private service: LockingService,
private store: Store,
private router: Router
) {}
ngOnInit(): void {}
locking(): void {
this.service
.getLocking()
.pipe(
takeUntil(this.unsubscribe$),
takeWhile((res) => res.RETURNCODE === ApiResponseCodeEnum.Ok),
tap(() => {
this.show = !this.show;
if (this.show) {
this.buttonName = "Hide";
console.log(this.show);
} else {
this.buttonName = "Show";
}
}),
concatMap(() => this.service.getPortfolios()),
takeWhile(
(res) =>
res.RETURNCODE === ApiResponseCodeEnum.Ok && res.PTF.length > 0
),
concatMap((res) => this.service.selectPortfolio(res.PTF[0]))
)
.subscribe();
}
}
Related
My system is working fine but this error keeps showing I don't know what is the problem. Please help.
Here is my add.component.html
<form
[formGroup]="userValue"
class="border border-secondary m-5 p-3 rounded-2 bg-dark"
>
<div class="body">
<h5 class="register-title text-white" id="registerTitle">
User Registration
</h5>
<div class="mb-3">
<label for="txtName" class="form-label">Name</label>
<input
type="text"
formControlName="name"
class="form-control bg-dark text-white"
id="txtName"
placeholder="Enter your name."
/>
<span
style="color: red"
*ngIf="
(userValue.controls['name'].dirty ||
userValue.controls['name'].touched) &&
userValue.hasError('required', 'name')
"
>Input required</span
>
<span
style="color: red"
*ngIf="
(userValue.controls['name'].dirty ||
userValue.controls['name'].touched) &&
userValue.hasError('minlength', 'name')
"
>Input must have minimum length of 3 characters</span
>
</div>
<div class="mb-3">
<label for="txtEmail" class="form-label">Email</label>
<input
type="email"
formControlName="email"
class="form-control bg-dark text-white"
id="txtEmail"
placeholder="Enter your email."
/>
<span
style="color: red"
*ngIf="
(userValue.controls['email'].dirty ||
userValue.controls['email'].touched) &&
userValue.hasError('required', 'email')
"
>Input required</span
>
<span
style="color: red"
*ngIf="
(userValue.controls['email'].dirty ||
userValue.controls['email'].touched) &&
userValue.hasError('email', 'email')
"
>Valid email is required</span
>
</div>
<div class="mb-3">
<label for="txtPhone" class="form-label">Phone</label>
<input
type="text"
formControlName="phone"
class="form-control bg-dark text-white"
id="txtPhone"
placeholder="Enter your phone number. ex. 09221111333"
/>
<span
style="color: red"
*ngIf="
(userValue.controls['phone'].dirty ||
userValue.controls['phone'].touched) &&
userValue.hasError('required', 'phone')
"
>Input required</span
>
<span
style="color: red"
*ngIf="
(userValue.controls['phone'].dirty ||
userValue.controls['phone'].touched) &&
userValue.hasError('maxlength', 'phone')
"
>Input must have maximum length of 11 characters</span
>
<span
style="color: red"
*ngIf="
(userValue.controls['phone'].dirty ||
userValue.controls['phone'].touched) &&
userValue.hasError('minlength', 'phone')
"
>Input must have minimum length of 11 characters</span
>
</div>
</div>
<div class="footer">
<button
type="button"
[disabled]="!userValue.valid"
*ngIf="btnShowSubmit"
(click)="addUser()"
class="btn btn-dark"
>
Submit
</button>
<button
type="button"
*ngIf="btnShowUpdate"
(click)="updateUser()"
class="btn btn-primary"
>
Update
</button>
</div>
</form>
Here is my .ts page
import {
Component,
OnInit,
Input,
EventEmitter,
Output,
SimpleChanges,
OnChanges,
} from '#angular/core';
import {
FormBuilder,
FormGroup,
Validators,
FormControl,
} from '#angular/forms';
import { ApiService } from '../service/api.service';
import { userModel } from '../models/model';
#Component({
selector: 'app-add',
templateUrl: './add.component.html',
styleUrls: ['./add.component.css'],
})
export class AddComponent implements OnInit, OnChanges {
userValue!: FormGroup;
userModelObj: userModel = new userModel();
userList: any = [];
btnShowSubmit = true;
btnShowUpdate = false;
#Input() userInfo: userModel = {
id: 0,
name: '',
email: '',
phone: '',
};
#Output() addlist = new EventEmitter();
#Output() updatelist = new EventEmitter();
constructor(private formbuilder: FormBuilder, private api: ApiService) {}
ngOnInit(): void {
this.userValue = this.formbuilder.group({
name: new FormControl('', [Validators.required, Validators.minLength(3)]),
email: new FormControl('', [Validators.required, Validators.email]),
phone: new FormControl('', [
Validators.required,
Validators.minLength(11),
]),
});
this.getUser();
}
ngOnChanges(changes: SimpleChanges): void {
this.editUser(this.userInfo);
}
addUser() {
this.userModelObj.name = this.userValue.value.name;
this.userModelObj.email = this.userValue.value.email;
this.userModelObj.phone = this.userValue.value.phone;
this.api.postUser(this.userModelObj).subscribe({
next: (v) => {
console.log(v);
console.log('User added');
this.addlist.emit(v);
alert('User record added');
this.getUser();
this.userValue.reset();
},
error: (e) => {
console.log(e);
alert('Error');
},
complete: () => {},
});
}
getUser() {
this.api.getUser().subscribe((res) => {
this.userList = res;
});
}
deleteUser(data: any) {
this.api.deleteUser(data.id).subscribe({
next: (v) => {
console.log(v);
},
error: (e) => {
console.log(e);
alert('Error');
},
complete: () => {
console.log('User deleted!');
alert('User record deleted');
this.getUser();
this.userValue.reset();
},
});
}
editUser(data: any) {
this.userValue.controls['name'].setValue(this.userInfo?.name);
this.userValue.controls['email'].setValue(this.userInfo?.email);
this.userValue.controls['phone'].setValue(this.userInfo?.phone);
this.userModelObj.id = this.userInfo.id;
this.showUpdate();
}
updateUser() {
this.userModelObj.name = this.userValue.value.name;
this.userModelObj.email = this.userValue.value.email;
this.userModelObj.phone = this.userValue.value.phone;
this.api.putUser(this.userModelObj, this.userModelObj.id).subscribe({
next: (v) => {
alert('User has been updated');
this.updatelist.emit(v);
this.getUser();
this.userValue.reset();
this.showSave();
this.userModelObj.id = 0;
},
error: (e) => {
console.log(e);
alert('Error');
},
complete: () => {
console.log('complete');
},
});
}
showSave() {
this.btnShowSubmit = true;
this.btnShowUpdate = false;
}
showUpdate() {
this.btnShowSubmit = false;
this.btnShowUpdate = true;
}
}
Here is the error
I don't get to understand the error
I tried replacing this.userValue = this.formbuilder.group with this.userValue = new FormControl but the error is still existing.
I am a newbie on programming please help. Thank you :)
In the template use userValue?.controls instead of userValue.controls.
Hope this will help you.
It's look like your editUser() doesn't found controls property, So that's why you are getting this error.
Your editUser() function should look like below:
editUser(data: any) {
this.userValue.controls.name.setValue(this.userInfo?.name);
this.userValue.controls.email.setValue(this.userInfo?.email);
this.userValue.controls.phone.setValue(this.userInfo?.phone);
this.userModelObj.id = this.userInfo.id;
this.showUpdate();
}
I'm getting the well known error in my Angular app, but not sure why it happens and how to fix it. I was trying a couple of ways including adding setTimeout, delay(0), switching to different hook but any of them seems to work in my case.
Problem description:
I have a list of products and on click single product can be added to the cart with selected products
//product.list.component.ts
addToProductCart(product: IProduct) {
this.productsService.addProductToSelectedProducts(product);
}
The service looks like below:
//product.service.ts
#Injectable({
providedIn: 'root'
})
export class ProductsService {
selectedProducts: BehaviorSubject<IProduct[]> = new BehaviorSubject<IProduct[]>([]);
product = this.selectedProducts.asObservable();
constructor(private http: HttpClient) { }
getProductsList(): Observable<IProduct[]> {
return this.http.get<IProduct[]>(`${environments.environment.baseUrl}/products`);
}
patchProductLikes(id: number, payload: Partial<IProduct>): Observable<number> {
return this.http.patch<number>(`${environments.environment.baseUrl}/products/${id}`, payload);
}
addProductToSelectedProducts(product: IProduct) {
this.selectedProducts.next([...this.selectedProducts.value, product]);
}
clearSelectedProducts(): void {
this.selectedProducts.next([]);
}
removeSelectedProduct(products: IProduct[]): void {
this.selectedProducts.next(products);
}
}
When product is selected on my header the product count is increased and displayed on cart icon:
//header.component.html
<span (click)="openDialog()" #openCartButton>
<mat-icon matBadge="{{selectedProductsCount}}"matBadgePosition="above after">
shopping_cart
</mat-icon>
</span>
//header.component.ts
openDialog() {
this.dialog.open(CartDetailsComponent, {
width: '450px',
height: '650px',
data: {
positionRelativeToElement: this.openCartButton
}
});
}
getSelectedProductsCount(): void {
this.productsService.product.subscribe((products) => {
this.selectedProductsCount = products.length;
});
}
If header cart icon is clicked the dialog with selected product is opened, and if there are no selected products then empty cart placeholder should be displayed:
//cart-details.component.html
<div *ngIf="products.length > 0 else emptyCart">
<h5 mat-dialog-title>Total order</h5>
<div mat-dialog-content class="product" [#loadProducts]="'in'">
<ul>
<li *ngFor="let groupedProducts of selectedProducts | keyvalue" class="product__product-item">
<div *ngFor="let prod of groupedProducts.value | productPipe; let i = index" class="product-details-container">
<div>
<img [src]="prod.image" alt="Product photo" class="product-details-container__product-img">
</div>
<div class="product-info">
<p>{{prod.name}}
<span class="product-info__price">${{prod.price}}</span>
</p>
<p>
{{prod.productMaterial}}
</p>
<p>
{{prod.color}}
</p>
<p #deleteProduct>Amount: {{groupedProducts.value.length}} </p>
<p>Total: ${{prod.price * groupedProducts.value.length}}</p>
<div class="product-actions-container">
<a (click)="deleteProduct(prod)" class="delete-product">Delete</a>
<a (click)="viewProductDetails(prod)" class="view-details">View details</a>
</div>
</div>
</div>
</li>
<span>SUM: ${{totalSum}}</span>
</ul>
</div>
</div>
<ng-template #emptyCart>
<div class="empty-bag-container">
<mat-icon svgIcon="empty-bag" class="empty-bag-container__empty-bag-icon"></mat-icon>
<h4 class="empty-bag-container__empty-bag-heading">
YOUR BAG IS EMPTY
</h4>
<span class="empty-bag-container__empty-bag-details"> Looks like you haven’t made your choice yet.
Check out 100+ styles for everyone!</span>
</div>
</ng-template>
//cart-details.component.ts
export class CartDetailsComponent implements OnInit, OnDestroy {
private positionRelativeToElement: ElementRef;
isOpen = false;
totalSum = 0;
totalPrices: number[] = [];
private destroySubject: Subject<boolean> = new Subject<boolean>();
selectedProductsCount: number;
selectedProducts: Record<string, IProduct[]>;
productSumPrice: number;
products: IProduct[] = [];
constructor(public dialogRef: MatDialogRef<CartDetailsComponent>,
private productsService: ProductsService,
#Inject(MAT_DIALOG_DATA) public data: { positionRelativeToElement: ElementRef }) {
this.positionRelativeToElement = data.positionRelativeToElement;
}
ngOnInit() {
const matDialogConfig = new MatDialogConfig();
const rect: DOMRect = this.positionRelativeToElement.nativeElement.getBoundingClientRect();
matDialogConfig.position = { right: `10px`, top: `${rect.bottom + 2}px` };
this.dialogRef.updatePosition(matDialogConfig.position);
this.getSelectedProducts();
this.calculatePrices();
}
ngOnDestroy() {
this.destroySubject.next(true);
}
close() {
this.dialogRef.close();
}
deleteProduct(product: IProduct) {
const prodId: number = product.id;
this.selectedProducts[prodId] = this.selectedProducts[prodId].slice(0, this.selectedProducts[prodId].length - 1);
const index: number = this.products.map(x => {
return x.id;
}).indexOf(product.id);
this.products.splice(index, 1);
this.productsService.removeSelectedProduct(this.products);
this.calculatePrices();
}
viewProductDetails(product: IProduct): void {
console.log(product);
}
animateCurrentItem(product: IProduct) {
console.log(product, 'animation');
}
calculatePrices() {
if (this.products.length > 0) {
this.totalPrices = [];
Object.values((this.selectedProducts))
.map((prod) => {
if (prod.length > 0) {
(prod as IProduct[]).map((p) => {
this.totalPrices.push(Number(p.price));
});
}
});
if (this.totalPrices.length > 0) {
this.totalSum = this.totalPrices.reduce((prev, cur) => {
return prev + cur;
});
} else {
this.totalSum = 0;
this.productsService.clearSelectedProducts();
}
}
}
getSelectedProducts() {
this.productsService.product
.pipe(
delay(0),
startWith([]),
takeUntil(this.destroySubject),
)
.subscribe((products) => {
if (products.length > 0) {
this.products = products;
this.productSumPrice = _.sumBy(products, (prod) => parseFloat(prod.price));
this.selectedProductsCount = _.sum(Object.values(_.countBy(products, product => product.id)));
this.selectedProducts = _.groupBy(products, 'id');
}
});
}
}
And here the error occurs. If cart is empty (meaning products.length === 0) the <ng-template #emptyCart> is displayed but with the error:
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'loading-background: false'. Current value: 'loading-background: true'.
The error is about loading-background in ngx-ui-loader lib witch I use in app.module:
//app.module
(...)
import { NgxUiLoaderModule, NgxUiLoaderHttpModule, NgxUiLoaderConfig, SPINNER, POSITION, PB_DIRECTION } from 'ngx-ui-loader';
imports: [
...
NgxUiLoaderModule.forRoot(ngxUiLoaderConfig),
NgxUiLoaderHttpModule,
]
Any idea what cause the issue and how to fix it and avoid in the future?
I was traying to reproduce it on stackblitz but with no luck :). Although maybe it will help understand my issue ;P
https://stackblitz.com/edit/angular-h3xyop?file=src%2Fapp%2Fproduct-list%2Fproduct-list.component.ts
This is because of your view changed after rendering. You need to use changeDetectorRef to detechChanges. add in constructor
construct(private ref: changeDetectorRef)
{}
and after change you add
this.ref.detectChanges();
https://angular.io/api/core/ChangeDetectorRef
This is my method where i try to check from the database which usernames are already taken:
takenUsernames(control: FormControl): Promise<any> | Observable<any> {
const promise = new Promise<any>((resolve, reject) => {
this.employeeService.getEmployeeList().subscribe((employees) => {
this.employees = employees;
for (let employee of control.value) {
if (this.employees.indexOf(employee) !== -1) {
resolve({ usernameIsTaken: true });
} else {
resolve(null);
}
}
});
});
return promise;
}
And this is my formGroup object:
this.newProjectForm = new FormGroup({
projectName: new FormControl(null, Validators.required),
description: new FormControl(null),
assignedEmployees: new FormControl(null, [Validators.required]),
employees: new FormArray(
[],
[this.forbidUsernames.bind(this)],
[this.takenUsernames.bind(this)]
),
});
As you can see employees is a FormArray so i loop true control.value to check each username but it doesnt seem to work. The problem is in the takenUsernames function because i dont get the pending status if i inspect the element.
Here's the whole html code:
link
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght#600&family=Roboto+Mono:wght#500&display=swap"
type="text"
rel="stylesheet"
/>
<div class="backdrop" (click)="onFormAlertClose()"></div>
<div class="alert-box">
<form [formGroup]="newProjectForm" (ngSubmit)="onSubmit()">
<h3>New Project Form</h3>
<div class="form-group">
<label for="projectName">Project Name</label>
<input
type="text"
id="projectName"
class="form-control"
[formControlName]="'projectName'"
/>
<!-- if omit [] also omit ''-->
<span
*ngIf="
!newProjectForm.get('projectName').valid &&
newProjectForm.get('projectName').touched
"
>Please enter a project name</span
>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea
id="description"
class="form-control"
formControlName="description"
></textarea>
</div>
<div class="form-group">
<label for="assignedEmployees">Assign Employees</label>
<div class="larger-width-divider"></div>
<input type="text" class="search-box" placeholder="Search..." />
<select
name="employees"
class="form-control"
multiple
size="employees.length"
formControlName="assignedEmployees"
>
<ng-container *ngIf="newProjectForm.get('projectName').valid">
<option id="option" *ngFor="let employee of employees">{{
employee.userName
}}</option>
</ng-container>
</select>
<span
*ngIf="
!newProjectForm.get('assignedEmployees').valid &&
newProjectForm.get('assignedEmployees').touched
"
>Please select at least one employee</span
>
</div>
<div>
<button
type="button"
class="add-emp-btn"
*ngIf="!addEmployeeBtnClicked"
(click)="onEmpBtnClicked()"
[disabled]="newProjectForm.invalid"
>
Add Employees
</button>
<ng-container *ngIf="addEmployeeBtnClicked" formArrayName="employees">
<div *ngFor="let employeesControl of getControls(); let i = index">
<label for="userName">Insert one or multiple employee</label>
<input type="text" [formControlName]="i" />
<div class="width-divider"></div>
<button
type="button"
class="goto-add-emp-btn"
(click)="onEmpBtnClicked()"
>
Cancel
</button>
<div>
<span
class="last-span"
*ngIf="
!newProjectForm.get('employees').valid &&
newProjectForm.get('employees').touched
"
>
<span
*ngIf="newProjectForm.get('employees').errors?.nameIsForbidden"
>This name is invalid!</span
>
<span *ngIf="newProjectForm.get('employees').errors?.required"
>Add at least on employee</span
>
<span
*ngIf="newProjectForm.get('employees').errors?.usernameIsTaken"
></span>
</span>
</div>
</div>
</ng-container>
</div>
<div class="height-divider"></div>
<div class="alert-box-actions">
<button type="submit" class="submit" [disabled]="!newProjectForm.valid">
Create
</button>
<div class="width-divider"></div>
<button type="button" (click)="onFormAlertClose()">Cancel</button>
</div>
</form>
</div>
And here's the whole class:
import { Component, Output, OnInit } from "#angular/core";
import { Subject, Observable } from "rxjs";
import { ProjectService } from "src/app/services/project.service";
import { FormGroup, FormControl, Validators, FormArray } from "#angular/forms";
import { IEmployee } from "../entities/employee";
import { EmployeeService } from "src/app/services/employee.service";
#Component({
selector: "app-alert",
templateUrl: "./new-project-form-alert.component.html",
styleUrls: ["./new-project-form-alert.component.css"],
})
export class AlertComponent implements OnInit {
closeNewProjectAlert: Subject<boolean> = new Subject<boolean>();
employees: IEmployee[];
newProjectForm: FormGroup;
addEmployeeBtnClicked = false;
forbiddenUsernames = [
"Admin",
"Manager",
"Developer",
"Guest",
"admin",
"manager",
"developer",
"guest",
];
constructor(
private projectService: ProjectService,
private employeeService: EmployeeService
) {}
ngOnInit() {
this.employeeService.getEmployeeList().subscribe((employees) => {
this.employees = employees;
});
this.newProjectForm = new FormGroup({
// properties are string so when the files is minified they are not destroyed
projectName: new FormControl(null, Validators.required),
description: new FormControl(null),
assignedEmployees: new FormControl(null, [Validators.required]),
employees: new FormArray(
[],
[this.forbidUsernames.bind(this)],
this.takenUsernames
),
});
}
onFormAlertClose() {
this.projectService.closeNewProjectForm$.next(true);
}
onSubmit() {
console.log(this.newProjectForm);
this.newProjectForm.reset();
this.onFormAlertClose();
console.log((<FormArray>this.newProjectForm.get("employees")).controls);
}
onEmpBtnClicked() {
if (!this.addEmployeeBtnClicked) {
this.addEmployeeBtnClicked = true;
const control = new FormControl(null, Validators.required);
(<FormArray>this.newProjectForm.get("employees")).push(control);
} else {
this.addEmployeeBtnClicked = false;
(<FormArray>this.newProjectForm.get("employees")).removeAt(0);
}
}
forbidUsernames(control: FormControl): { [s: string]: boolean } {
for (let employee of control.value) {
if (this.forbiddenUsernames.indexOf(employee) !== -1) {
return { nameIsForbidden: true };
}
}
return null; // this if username is valid, do not return false
}
takenUsernames(control: FormControl): Promise<any> | Observable<any> {
const promise = new Promise<any>((resolve, reject) => {
const observable = this.employeeService.getEmployeeList().subscribe((employees) => {
this.employees = employees;
for (let employee of control.value) {
if (this.employees.indexOf(employee) !== -1) {
resolve({ usernameIsTaken: true });
} else {
resolve(null);
}
}
});
});
return promise;
}
getControls() {
return (<FormArray>this.newProjectForm.get("employees")).controls;
}
}
Sorry i know it a big mess, it's my first web app.
Edit: i provided a small video of the form if it helps: https://streamable.com/ua7mcq
Well yep, i have named the formArray as employees because when i submit the form the username or usernames get added to IEmployee objects and saved in the database.
But of the time of typing the usernames they are just string and i was comparing these strings with the employee objects.
So here's it fixed:
takenUsernames(control: FormControl): Promise<any> | Observable<any> {
const promise = new Promise<any>((resolve, reject) => {
const observable = this.employeeService.getEmployeeList().subscribe((employees) => {
this.employees = employees;
const usernames = this.employees.map(employee => employee.userName)
console.log(usernames)
for (let employee of control.value) {
if (usernames.indexOf(employee) !== -1) {
resolve({ usernameIsTaken: true });
} else {
resolve(null);
}
}
});
});
return promise;
}
But now i will reduce the input box to be able to add just one employee at time, wasted too much time on these component.
I am creating a form where a user select date and fill the form and subimit the form, here is what I have . for reference I am using igx-calendar
calendar component.ts:
import { Component, OnInit, forwardRef, Input, ElementRef, ViewChild } from '#angular/core';
import { IgxCalendarComponent, IgxDialogComponent } from 'igniteui-angular';
import { NgModel, FormControl, ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '#angular/forms';
#Component({
selector: 'app-calendar',
templateUrl: './calendar.component.html',
styleUrls: ['./calendar.component.scss'],
providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CalendarComponent), multi: true }
]
})
export class CalendarComponent implements ControlValueAccessor, OnInit {
#ViewChild('calendar') public calendar: IgxCalendarComponent;
#ViewChild('alert') public dialog: IgxDialogComponent;
#Input()
label: string;
private _theDate: string;
constructor() { }
propagateChange = (_: any) => { };
onTouched: any = () => { };
writeValue(obj: any): void {
console.log('writeValue => obj : ', obj);
if (obj) {
this._theDate = obj;
}
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
console.log('registerOnChange => fn : ', fn);
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
console.log('registerOnTouched => fn : ', fn);
}
get theDate() {
console.log('get theDate()');
return this._theDate;
}
set theDate(val) {
console.log('set theDate(val) - val => ', val);
this._theDate = val;
this.propagateChange(val);
}
public verifyRange(dates: Date[]) {
if (dates.length > 5) {
this.calendar.selectDate(dates[0]);
this.dialog.open();
}
}
ngOnInit() {
}
}
calender.html
<div class="sample-wrapper">
<div class="sample-content">
<!-- Single selection mode -->
<article class="sample-column calendar-wrapper">
<igx-calendar></igx-calendar>
</article>
</div>
</div>
Booking.component.ts UPDATE
export class BookingComponent implements OnInit {
comments: {};
addcomments: Comment[];
angForm: FormGroup;
// tslint:disable-next-line:max-line-length
validEmail = false;
constructor(private flashMessages: FlashMessagesService,
private fb: FormBuilder,
private activeRouter: ActivatedRoute,
private moviesService: MoviesService) {
this.comments = [];
this.createForm();
}
onChange(newValue) {
// tslint:disable-next-line:max-line-length
const validEmail = /^(([^<>()\[\]\\.,;:\s#"]+(\.[^<>()\[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (validEmail.test(newValue)) {
this.validEmail = true;
} else {
this.validEmail = false;
}
}
createForm() {
this.angForm = this.fb.group({
email: new FormControl('', [Validators.required, Validators.email])
});
}
addReview(date, email, city, hotel) {
this.moviesService.addReview(date, email, city, hotel).subscribe(success => {
this.flashMessages.show('You are data we succesfully submitted', { cssClass: 'alert-success', timeout: 3000 });
// get the id
this.activeRouter.params.subscribe((params) => {
// tslint:disable-next-line:prefer-const
let id = params['id'];
this.moviesService.getComments(id)
.subscribe(comments => {
console.log(comments);
this.comments = comments;
});
});
}, error => {
this.flashMessages.show('Something went wrong', { cssClass: 'alert-danger', timeout: 3000 });
});
}
ngOnInit() {
}
}
Booking.component.html
<div class="row about-booking">
<flash-messages></flash-messages>
<form [formGroup]="angForm" class="form-element">
<div class="col-sm-4 offset-sm-2 about-booking_calendar">
<div class="form-group form-element_date">
<app-calendar formControlName="date" [(ngModel)]="theDate" #date></app-calendar>
</div>
</div>
<div class="col-sm-4 about-booking_form">
<div class="form-group form-element_email">
<input type="email" class="form-control info" placeholder="Email" formControlName="email" #email (ngModelChange)="onChange($event)">
</div>
<div *ngIf="angForm.controls['email'].invalid && (angForm.controls['email'].dirty || angForm.controls['email'].touched)"
class="alert alert-danger">
<div *ngIf="angForm.controls['email'].errors.required">
Email is required.
</div>
</div>
<div class="input-group mb-3 form-element_city">
<select class="custom-select" id="inputGroupSelect01" #cityName>
<option selected *ngFor="let city of cities" [ngValue]="city.name">{{city.name}}</option>
</select>
</div>
<div class="input-group mb-3 form-element_hotel">
<select class="custom-select" id="inputGroupSelect01" #hotelName>
<option selected *ngFor="let hotel of hotels" [ngValue]="hotel.name">{{hotel.name}}</option>
</select>
</div>
<div class="form-group">
<button type="submit" (click)="addReview(date.value, email.value, cityName.value , hotelName.value)" class="btn btn-primary btn-block form-element_btn"
[disabled]="!validEmail">Book</button>
</div>
</div>
</form>
</div>
when I submit the data I get the following error:
BookingComponent.html:59 ERROR Error: Cannot find control with name:
'date'
what is wrong with my code?
in your createForm() function you didn't add the date FormControl
createForm() {
this.angForm = this.fb.group({
email: new FormControl('', [Validators.required, Validators.email]),
date: new FormControl('') // this line missing in your code
});
}
you shouldn't use both ngModel (template driven forms) and formControlName (reactive forms) in the same input.
In my case I was getting this same error for all the fields in the FormGroup, the reason was the data from server was NOT received and the html UI code attempted to executed the reactive form and hence the error. So to solve this issue I had to use the *ngIf condition on the form element and stopped the rendering of the form until the server response was completed.
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()" *ngIf="userProfile != undefined">
I'm not sure what's missing here. When the site initially loads and I try to submit an empty form, validation kicks in the validation result correctly indicates valid == false. I then attempt to validate again, anywhere on the site, and validation always returns valid == true. If I refresh the browser, again it works the first time but not afterward.
Inspecting my ValidationController instance (this.validation) on the initial attempt, bindings is filled in. On Subsequent attempts, bindings is empty.
Here is an example of how my validation is set up.
create.ts
import { autoinject} from "aurelia-framework";
import { ValidationController, ValidationRules } from "aurelia-validation";
import { DefaultValidationControllerFactory } from "../../lib/validation/default-validation-controller-factory";
import { Customer } from "../../lib/models/Customer";
#autoinject
export class CreateForm {
private validation: ValidationController;
public customer = new Customer();
constructor(validationControllerFactory: DefaultValidationControllerFactory) {
this.validation = validationControllerFactory.createForCurrentScope();
}
public attached() {
ValidationRules
.ensure((o: Customer) => o.firstName)
.required()
.ensure((o: Customer) => o.lastName)
.required()
.ensure((o: Customer) => o.facebookName)
.required()
.ensure((o: Customer) => o.state)
.required()
.on(this.customer);
}
public createCustomer() {
this.isBusy = true;
return this.validation
.validate()
.then(result => {
if (result.valid) {
// ...
}
})
.finally(() => {
this.isBusy = false;
});
}
}
create.html
<template>
<div busy.bind="isBusy" class="form content-area">
<div class="row">
<div class="col-xs-12 col-sm-6">
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label>First Name</label>
<input value.bind="customer.firstName & validate" type="text" class="form-control" />
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label>Last Name</label>
<input value.bind="customer.lastName & validate" type="text" class="form-control" />
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label>Email Address</label>
<input value.bind="customer.emailAddress & validate" type="text" class="form-control" />
</div>
</div>
</div>
</div>
</div>
</div>
</template>
default-validation-controller-factory.ts
import { autoinject } from "aurelia-framework";
import { Validator, ValidationController, ValidationControllerFactory, ValidationRules } from "aurelia-validation";
import { DefaultValidationRenderer } from "./default-validation-renderer";
#autoinject
export class DefaultValidationControllerFactory {
private validationControllerFactory: ValidationControllerFactory;
private validationRenderer: DefaultValidationRenderer;
constructor(validationControllerFactory: ValidationControllerFactory, validationRenderer: DefaultValidationRenderer) {
this.validationControllerFactory = validationControllerFactory;
this.validationRenderer = validationRenderer;
}
public create(validator?: Validator): ValidationController {
let validationController = this.validationControllerFactory.create(validator);
this.setDefaults(validationController);
return validationController;
}
public createForCurrentScope(validator?: Validator): ValidationController {
let validationController = this.validationControllerFactory.createForCurrentScope(validator);
this.setDefaults(validationController);
return validationController;
}
private setDefaults(validationController: ValidationController) {
validationController.addRenderer(this.validationRenderer);
}
}
default-validation-renderer.ts
import { inject } from "aurelia-dependency-injection";
import { ValidationRenderer, RenderInstruction } from "aurelia-validation";
const ValidationErrorClassName = "validation-error";
#inject(Element)
export class DefaultValidationRenderer implements ValidationRenderer {
private boundaryElement: HTMLElement;
constructor(boundaryElement: HTMLElement) {
this.boundaryElement = boundaryElement;
}
public render(instruction: RenderInstruction) {
for (let item of instruction.unrender) {
for (let target of item.elements) {
let messages = target.parentElement.getElementsByClassName(ValidationErrorClassName);
for (let i = 0; i < messages.length; i++) {
let message = messages[i];
target.parentElement.removeChild(message);
}
}
}
for (let item of instruction.render.filter(o => !o.result.valid)) {
for (let target of item.elements) {
let message = target.ownerDocument.createElement("div");
message.classList.add(ValidationErrorClassName);
message.innerHTML = item.result.message;
target.parentNode.insertBefore(message, target.nextSibling);
}
}
}
}
The problem is your DefaultValidationControllerFactory. Replace it for:
export class DefaultValidationControllerFactory {
public static get(container: Container) {
return new DefaultValidationControllerFactory(container);
}
constructor(private container: Container) { }
public create(validator?: Validator) {
if (!validator) {
validator = this.container.get(Validator) as Validator;
}
return new ValidationController(validator);
}
public createForCurrentScope(validator?: Validator) {
const controller = this.create(validator);
controller.addRenderer(new DefaultValidationRenderer());
this.container.registerInstance(ValidationController, controller);
return controller;
}
}
(DefaultValidationControllerFactory as any)['protocol:aurelia:resolver'] = true;