How to dynamically add validations to material forms in Angular (v6)? - javascript

I have a form whose field names and required validators will change based on the form type (content_type) selected. On selecting the form_type, I get a list of required_fields and optional_fields, accordingly, I need to add Validators.required, so i can disable the submit button if the form is not filled properly.
After adding logic, i get following errors:
this.form._updateTreeValidity is not a function
and
this.form.get is not a function
and the form ends up looking empty with the following errors. If i just keep fields without any formControlName or validations it looks perfectly fine.
Code
Sample form fields
formFields = {
required_fields: {
actual_content: "Content",
name: "Name",
contact: "Phone Number"
},
optional_fields: {
additional_content: "Additional card content",
website: "Website URL",
}
};
form.component.ts
formGroup: FormGroup;
ngOnInit() {
this.selectedType = 'text';
this.service.getFormFields(this.selectedType)
.subscribe((data: {required_fields, optional_fields }) => {
// create array of field names to display in template
this.fieldNames = Object.assign(data.required_fields, data.optional_fields);
// create dynamic formGroup, with proper validators
this.formGroup = this.createGroup(data);
console.log({FormGroup: this.formGroup});
});
}
createGroup(formFields): FormGroup {
const group: any = {};
// required fields
const reqFields = Object.keys(formFields.required_fields);
reqFields.forEach(field => {
group[field] = new FormControl(``, [Validators.required]);
});
// optional fields
const optFields = Object.keys(formFields.optional_fields);
optFields.forEach(field => {
group[field] = new FormControl(``);
});
return group;
}
// the dynamically generated formGroup might look like
// formGroup = new FormGroup({
// actual_content: new FormControl(``, [Validators.required]),
// name: new FormControl(``, [Validators.required]),
// contact: new FormControl(``, [Validators.required]),
// additional_content: new FormControl(),
// website: new FormControl(),
// })
form.component.html
<form novalidate class="mainForm" [style.fontSize.px]="15" [formGroup]="formGroup" #myForm="ngForm">
<div>
<h3 class="sHeading"> Select Form Type </h3>
<mat-form-field appearance="outline" class="formField">
<mat-select placeholder="Select Form Type" formControlName="content_type">
<mat-option
#formType
class="option"
*ngFor="let type of formTypes"
[value]="type.type"
(click)="updateFormType(formType.value)">
{{type.type}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div>
<h3 class="sHeading"> {{fieldNames.name}} </h3>
<mat-form-field appearance="outline" class="formField">
<input matInput
placeholder="Name should have 3 characters min."
formControlName="name">
</mat-form-field>
</div>
<div>
<h3 class="sHeading"> {{fieldNames.content_url}} </h3>
<mat-form-field appearance="outline" class="formField">
<input matInput
placeholder="Provide Valid URL"
formControlName="content_url">
</mat-form-field>
</div>
<div>
<h3 class="sHeading"> {{fieldNames.actual_content}} </h3>
<mat-form-field appearance="outline" class="formField">
<textarea
matInput
placeholder="Describe the content"
rows=6
formControlName="actual_content"></textarea>
</mat-form-field>
</div>
<div>
<h3 class="sHeading"> {{fieldNames.additional_content}} </h3>
<mat-form-field appearance="outline" class="formField">
<textarea
matInput
placeholder="Describe the additional content"
rows=6
formControlName="additional_content"></textarea>
</mat-form-field>
</div>
<div>
<button mat-raised-button type="submit" class="submitBtn" [disabled]="!myForm.valid">Submit</button>
</div>
</form>
Question
What is the correct way to add validationss dynamically?
Thanks in advance.

Related

How to bind data to a mat form field in angular?

I have an http request that pulls that data and I am populating this data on the UI. Now when the user clicks edit a form will show . How do we assign and show the value on the fields when the edit button was clicked ? cause I wanna use that data to submit. The email , firstname etc should display on the field.
When I click edit the value of the form field should be the value from the getuserdetails like firstname , lastname etc like the sample below {{this.data.emailAddress}} but instead I want it to be value of the input field
#init
ngOnInit(): void {
this._activatedRoute.queryParams.subscribe((params) => {
this.userId = params.id;
this.getUserGeneralDetails();
});
}
#constructor
constructor(
private _activatedRoute: ActivatedRoute,
private _notificationService: NotificationService,
private _userProfileService: UserProfileService,
private _router: Router,
private fb: FormBuilder
) {
this.generalForm.disable();
this.securityForm.disable();
}
I only wanna show the data on the formfield when Edit is clicked.
generalForm = new FormGroup({
email: new FormControl(),
fname: new FormControl(),
lname: new FormControl(),
phone: new FormControl(),
companyName: new FormControl(),
title: new FormControl(),
});
Just new to angular guys badly wanted to know techniques and ideas. Thank you.
#template
<div class="card" #generalCard>
<span class="anchor" id="general"></span>
<div class="card-header">
<mat-icon class="card-icon">group</mat-icon>
<div class="title">GENERAL</div>
<div class="spacer"></div>
<button mat-button *ngIf="generalForm.disabled" (click)="generalForm.enable()">
<mat-icon>edit</mat-icon> Edit
</button>
<button mat-button *ngIf="generalForm.enabled" (click)="generalForm.disable()">
Cancel
</button>
<button mat-stroked-button *ngIf="generalForm.enabled" (click)="saveGeneralFormChanges()">
Save Changes
</button>
</div>
<div class="card-content" *ngIf="generalForm.disabled" >
<div class="line-item">
<div class="title">EMAIL</div>
<div class="detail">{{this.data.emailAddress}}</div>
<mat-icon class="active" style="color:#00B0DB;">check_circle</mat-icon>
</div>
<div class="line-item">
<div class="title">EMAIL</div>
<div class="detail">{{this.data.emailAddress}}</div>
<mat-icon>check_circle_outline</mat-icon>
<button mat-button class="detail active">Resend welcome email</button>
</div>
<div class="line-item">
<div class="title">FIRST NAME</div>
<div class="detail">{{this.data.firstName}}</div>
</div>
<div class="line-item">
<div class="title">LAST NAME</div>
<div class="detail">{{this.data.lastName}}</div>
</div>
<div class="line-item">
<div class="title">PHONE NUMBER</div>
<div class="detail">+1 {{this.data.phoneNumber}}</div>
</div>
<div class="line-item">
<div class="title">COMPANY NAME</div>
<div class="detail">{{this.data.companyName || 'None'}}</div>
</div>
<div class="line-item">
<div class="title">TITLE</div>
<div class="detail">{{this.data.title || 'None'}}</div>
</div>
</div>
#GetUserData
getUserGeneralDetails() {
this.isInProgress = true;
this._userProfileService
.getUserGeneralDetails(this.userId)
.pipe(finalize(() => (this.isInProgress = false)))
.subscribe({
next: (res) => {
if (res.isSuccess) {
this.data = res.data;
}
},
error: (err) => this._notificationService.showError(err),
complete: noop,
});
}
#field code
<div class="card-content" *ngIf="generalForm.enabled">
<form [formGroup]="generalForm" class="generalForm">
<mat-form-field appearance="fill" class="email">
<mat-label>Email</mat-label>
<input matInput formControlName="email" />
</mat-form-field>
<button mat-raised-button class="validateEmail">Validate email</button>
<mat-divider></mat-divider>
<mat-form-field appearance="fill" class="fname">
<mat-label>First Name</mat-label>
<input matInput formControlName="fname" />
</mat-form-field>
<mat-form-field appearance="fill" class="lname">
<mat-label>Last Name</mat-label>
<input matInput formControlName="lname" />
</mat-form-field>
<mat-form-field appearance="fill" class="phone">
<mat-label>Phone Number</mat-label>
<input matInput formControlName="phone" />
</mat-form-field>
<mat-form-field appearance="fill" class="companyName">
<mat-label>Company Name</mat-label>
<input matInput formControlName="companyName" />
</mat-form-field>
<mat-form-field appearance="fill" class="title">
<mat-label>Title</mat-label>
<input matInput formControlName="title" />
</mat-form-field>
</form>
</div>
</div>
Your HTML code looks like good and as per ur question you are facing issue in filling data into form controls. That can be done as follow:
constructor(private fb: FormBuilder) { }
fillUpData() {
this.generalForm = this.fb.group({
email: [this.data.email, [Validators.required]],
fname: [this.data.fname, [Validators.required]],
lname: [this.data.lname, [Validators.required]],
phone: this.data.phone,
companyName: this.data.companyName,
title: this.data.title
});
}
You need to call this method once you have data. So after your API call.
getUserGeneralDetails() {
this.isInProgress = true;
this._userProfileService
.getUserGeneralDetails(this.userId)
.pipe(finalize(() => (this.isInProgress = false)))
.subscribe({
next: (res) => {
if (res.isSuccess) {
this.data = res.data; // After this call function.
this.fillUpData();
}
},
error: (err) => this._notificationService.showError(err),
complete: noop,
});
}
After doing this if you get an error related to the form group then add condition like *ngIf="generalForm" in your HTML code... That means if form exist then render its formControl...

Chaining Tasks performed on NgOnInit and NgAfterViewInit

Guys I am seriously going nuts about handling Angular lifecycle hooks correctly. I am not sure where I am mistaking. I have tried to read a lot of things and is struggling with them from around a week now.
My Problem
I have quite a bit of server api calls and some business logic that I am applying during NgOnInIt(). But doing so I am sometimes running in to RegisterationComponent.html:82 ERROR TypeError: Cannot read property 'titleId' of undefined. And at other times I am struggling to initialize a wizard correctly.
I am now going to define you in detail what is it that I am facing:
ngOnInit()
ngOnInit() {
//First see if it is a redirect request:
this.route.queryParamMap
.pipe(
map(params => params.get('payment')),
filter(paramVal => paramVal != null),
filter(paramVal => paramVal == 'success'),
tap((paymentInd: string) => {
this.setOrderDetails();
}),
untilDestroyed(this)
).subscribe((successPayment: string) => {
//Update the transaction here
this.regService.updateTransaction(this.registrationTransactionDetails)
.pipe(
filter((regUpdate: RegistrationTransaction) => regUpdate != null),
tap((regUpdate: RegistrationTransaction) => {
//This is so that user sees the correct template.
this.showRegConfirmation = true;
this.showConfirmation = false;
this.wizard = new KTWizard(this.el.nativeElement, {
startStep: 2
});
this.initializeWizardEvents();
JsBarcode('#barcode', regUpdate.order.orderLine.barcode);
}),
untilDestroyed(this)
)
.subscribe((regUpdate: RegistrationTransaction) => {
//Update the user details
this._profileService.updateUser(this.userDetails.id, this.userDetails).subscribe();
});
});
//fresh transaction case
this.route.queryParamMap
.pipe(
map(params => params.get('payment')),
filter(param => param == null),
tap((paymentInd: string) => {
this.setOrderDetails();
//this is to make sure user sees the correct template even after refresh
this.showConfirmation = true;
this.showRegConfirmation = false;
}),
filter(id => this.registrationTransactionDetails.id == null),
untilDestroyed(this)
)
.subscribe((paymentInd: string) => {
this.regService
.createTransaction(this.registrationTransactionDetails)
.pipe(
filter(regCreated => regCreated.id != null),
tap((regCreated: RegistrationTransaction) => {
if(!this._utilities.isNullOrUndefined(regCreated.id)){
this.wizard = new KTWizard(this.el.nativeElement, {
startStep: 1
});
this.initializeWizardEvents();
this.regService
.getSessionDetails(regCreated.eventId, regCreated.order.id)
.pipe(
tap((response: SessionResponse) => {
//Just so that we can update the registration details with the gateway order id as well.
this.registrationTransactionDetails = JSON.parse(this.storage.getItem(Constants.RegistrationStorageKey));
this.registrationTransactionDetails.gatewayOrderId = response.gatewayOrderId;
this.storage.setItem(Constants.RegistrationStorageKey, JSON.stringify(this.registrationTransactionDetails));
}),
untilDestroyed(this)
)
.subscribe((response: SessionResponse) => {
console.log(response);
this.gatewayConfig.session = response.session;
this.gatewayConfig.callbacks = this.callback;
Checkout.configure(this.gatewayConfig);
});
}
}),
untilDestroyed(this)
)
.subscribe((regCreated: RegistrationTransaction) => {
this.registrationTransactionDetails = JSON.parse(this.storage.getItem(Constants.RegistrationStorageKey));
//Save all the details fo the registration created.
this.registrationTransactionDetails.id = regCreated.id;
this.registrationTransactionDetails.order.id = regCreated.order.id;
this.registrationTransactionDetails.order.orderLine.id = regCreated.order.orderLine.id;
this.storage.setItem(
Constants.RegistrationStorageKey,
JSON.stringify(this.registrationTransactionDetails)
);
});
});
this.regService
.getCommonValues()
.pipe(
filter(list => list != null),
tap((list: CommonLists) =>{
this.filteredCities = _.filter(list.cities, { countryCode: this.userDetails.countryCode });
}),
untilDestroyed(this)
)
.subscribe((lists: CommonLists) => {
this.countries = lists.countries;
this.titles = lists.titles;
this.genders = lists.genders;
this.cities = lists.cities;
this.jobFunctions = lists.jobFunctions;
this.jobTitles = lists.jobTitles;
});
}
ngAfterViewInit()
ngAfterViewInit(): void {
// Initialize form wizard
//let wizard;
if(this.showRegConfirmation && !this.showConfirmation)
{
this.wizard = new KTWizard(this.el.nativeElement, {
startStep: 3
});
}
else
{
this.wizard = new KTWizard(this.el.nativeElement, {
startStep: 2
});
}
// Validation before going to next page
this.wizard.on('beforeNext', wizardObj => {
// https://angular.io/guide/forms
// https://angular.io/guide/form-validation
// validate the form and use below function to stop the wizard's step
// wizardObj.stop();
this.onBeforeNext(wizardObj);
});
this.wizard.on('beforePrev', wizardObj => {
// https://angular.io/guide/forms
// https://angular.io/guide/form-validation
// validate the form and use below function to stop the wizard's step
// wizardObj.stop();
this.onBeforePrev(wizardObj);
});
// Change event
this.wizard.on('change', wizard => {
setTimeout(() => {
KTUtil.scrollTop();
}, 500);
});
}
Now this wizard has to be initialized in the ngAfterViewInit. I have tried to do it in the ngOnInit but it doesn't work. The events do not work.
Primarily the lines you all see in the queryParamsMap subscriptions are my attempt to achieve what I want to achieve. I want to start the wizard from a different step based on the state user land to the screen.
this.wizard = new KTWizard(this.el.nativeElement, {
startStep: 2
});
this.initializeWizardEvents();
Additionally you all can find this line:
this.setOrderDetails() <- it fetches the user details during the ngOnInit. It is run after the ngOnInit and hence I get some initialization errors during runtime undefined title error as pasted above. When data comes it fills the UI, but I don't quite understand how to get around with this error.
RegistrationComponent.html Portion throwing error
<!--begin: Form Wizard Step 2-->
<div class="kt-wizard-v3__content" data-ktwizard-type="step-content" data-ktwizard-state="current">
<div class="kt-form__section kt-form__section--first">
<!-- <ng-template #loggedIn> -->
<div class="kt-wizard-v3__form">
<div class="wizard-title-area">
<p class="para">
<span class="name">Hi {{ userDetails.firstName }}, </span>You have logged in using your social
account. Click here if this is not the correct information.
</p>
</div>
<div class="kt-separator kt-separator--border-2x separator-margin-top-0"></div>
<form #userForm="ngForm">
<div class="form-input-wrap">
<div class="row">
<div class="col-sm-2">
<div class="form-group">
<mat-form-field>
<mat-select
id="prefix"
name="prefix"
placeholder="prefix"
[(ngModel)]="userDetails.titleId"
name="userTitle"
id="userTitle"
required
>
<mat-option *ngFor="let title of titles" [value]="title.id">{{
title.description
}}</mat-option>
</mat-select>
</mat-form-field>
<!-- <div
*ngIf="userTitle.invalid && (userTitle.dirty || userTitle.touched)"
class="alert alert-danger"
>
<div *ngIf="userTitle.errors.required">
Please select a Title
</div>
</div> -->
</div>
</div>
<div class="col-sm-4">
<div class="form-group">
<mat-form-field>
<input
matInput
#input
maxlength="20"
placeholder="First Name"
required
[(ngModel)]="userDetails.firstName"
[ngModelOptions]="{ standalone: true }"
/>
</mat-form-field>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<mat-form-field>
<input
matInput
#input
maxlength="20"
placeholder="Last Name"
required
[(ngModel)]="userDetails.lastName"
[ngModelOptions]="{ standalone: true }"
/>
</mat-form-field>
</div>
</div>
</div>
</div>
<div class="form-input-wrap">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<mat-form-field>
<input
matInput
type="email"
#input
maxlength="20"
placeholder="email"
required
[(ngModel)]="userDetails.email"
[ngModelOptions]="{ standalone: true }"
/>
</mat-form-field>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<mat-form-field>
<mat-select
id="gender"
name="gender"
placeholder="gender"
[(ngModel)]="userDetails.genderId"
aria-required="true"
[ngModelOptions]="{ standalone: true }"
>
<mat-option *ngFor="let gender of genders" [value]="gender.id">{{
gender.alias
}}</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
</div>
</div>
<div class="form-input-wrap">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<mat-form-field>
<mat-label>Select your nationality</mat-label>
<mat-select
placeholder="nationality"
[(ngModel)]="userDetails.nationalityCode"
[ngModelOptions]="{ standalone: true }"
>
<mat-option *ngFor="let country of countries" [value]="country.code">{{
country.name
}}</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<mat-form-field>
<mat-label>Select the country of residence</mat-label>
<mat-select
placeholder="country"
[(ngModel)]="userDetails.countryCode"
[ngModelOptions]="{ standalone: true }"
(selectionChange)="onCountryChange()"
>
<mat-option *ngFor="let country of countries" [value]="country.code">{{
country.name
}}</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
</div>
</div>
<div class="form-input-wrap">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<mat-form-field>
<mat-label>Select a job function</mat-label>
<mat-select
matInput
id="userJobFunction"
name="userJobFunction"
placeholder="job function"
[(ngModel)]="userDetails.jobFunctionId"
required
#userJobFunction="ngModel"
>
<mat-option *ngFor="let function of jobFunctions" [value]="function.id">{{
function.name
}}</mat-option>
</mat-select>
<mat-error *ngIf="userJobFunction.hasError('required')">
Please select a job function.
</mat-error>
</mat-form-field>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<mat-form-field>
<mat-label>Select a job title</mat-label>
<mat-select
matInput
id="userJobTitle"
name="userJobTitle"
placeholder="job title"
[(ngModel)]="userDetails.jobTitleId"
required
#userJobTitle="ngModel"
>
<mat-option *ngFor="let jobTitle of jobTitles" [value]="jobTitle.id">{{
jobTitle.name
}}</mat-option>
</mat-select>
<mat-error *ngIf="userJobTitle.hasError('required')">
Please select a job title.
</mat-error>
</mat-form-field>
</div>
</div>
</div>
</div>
<div class="form-input-wrap">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<mat-form-field>
<input
matInput
type="text"
#input
maxlength="20"
placeholder="phone"
required
[(ngModel)]="userDetails.mobile"
[ngModelOptions]="{ standalone: true }"
/>
</mat-form-field>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<mat-form-field>
<mat-label>Select a city</mat-label>
<mat-select
matInput
id="userCity"
name="userCity"
placeholder="city"
[(ngModel)]="userDetails.city"
required
#userCity="ngModel"
>
<mat-option *ngFor="let city of filteredCities" [value]="city.id">
{{ city.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="userCity.hasError('required')">
Please select a city.
</mat-error>
</mat-form-field>
</div>
</div>
</div>
</div>
<div class="form-input-wrap">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<mat-form-field>
<input matInput [matDatepicker]="userDOB" [max]="maxDate" id="userDOB" name="userDOB" placeholder="Date of Birth" [(ngModel)]="userDetails.userDOB"
[ngModelOptions]="{ standalone: true }">
<mat-datepicker-toggle matSuffix [for]="userDOB"></mat-datepicker-toggle>
<mat-datepicker #userDOB disabled="false"></mat-datepicker>
</mat-form-field>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<mat-form-field>
<input
matInput
type="text"
#input
maxlength="20"
placeholder="company"
[(ngModel)]="registrationTransactionDetails.companyName"
[ngModelOptions]="{ standalone: true }"
/>
</mat-form-field>
</div>
</div>
</div>
</div>
</form>
</div>
<!-- </ng-template> -->
</div>
</div>
<!--end: Form Wizard Step 2-->
Any help will be appreciated. Thank you!
seens like a bad definition variable, in your template RegisterationComponent.htmlline 88, you should have something like variable.titleId, that variable in your .ts file should be define like this:
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class YourComponentName {
variable;
}
the problem is that when your component is loaded variable is undefined so you should define like an empty object variable: typing = {} so the html component will get and undefined intead a error, if you component is specting something different that undefined you sould define your variable like variable = {titleId: expectedValue}

AWS - Pull email template from S3 bucket using java/js

My question seems to be very similar to AWS - Using email template from S3 bucket except instead of Python, I am using java/springboot to send the email from AWS SES and using javascript/typescript for the front end. I'm able to send an email with a hardcoded subject and body. I want to be able to send an email with a template from my S3 bucket. My application has a list of the templates in the bucket. When selected, a preview is displayed. How can I send a selected template within my application?
# "send-email-templates.ts"
constructor(
private templateService: TemplateService,
private schoolService: SchoolService,
private hireStageService: HireStageService,
private emailSearchService: EmailSearchService,
private router: Router,
private _ngZone: NgZone,
private candidateService: CandidateService,
public dialog: MatDialog,
) { }
ngOnInit() {
this.getSchools();
this.getHireStages();
this.isPreviewOpen = false;
this.selectedTemplateName = null;
this.getAllTemplates();
}
triggerResize() {
this._ngZone.onStable.pipe(take(1)).subscribe(() => this.autosize.resizeToFitContent(true));
}
createNewTemp() {
this.router.navigate([`/create_template`])
}
// Send email template to multiple emails after search filter
sendEmail() {
this.dialog.open(DialogOverviewExampleDialog, {
width: '250px'
});
let candidateEmails = (<HTMLInputElement>document.getElementById("emailList")).value
let subject = this.sendForm.get('subjectForm').value
let body = "HARDCODED BODY"
this.candidateService.sendEmailWithoutPositions(candidateEmails, subject, body).subscribe(() => {
this.router.navigate(['/send_email']);
});
}
searchCandidates() {
this.emailSearchService.searchCandidates(this.searchForm.get('namesForm').value,
this.searchForm.get('majorsForm').value, this.schools.toString(),
this.hireStages.toString(), this.searchForm.get('startDateForm').value,
this.searchForm.get('endDateForm').value)
.subscribe(listOfEmails => {
console.log(listOfEmails);
(<HTMLInputElement>document.getElementById('emailList')).value = <string> listOfEmails;
})
}
//Send email to new candidate
sendEmail(candidateEmail: string, positionId: number[], subject: string, body: string) {
let details:emailDetails = new emailDetails();
details.to = candidateEmail;
details.positionId = positionId;
details.subject = subject;
details.body = body;
return this.http.post<emailDetails>(`${this.context.getOutgoingEmailUrl()}`, details)
}
// Send email to string of search/filtered candidate emails
sendEmailWithoutPositions(candidateEmails: string, subject: string, body: string) {
let details:emailDetails2 = new emailDetails2();
details.to = candidateEmails;
details.subject = subject;
details.body = body;
return this.http.post<emailDetails2>(`${this.context.getOutgoingEmailUrl()}`, details)
}
HTML
<mat-grid-tile colspan="6" rowspan="4">
<div class='search'>
<form [formGroup]='searchForm' autocomplete='off' novalidate>
<mat-form-field class="nameSearch">
<span matPrefix>Name: </span>
<input matInput id='namesForm' placeholder=" Enter candidate name" formControlName='namesForm'>
</mat-form-field>
<mat-form-field class="majorSearch">
<span matPrefix>Major: </span>
<input matInput id='majorsForm' placeholder=" Enter candidate major" formControlName="majorsForm">
</mat-form-field>
<tr>
<td>
<mat-form-field *ngIf="schoolSource" class="schools">
<mat-label>Schools</mat-label>
<mat-chip-list #chipList>
<mat-chip *ngFor="let school of schools" [selectable]="true" [removable]="true"
(removed)="removeSchool(school)">
{{ school }}
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
</mat-chip>
<input input='schoolsForm' placeholder="Choose school(s)" #schoolInput [formControl]="schoolCtrl"
[matAutocomplete]="auto" [matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="addOnBlur" formControlName='schoolsForm'>
</mat-chip-list>
<mat-autocomplete #auto (optionSelected)="selectedSchool($event)">
<mat-option *ngFor="let school of schoolSource" [value]='schoolId'>
{{ school.schoolName }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
</td>
<td>
<mat-form-field *ngIf="hireStageSource" class="hireStages">
<mat-label>Hire Stage</mat-label>
<mat-chip-list #chipList1>
<mat-chip *ngFor="let hireStage of hireStages" [selectable]="true" [removable]="true"
(removed)="removeStage(hireStage)">
{{ hireStage }}
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
</mat-chip>
<input id='stagesForm' placeholder="Choose Hire Stage(s)" #hireStageInput [formControl]="hireStageCtrl"
[matAutocomplete]="auto1" [matChipInputFor]="chipList1"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="addOnBlur" formControlName='stagesForm'>
</mat-chip-list>
<mat-autocomplete #auto1 (optionSelected)="selectedStage($event)">
<mat-option *ngFor="let hireStage of hireStageSource" [value]='stageId'>
{{ hireStage.stageName }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
<tr>
<div class="dates">
<mat-form-field class="startDate">
<input matInput id='startDateForm' [matDatepicker]="picker" placeholder="Start date"
name="startDate" formControlName='startDateForm'>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
<mat-form-field class="endDate">
<input matInput id='endDateForm'[matDatepicker]="picker2" placeholder="End date"
name="endDate" formControlName='endDateForm'>
<mat-datepicker-toggle matSuffix [for]="picker2"></mat-datepicker-toggle>
<mat-datepicker #picker2></mat-datepicker>
</mat-form-field>
<button class="addEmail" mat-raised-button color = "primary" (click) = "searchCandidates()">Add</button>
</div>
</tr>
</td>
</form>
</div>
</mat-grid-tile>
<mat-grid-tile colspan="4" rowspan="4">
<div class='send'>
<form [formGroup]='sendForm' autocomplete='off' novalidate name="form">
<tr>
<button class="newTemplateButt" mat-raised-button color = "primary" (click) = "createNewTemp()">Create New Template</button>
</tr>
<tr>
<mat-form-field class="sendToList">
<mat-label>Candidate Emails will be listed here</mat-label>
<textarea id = 'emailList'
matInput
cdkTextareaAutosize
#autosize = "cdkTextareaAutosize"
cdkAutosizeMinRows = "1"
cdkAutosizeMaxRows = "8"></textarea>
</mat-form-field>
</tr>
<tr>
<mat-form-field class = "subjectLine">
<span matPrefix>Subject: </span>
<input matInput id='subjectLine' formControlName='subjectForm'>
</mat-form-field>
</tr>
<button mat-raised-button color = "primary" class = "sendEmail" (click) = "sendEmail()">Send Email</button>
</form>
</div>
</mat-grid-tile>
</mat-grid-list>
<div class="email-templates" [style.display]="isPreviewOpen ? 'grid' : 'flex'">
<app-email-templates-list [templates]="templateList" [selectedTemplate]="selectedTemplateName" (display)="displayPreviewHandler($event)"></app-email-templates-list>```
<app-email-templates-preview [previewStatus]="isPreviewOpen" [selectedTemplate]="selectedTemplateName"></app-email-templates-preview>
</div>
sendEmail() {
this.dialog.open(DialogOverviewExampleDialog, {
width: '250px'
});
let candidateEmails = (<HTMLInputElement>document.getElementById("emailList")).value
let subject = this.sendForm.get('subjectForm').value
console.log(this.selectedTemplateName)
this.templateService.getTemplate(this.selectedTemplateName).subscribe((templateData : any) => {
console.log(templateData.Body)
console.log(templateData.Body.data)
let body = importTemplate(templateData.Body.data).toString();
console.log(body)
this.candidateService.sendEmailWithoutPositions(candidateEmails, subject, body).subscribe(() => {
this.router.navigate(['/send_email']);
});
})
}
This is now sending the templates, but images are failing to display. Still looking into that issue.
Edit: The images weren't sending because they were base64 encoded. When viewing the source of all images in google images search results, they all showed base64 encoding. I noticed that when I went to the source of these images, it would give me a more specific src such as https://......jpg. When I dragged and dropped this source image into my template and saved and sent, it forwent the base64 encoding and therefore, images show up in my Gmail inbox when sent from my application now.

Form control problem with angular Material

I have this message appear in my console when I want edit my user.
I see some solution on the web, Some people have trouble with it but no solution for me.
Does someone have the same problem ?
EditUserDialogComponent.html:37 ERROR Error:
ngModel cannot be used to register form controls with a parent formGroup directive. Try using
formGroup's partner directive "formControlName" instead. Example:
<div [formGroup]="myGroup">
<input formControlName="firstName">
</div>
In your class:
this.myGroup = new FormGroup({
firstName: new FormControl()
});
Or, if you'd like to avoid registering this form control, indicate that it's standalone in ngModelOptions:
Example:
<div [formGroup]="myGroup">
<input formControlName="firstName">
<input [(ngModel)]="showMoreControls" [ngModelOptions]="{standalone: true}">
</div>
at Function.modelParentException (forms.js:6182)
at NgModel._checkParentType (forms.js:6754)
at NgModel._checkForErrors (forms.js:6740)
at NgModel.ngOnChanges (forms.js:6640)
at checkAndUpdateDirectiveInline (core.js:31905)
at checkAndUpdateNodeInline (core.js:44366)
at checkAndUpdateNode (core.js:44305)
at debugCheckAndUpdateNode (core.js:45327)
at debugCheckDirectivesFn (core.js:45270)
at Object.eval [as updateDirectives] (EditUserDialogComponent.html:42)
and this is my HTML,
I think it's a problem about the form control, but if I follow the instructions of the error message,
I have the same...
<div class="manage-content">
<div class="title">
<mat-icon class="user-icon">how_to_reg</mat-icon>
<h3>Edit a user</h3>
</div>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<mat-form-field class="full-width-input">
<input id="firstName" matInput placeholder="First name" formControlName="f_name" [(ngModel)]="selectedUser.f_name"#f_name>
<mat-error *ngIf="isFieldInvalid('f_name')">
The first name you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field class="full-width-input">
<input id="middleName" matInput placeholder="Middle name" formControlName="m_name" [(ngModel)]="selectedUser.m_name" #m_name>
<mat-error *ngIf="isFieldInvalid('m_name')">
The middle name you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field class="full-width-input">
<input id="lastName" matInput placeholder="Last name" formControlName="l_name" [(ngModel)]="selectedUser.l_name" #l_name>
<mat-error *ngIf="isFieldInvalid('l_name')">
The last name you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field class="full-width-input">
<input id="email" matInput placeholder="Email" formControlName="email" [(ngModel)]="selectedUser.email" #email>
<mat-error *ngIf="isFieldInvalid('email')">
The email you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field class="full-width-input">
<div class="visibility">
<input id="password" matInput type="password" placeholder="Password" formControlName="password" [(ngModel)]="selectedUser.password">
<mat-icon *ngIf="isPasswordVisible" (click)=showPassword()>visibility</mat-icon>
<mat-icon *ngIf="!isPasswordVisible" (click)="showPassword()">visibility_off</mat-icon>
</div>
<mat-error *ngIf="isFieldInvalid('password')">
The password you've entered is malformed.
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-select placeholder="Role" class="full-width-input" [(ngModel)]="selectedUser.role">
<mat-option value="option">End-User</mat-option>
<mat-option value="option">View-User</mat-option>
<mat-option value="option">Vendor</mat-option>
<mat-option value="option">Tool Guy</mat-option>
</mat-select>
</mat-form-field>
<div class="cta-btn">
<button mat-raised-button class="createUserBtn" color="primary" type="submit">Update user</button>
<button mat-raised-button class="createUserBtn" color="warn" type="submit" (click)="click()">Cancel</button>
</div>
</form>
</div>
#Component({
selector: 'app-edit-user-dialog',
templateUrl: 'edit-user-dialog.component.html',
styleUrls: ['./manage-user.component.scss']
})
export class EditUserDialogComponent {
form: FormGroup;
formSubmitAttempt: boolean;
isPasswordVisible = false;
selectedUser: User;
constructor(private formBuilder: FormBuilder, private userService: UserService, public dialog: MatDialog, public dialogRef: MatDialogRef<EditUserDialogComponent>,
#Inject(MAT_DIALOG_DATA) public data: any) {
this.selectedUser = data;
console.log(this.selectedUser);
this.form = this.formBuilder.group({
email: ['', Validators.compose([Validators.required, Validators.pattern('^[a-zA-Z0-9_.+-]+#[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$')])],
password: ['', Validators.compose([Validators.required, Validators.minLength(10), Validators.maxLength(35)])],
f_name: ['', Validators.compose([Validators.required, Validators.maxLength(100)])],
m_name: ['', Validators.compose([Validators.maxLength(100), Validators.pattern('[0-9]+')])],
l_name: ['', Validators.compose([Validators.required, Validators.maxLength(100)])]
// client: ['', Validators.compose([Validators.required, Validators.minLength(10), Validators.maxLength(35)])],
});
}
click(): void {
this.dialogRef.close();
}
showPassword() {
const pass: HTMLInputElement = document.getElementById('password') as HTMLInputElement;
if (pass.type === "password") {
this.isPasswordVisible = true;
pass.type = "text";
} else {
this.isPasswordVisible = false;
pass.type = "password";
}
}
isFieldInvalid(field: string) {
return (
(!this.form.get(field).valid && this.form.get(field).touched) ||
(this.form.get(field).untouched && this.formSubmitAttempt)
);
}
onSubmit() {
// if (this.form.valid) {
// this.authService.login(this.form.value);
// }
this.formSubmitAttempt = true;
}
}
thanks a lot for your help
The issue you are having is the usage of the ([ngModel]) inside of a formGroup.
If you need to extract a value, you will have to do it via the formGroup variable on the TS component.
Check out more information on the Angular support page: https://angular.io/guide/reactive-forms
change this:
`<input id="password" matInput type="password" placeholder="Password" formControlName="password" [(ngModel)]="selectedUser.password">`
into this:
`<input id="password" matInput type="password" placeholder="Password" formControlName="password" (change)="selectedUserChanged($event)">`
//and add to your ts
`selectedUserChanged(change: any) {
this.group.get('password').setValue(change.value);
}`

Angular 6 form returns validation error after submit and reset

I'm using angular 6 and I have a form and a button. When I press the button the app shows the form data above the form and I call form.reset(). But after form reset the input fields become red because I set the fields required in my form. Where is the problem?
app.html
<form [formGroup]='fuelForm'>
<mat-form-field class="input input2">
<input matInput placeholder="Nozzle name" formControlName="nozzleName">
</mat-form-field>
<mat-form-field class="input input2">
<input matInput type="number" min="1" id="nozzleNumber" formControlName="nozzleNumber" placeholder="Nozzle number" >
</mat-form-field>
<mat-form-field class="input input4">
<mat-select placeholder="Fuel type" formControlName="fuelType">
<mat-option *ngFor="let item of fuelList" [value]="item">{{item}}</mat-option>
</mat-select>
</mat-form-field>
<button mat-icon-button class="circle_button" (click)="add()">
<mat-icon class="plus_icon">add_circle_outline</mat-icon>
</button>
</form>
app.ts
export class DialogNozzleComponent {
fuelForm :FormGroup;
fuelList = ['Petrol', 'Super petrol', 'Euro4 petrol', 'Gasoline', 'Euro4 gasoline'];
nozzleItem = [
{
nozzleName: 'Nozzle',
nozzleNumber: '1',
fuelType: 'Super petrol'
},
{
nozzleName: 'Nozzle',
nozzleNumber: '2',
fuelType: 'Gasoline'
}
];
constructor(public fb : FormBuilder) {
this.fuelForm = fb.group({
nozzleName: {value:'Nozzle', disabled: true},
nozzleNumber: [null, Validators.required],
fuelType: [null, Validators.required]
});
}
add() {
const formValue = this.fuelForm.value;
formValue.nozzleName = this.fuelForm.controls['nozzleName'].value;
this.nozzleItem.push(formValue);
this.fuelForm.controls['nozzleNumber'].reset();
this.fuelForm.controls['fuelType'].reset();
}
}
Have you tried
this.fuelForm.reset();
this.fuelForm.markAsPristine();

Categories