Reputation: 5576
I have a form , which require a user to select a date and enter an email , city, and hotel , and the user must be able to submit the data.
Here is what I have:
about.component.html (form):
<div class="container about-booking-input">
<div class="row about-title">
<div class="col-md-7 about-title_bookingtitle">
<h2>Booking</h2>
</div>
</div>
<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" name="date" #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>
</div>
about.component.ts
export class AboutComponent 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]),
date: new FormControl('') // this line missing in your code
});
}
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() {
}
}
here is calendar.ts
import { Component, OnInit, forwardRef, Input, ElementRef, ViewChild } from '@angular/core';
import { IgxCalendarComponent, IgxDialogComponent } from 'igniteui-angular';
import { 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;
@Input()
isConsultation: boolean;
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() {
}
}
When the form is submited I get the following error date = undefined.
what is wrong with my codes? please I need your help
Upvotes: 0
Views: 2523
Reputation: 123
It looks like you have @input for the calendar but no @output. Angular components do not have two way data binding and would require you to have an output for your calendar component otherwise your parent class that calls app-calendar will always appear as if the date passed in is undefined.
So in your code,
html, add the output, I just named it outputDate but could be whatever you want.
app-calendar formControlName="date" outputDate="returnedDate" name="date" #date></app-calendar>
component ts, add a string that will be passed into the calendar component. Use that everywhere you get date. I named it returnedDate
export class AboutComponent implements OnInit {
comments: {};
addcomments: Comment[];
angForm: FormGroup;
// tslint:disable-next-line:max-line-length
validEmail = false;
returnedDate: string;
and in app Calander ts, replace _theDate with outputDate. That will set the outputDate instead which is accessible to the parent scope.
export class CalendarComponent implements ControlValueAccessor, OnInit
@ViewChild('calendar') public calendar: IgxCalendarComponent;
@ViewChild('alert') public dialog: IgxDialogComponent;
@Output()
outputDate: string;
Upvotes: 1