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">
Related
I need to fill in a field of a form builder using the zxing-js library, I am able to read the qrcode and insert this data into the input, but when submitting the field it is blank, if the insertion is from the keyboard everything goes perfectly. .. follow the code snippets
my form:
<form [formGroup]="formulario" (ngSubmit)="submit()" action="#">
<div class="divEtiqueta">
<mat-form-field class="inputEtq">
<input
formControlName="numRomaneio"
matInput
[(ngModel)]="romaneio.id"
name="name"
placeholder="Nº Romaneio"
/>
</mat-form-field>
<mat-form-field class="inputEtq">
<input
matInput
id="codEtiqueta"
name="codEtiqueta"
formControlName="codEtiqueta"
placeholder="Cód. Etiqueta"
/>
</mat-form-field>
</div>
<section class="container" id="demo-content">
<div>
<video
id="video"
width="95%"
height="300"
style="border: 1px solid gray"
></video>
</div>
<div class="buttonConfirmar">
<button
type="submit"
class="btnConfirmar"
id="confirmarButton"
mat-raised-button
onclick="location.href='#'"
>Confirmar
</button>
</div>
</section>
</form>
my TS:
formulario!: FormGroup;
romaneio: Romaneio = {
id: 0,
status: ''
}
idRomaneio!: number;
numcarga!: number;
id!: string;
itens!: Item[];
displayedColumns = ['cod_fab', 'descricao', 'qac', 'qc', 'faltam']
constructor(private qrcodeapiService: QrcodeapiService, private fb: FormBuilder, private router: Router, private route: ActivatedRoute) { }
ngOnInit(): void {
this.formulario = this.fb.group({
codEtiqueta: ['', Validators.required],
numRomaneio: ['', Validators.required]
})
this.romaneio.id = this.idRomaneio;
}
buscarItens(){
this.qrcodeapiService.listar(this.romaneio.id).subscribe(itens =>{
this.itens = itens
console.log(itens)
})
}
submit(){
const formValues = this.formulario.value;
const etiqueta: Etiqueta = new Etiqueta(formValues.codEtiqueta, formValues.numRomaneio);
this.qrcodeapiService.salvar(etiqueta).subscribe(etiqueta =>{
this.qrcodeapiService.showMessage('Registrado')
this.formulario = this.fb.group ({
codEtiqueta: ['', Validators.required],
numRomaneio: this.romaneio.id
})
this.buscarItens();
});
}
and the script for read QrCode:
function decodeOnce(codeReader, selectedDeviceId) {
codeReader
.decodeFromInputVideoDevice(selectedDeviceId, "video")
.then((result) => {
console.log(result);
document.getElementById("codEtiqueta").value = result.text;
})
.catch((err) => {
console.error(err);
});
}
from scan:
input scan
payload scan
From keyboard:
input keyboard
payload keyboard
Using the #zxing/ngx-scanner package I used it in the (trimmed down) code as below, hope this helps.
html:
<p-card>
<zxing-scanner [enable]=showScanner
[formats]=allowedFormats
(scanSuccess)="onScanSucces($event)"
></zxing-scanner>
<label for="first_scan">First Scan</label>
<input id="first_scan" type="text"
[value]="firstScanNumber"
required autofocus disabled>
</p-card>
TS:
export class ScanComponent implements OnInit {
firstScanNumber: string = "";
allowedFormats = [ BarcodeFormat.QR_CODE ];
showScanner: boolean = false;
constructor() { }
ngOnInit(): void {
this.showScanner = true;
}
onScanSucces($event: any){
console.log($event)
this.firstScanNumber = $event;
this.showScanner = false;
}
}
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 have created an angular 7 app that has the ability to add and remove the controls dynamically. However, I am not sure how to bind the data to the form.
In the code attached below, I am trying to add and remove websites. I figured out the reason why the data was not getting bound to the UI elements. Websites under FirmDetails model is an array of objects. At the moment for testing, I have added websiteUrl: FirmDetails.Websites[0].WEBSITE_URL. What if I have more than one object. How do I bind then?
UI
<div class="form-group row">
<label for="inputEmail" class="col-md-1 col-form-label header">Websites</label>
<div class="col-md-9">
<div class="form-group row">
<div class="col-md-3">
<label for="inputEmail">Website URL</label>
</div>
<div class="col-md-3">
<label for="inputEmail">User Name</label>
</div>
<div class="col-md-3">
<label for="inputEmail">Password</label>
</div>
</div>
<div formArrayName="websites"
*ngFor="let item of frmFirm.get('websites').controls; let i = index; let last = last">
<div [formGroupName]="i">
<div class="form-group row">
<div class="col-md-3">
<input style="width:100%" formControlName="websiteUrl"
placeholder="Website Url">
</div>
<div class="col-md-3">
<input style="width:100%" formControlName="username" placeholder="User Name">
</div>
<div class="col-md-3">
<input style="width:100%" formControlName="password" placeholder="Password">
</div>
<div class="col-md-3">
<button (click)="removeWebsite()">Remove Website</button>
</div>
</div>
</div>
</div>
<button (click)="addWebsite()">Add Website</button>
</div>
</div>
Component
import { Component, Injectable, NgZone, ViewEncapsulation, ViewChild, Input } from '#angular/core';
import { OnInit } from '#angular/core';
import { FirmService } from '../services/firm.service';
import * as ClassicEditor from '#ckeditor/ckeditor5-build-classic';
import { CommonDataService } from '../services/common.data.service';
import { FormGroup, FormControl, FormBuilder, FormArray } from '#angular/forms';
import { ListItem } from '../models/listItem';
#Component({
selector: 'mgr-firm',
templateUrl: './firm.component.html'
})
export class FirmComponent implements OnInit {
private Error: string;
public FirmDetails: any;
public EditMode: boolean;
public Editor = ClassicEditor;
public EditorConfig: string;
public events: string[] = [];
#Input() FirmId: number;
DateFoundedDate: Date;
public frmFirm: FormGroup;
constructor(private _fb: FormBuilder, private firmService: FirmService, private commonDataService: CommonDataService) {
}
ngOnInit() {
this.initializeFormModel();
this.getFirmDetails();
}
initializeFormModel() {
this.frmFirm = this._fb.group({
firmName: [''],
shortName: [''],
alternateName: [''],
dateFounded: [''],
intraLinks: this._fb.array([
this.createCredentials()
]),
firmHistory: [''],
People: [''],
websites: this._fb.array([
this.createWebsite()
]),
addressess: this._fb.array([
this.createAddress()
])
});
}
// public addWebsite(): void {
// const websites = this.frmFirm.get('websites') as FormArray;
// websites.push(this._fb.control(''));
// }
public addWebsite(): void {
this.websites.push(this.createWebsite());
}
public removeWebsite(index: number): void {
const websites = this.frmFirm.get('websites') as FormArray;
websites.removeAt(index);
}
private createWebsite(): FormGroup {
return this._fb.group({
websiteUrl: [''],
username: [''],
password: ['']
});
}
public addAddress(): void {
this.addressess.push(this.createAddress());
}
public removeAddress(index: number): void {
const addressess = this.frmFirm.get('addressess') as FormArray;
addressess.removeAt(index);
}
private createAddress(): FormGroup {
return this._fb.group({
city: [''],
street: [''],
line2: [''],
line3: [''],
zipCode: [''],
phone: ['']
});
}
public addCredentials(): void {
this.intraLinks.push(this.createCredentials());
}
public removeCredentials(index: number): void {
const intraLinks = this.frmFirm.get('intraLinks') as FormArray;
intraLinks.removeAt(index);
}
private createCredentials(): FormGroup {
return this._fb.group({
intraUsername: [''],
intraPassword: ['']
});
}
get websites(): FormArray {
return <FormArray>this.frmFirm.get('websites');
}
get addressess(): FormArray {
return <FormArray>this.frmFirm.get('addressess');
}
get intraLinks(): FormArray {
return <FormArray>this.frmFirm.get('intraLinks');
}
get cities(): ListItem[] {
return JSON.parse(this.FirmDetails.LongCitiesJson).map(x => new ListItem(x.CITY_ID, x.CITY_NAME, null));
}
setFormValues(FirmDetails: any) {
this.frmFirm.patchValue({
firmName: FirmDetails.Firm.NAME,
shortName: FirmDetails.Firm.SHORT_NAME,
alternateName: FirmDetails.Firm.ALTERNATE_NAME,
dateFounded: this.getDate(FirmDetails.Firm.DATE_FOUNDED),
firmHistory: FirmDetails.Firm.HISTORY_HTML,
People: FirmDetails.People
});
const websiteGroup = this._fb.group({
// websiteUrl: FirmDetails.Websites[0].WEBSITE_URL,
// username: FirmDetails.Websites[0].USERNAME,
// password: FirmDetails.Websites[0].PASSWORD
websites: FirmDetails.Websites
});
this.frmFirm.setControl('websites', this._fb.array([websiteGroup]));
const addressGroup = this._fb.group({
city: this.FirmDetails.LongCitiesJson,
addressess: FirmDetails.Addresses
// street: FirmDetails.Firm.Addresses[0].LINE1,
// line2: FirmDetails.Firm.Addresses[0].LINE2,
// line3: FirmDetails.Firm.Addresses[0].LINE3,
// zipCode: FirmDetails.Firm.Addresses[0].POSTAL_CODE,
// phone: FirmDetails.Firm.Addresses[0].SWITCHBOARD_INT
});
this.frmFirm.setControl('addressess', this._fb.array([addressGroup]));
const intraLinksGroup = this._fb.group({
intraUsername: FirmDetails.Intralinks[2].USERNAME,
intraPassword: FirmDetails.Intralinks[2].PASSWORD
});
this.frmFirm.setControl('intraLinks', this._fb.array([intraLinksGroup]));
}
getFirmDetails() {
if (this.FirmId != null) {
this.firmService.getFirmDetails(this.FirmId)
.subscribe(data => {
this.FirmDetails = data;
this.setFormValues(this.FirmDetails);
},
err => {
this.Error = 'An error has occurred. Please contact BSG';
},
() => {
});
}
}
get dateFoundedDate(): string {
const dateString = this.FirmDetails.Firm.DATE_FOUNDED;
const results =parseInt(dateString.replace(/\/Date\(([0-9]+)[^+]\//i, "$1"));
const date = new Date(results);
const month = date.toLocaleString('en-us', { month: 'long' });
return (month + '-' + date.getFullYear());
}
private getDate(dateFounded: string): Date {
const results =parseInt(dateFounded.replace(/\/Date\(([0-9]+)[^+]\//i, "$1"));
const date = new Date(results);
return new Date (date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate());
}
saveManager() {
this.firmService.createFirm(this.FirmDetails)
.subscribe(data => {
this.getFirmDetails();
this.EditMode = !this.EditMode;
},
err => {
this.Error = 'An error has occurred. Please contact BSG';
},
() => {
});
}
dateFoundedChanged(dateFoundedDate: Date) {
this.DateFoundedDate = dateFoundedDate;
}
}
You can bind the array of values using patchValue like this,
setFormValues(FirmDetails: any) {
this.frmFirm.patchValue({
firmName: FirmDetails.Firm.NAME,
shortName: FirmDetails.Firm.SHORT_NAME,
alternateName: FirmDetails.Firm.ALTERNATE_NAME,
dateFounded: FirmDetails.Firm.DATE_FOUNDED,
firmHistory: FirmDetails.Firm.HISTORY_HTML,
People: FirmDetails.People,
websites: FirmDetails.Websites
});
}
since, the FirmDetails.Websites value is an array the value will get binded if you do like the above method
First you have to invoke the initializeFormModel() function and then
you have to invoke getFirmDetails() function in the ngOnInit() so
that the form gets initialized first and the value gets binded.
patchValue can also be done for form array
I have a form which contain a calendar allowing users to select a specific date ,
Here is the form :
about.component.html
<form [formGroup]="angForm" class="form-element">
<div class="col-sm-4 offset-sm-2 about-booking_calendar">
<div class="form-group form-element_date">
<igx-calendar [value]="date"></igx-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 (change)="changeSelect(cityName.value)" formControlName='city'>
<option selected *ngFor="let city of cities" [ngValue]="city.cityName">{{city.cityName}}</option>
</select>
</div>
<div class="input-group mb-3 form-element_hotel">
<select class="custom-select" id="inputGroupSelect01" #hotelName formControlName='hotel'>
<option selected *ngFor="let hotel of hotels" [ngValue]="hotel.hotelName">{{hotel.hotelName}}</option>
</select>
</div>
<div class="form-group">
<button type="submit" (click)="addReview(date, email.value, cityName.value , hotelName.value)" class="btn btn-primary btn-block form-element_btn"
[disabled]="!validEmail">Book</button>
</div>
</div>
</form>
here is about.component.ts
import { Component, OnInit } from '#angular/core';
import { MoviesService } from '../movies.service';
import { FormGroup, FormBuilder, Validators, FormControl } from '#angular/forms';
import { ActivatedRoute } from '#angular/router';
import { FlashMessagesService } from 'angular2-flash-messages';
#Component({
selector: 'app-about',
templateUrl: './about.component.html',
styleUrls: ['./about.component.scss']
})
export class AboutComponent implements OnInit {
comments: {};
addcomments: Comment[];
angForm: FormGroup;
// tslint:disable-next-line:max-line-length
validEmail = false;
cities = [
{
cityName: 'Berlin',
},
{
cityName: 'Oslo',
},
{
cityName: 'Warsaw',
},
{
cityName: 'Paris',
}
];
hotels: Array<any> = [
{
cityName: 'Oslo',
hotelName: 'Sheraton Hotel',
},
{
cityName: 'Berlin',
hotelName: 'grand hotel',
},
{
cityName: 'Warsaw',
hotelName: 'Hiltom hotel',
},
{
cityName: 'Paris',
hotelName: 'Africanskiej hotel',
},
];
public date: Date = new Date(Date.now());
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]),
date: new FormControl(''), // this line missing in your code
city: this.cities[0].cityName,
hotel: null
});
}
changeSelect(event) {
const ret = this.hotels.find(data => data.cityName.toString() === event.split(': ')[1].toString());
this.angForm.get('hotel').setValue(ret.hotelName);
}
addReview(date, email, cityName, hotelName) {
this.moviesService.addReview(date, email, cityName, hotelName).subscribe(() => {
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;
});
});
}, () => {
this.flashMessages.show('Something went wrong', { cssClass: 'alert-danger', timeout: 3000 });
});
}
ngOnInit() {
}
}
The calendar shiuld look something like this
I want past dates in calendar to be disabled, You may assume that the list of “forbidden” days will be supplied by the server. You may suggest Json format and the data structure.
Also highlight dates that are full booked eg 19 july 2018 is full so user cant be able to book the resevertion.
Note: am using this calendar : igx-calendar
Can some please help me out? am just learning
Hey #Kaczka pojebana I dont know whether this date picker accept directive min or not.
Otherwise you can use:
HTML:
<input type="date" [min] = "minDate" fromControlName="date">
TS:
minDate: any= new Date();
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;