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.
Related
This is my error
core.js:6479 ERROR TypeError: Cannot read property 'get' of null
at FormGroupDirective.addControl (forms.js:5346)
at FormControlName._setUpControl (forms.js:5929)
at FormControlName.ngOnChanges (forms.js:5874)
at FormControlName.rememberChangeHistoryAndInvokeOnChangesHook (core.js:1498)
at callHook (core.js:2536)
at callHooks (core.js:2495)
at executeInitAndCheckHooks (core.js:2446)
at selectIndexInternal (core.js:8447)
at Module.ɵɵadvance (core.js:8430)
at SignInComponent_Template (sign-in.component.html:36)
I don't know why and where am I going wrong.
This is my HTML file
<form (formGroup)="signInForm">
<mat-form-field class="full-width">
<mat-label>Email</mat-label>
<input matInput placeholder="Email" name="email" formControlName="email" [(ngModel)]="email">
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>Password</mat-label>
<input matInput placeholder="Password" name="password" formControlName="pass" [(ngModel)]="pass">
</mat-form-field>
<button
(click)="signIn()"
mat-raised-button
color="primary"
class="login-button">
Sign In
</button>
</form>
<form [formGroup]="signUpForm">
<mat-form-field class="full-width">
<mat-label>Username</mat-label>
<input matInput placeholder="Full name" name="name" formControlName="name" [(ngModel)]="name">
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>E-mail</mat-label>
<input matInput placeholder="Email" name="email" formControlName="email" [(ngModel)]="email">
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>Contact</mat-label>
<input matInput placeholder="Contact" name="contact" formControlName="contact" [(ngModel)]="contact">
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>Address</mat-label>
<input matInput placeholder="Address" name="address" formControlName="address" [(ngModel)]="address">
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>Password</mat-label>
<input matInput placeholder="Password" name="pass" formControlName="pass" [(ngModel)]="pass">
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>Confirm Password</mat-label>
<input matInput placeholder="Confirm Password" name="conpass" formControlName="conpass" [(ngModel)]="conpass">
</mat-form-field>
<!-- <label class="example-margin">Type of User:</label>
<mat-radio-group>
<mat-radio-button class="example-margin" value="after">Buyer</mat-radio-button>
<mat-radio-button class="example-margin" value="before">Seller</mat-radio-button>
</mat-radio-group> -->
<mat-form-field appearance="fill">
<mat-label>Choose one</mat-label>
<mat-select [formControl]="selected" [errorStateMatcher]="matcher" name="option" id="option">
<mat-option value="none">please select</mat-option>
<mat-option value="valid">Buyer</mat-option>
<mat-option value="invalid">Seller</mat-option>
</mat-select>
</mat-form-field>
<button (click)="signUp()"
mat-raised-button
color="primary"
class="login-button">
Sign Up
</button>
</form>
And this is my TS file
import { NumberInput } from '#angular/cdk/coercion';
import { Component, OnInit } from '#angular/core';
import { FormControl, Validators, FormGroupDirective, NgForm, FormGroup, FormBuilder } from '#angular/forms';
import {ErrorStateMatcher} from '#angular/material/core';
export class MyErrorStateMatcher implements ErrorStateMatcher {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const isSubmitted = form && form.submitted;
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
}
}
#Component({
selector: 'app-sign-in',
templateUrl: './sign-in.component.html',
styleUrls: ['./sign-in.component.css']
})
export class SignInComponent implements OnInit {
email:String="";
name:String="";
contact:NumberInput="";
address:String="";
pass:String="";
conpass:String="";
constructor( private fb : FormBuilder) { }
signUpForm=this.fb.group ({
name : new FormControl(Validators.required),
email : [null,[Validators.email , Validators.required]],
contact :[null,[Validators.required]],
address : [null,[Validators.required]],
pass : [null,[Validators.required]],
conpass : [null,[Validators.required]]
});
signInForm=this.fb.group({
email : [null,[Validators.email , Validators.required]],
pass : [null,[Validators.required]]
});
selected = new FormControl('valid', [
Validators.required,
Validators.pattern('valid'),
]);
selectFormControl = new FormControl('valid', [
Validators.required,
Validators.pattern('valid'),
]);
nativeSelectFormControl = new FormControl('valid', [
Validators.required,
Validators.pattern('valid'),
]);
matcher = new MyErrorStateMatcher();
// favoriteSeason: string;
// seasons: string[] = ['Winter', 'Spring', 'Summer', 'Autumn'];
// onLogin(form : NgForm)
// {
// console.log(form.value)
// }
// onSingup(form : NgForm)
// {
// console.log(form.value)
// }
ngOnInit(): void {
}
signUp(){
if(this.signUpForm.valid || (this.signUpForm.controls.pass.value != this.signUpForm.controls.conpass.value))
{
console.log("Invalid Form!")
return;
}
console.log(JSON.stringify(this.signUpForm.value))
}
signIn(){
if(this.signUpForm.valid)
{
console.log("Invalid Form!")
return;
}
console.log(JSON.stringify(this.signUpForm.value))
}
}
I thought that not mentioning ngModel would be an issue first but even after adding that the same error again and again, when I run the code it my GUI is totally destroyed and disrupted showing the above error.
Please help me with this, stuck since forever here.
Your signInForm form group directive should be in a square bracket.
(formGroup)="signInForm"
It should be like,
[formGroup]="signInForm"
I have a phonenumber field which has country list dropdown and input field for the user to enter phonenumber. Now once the user select the country from dropdown I'm showing the country code in input field in input field using mat prefix. Which I'm able to do successfully.
Myform = this.fb.group({
Details: this.fb.array([this.createdetails()])
});
createdetails(): FormGroup {
return this.fb.group({
name: ['', Validators.required ]
phone: ['', Validators.required]
});
}
switchCountryForPhoneNumber(event) {
if(event.value){ this.countrySelected = "+" + event.value.phoneCode;
}
else { this.countrySelected = '';
}
}
Now user can add more set of details(Name and number) by clicking add more. Now the problem I'm facing if I click on add more and select country from dropdown in row1 it is showing even in row 2. Could you guys please advice. Please refer images
Dropdown ; Row1 selected but shown in row2 also
<div
formArrayName="Details"
*ngFor="let item of Details.controls; let i = index"
>
<div [formGroupName]="i">
<mat-form-field appearance="outline">
<mat-label class="required">
{{"Name"}}
</mat-label>
<input
formControlName="name"
class="mat-body-1"
matInput
/>
</mat-form-field>
<div class="phn-wrapper">
<mat-form-field
appearance="outline"
class="countryCode"
style="width: 25%"
>
<mat-label appearance="outline">Country</mat-label>
<mat-select
(selectionChange)="switchCountryForPhoneNumber($event)"
#countrySelect
>
<mat-select-trigger>
<span
[class]="
'flag flag-' +
countrySelect.value?.countryCode.toLowerCase()
"
></span>
</mat-select-trigger>
<mat-option [value]="null" show>Select Option</mat-option>
<mat-option *ngFor="let item of countryData" [value]="item">
<span
[class]="'flag flag-' + item.countryCode.toLowerCase()"
></span>
{{ item.countryName + ' (+' + item.phoneCode + ')' }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field
appearance="outline"
class="phone"
style="width: 75%"
>
<mat-label
appearance="outline"
>
</mat-label>
<span matPrefix>{{ countrySelected }}</span>
<input
formControlName="phone"
matInput
/>
</mat-form-field>
</div>
<div class="hr"></div>
</div>
</div>
<div
*ngIf="Details.value.length < 5"
class="add-fields"
(click)="addDetails()"
>
<mat-icon class="icon">add_circle_outline</mat-icon>
<span class="text mat-button">
{{ "ADD MORE Details"}}</span
>
</div>
addDetails() {
this.Details.push(this.createdetails());
}
I'm trying to build a website which provides the functionality of uploading your own courses.
Course Structure
Name of course
|-Module1
|-Lecture1
|-Lecture2
|-Module2
|-Lecture1
|-Lecture2
Using Angular I'm trying to create a dynamic form which will add/remove modules within the course and lecture within a module
So far, I have written the following -
course-upload.component.ts
export class CourseUploadComponent implements OnInit {
courseUploadForm: FormGroup;
constructor(private formBuilder: FormBuilder) { }
ngOnInit() {
this.courseUploadForm = this.formBuilder.group({
coursename: ['', Validators.required],
modules: this.formBuilder.array([
this.initModules()
])
})
}
initModules() {
return this.formBuilder.group({
modulename: ['', Validators.required],
lectures: this.formBuilder.array([
this.initLecture()
])
});
}
initLecture() {
return this.formBuilder.group({
lecturename: ['', Validators.required],
description: ['', Validators.required],
lecture: ['', Validators.required]
});
}
addModule() {
const control = <FormArray>this.courseUploadForm.get('modules');
control.push(this.initModules());
}
addLecture() {
const control = <FormArray>this.courseUploadForm.get('lectures');
control.push(this.initLecture());
}
removeModule(i: number) {
const control = <FormArray>this.courseUploadForm.get('modules');
control.removeAt(i);
}
removeLecture(i: number) {
const control = <FormArray>this.courseUploadForm.get('lectures');
control.removeAt(i);
}
getModulesControls(i: number) {
>>>> return [(this.courseUploadForm.controls.modules as FormArray).controls[i]['controls']];
}
getLecturesControls(i: number) {
return [(this.courseUploadForm.controls.lectures as FormArray).controls[i]['controls']];
}
}
course-upload.component.html
<form [formGroup]="courseUploadForm" novalidate>
<div formArrayName="modules">
<mat-card *ngFor="let module of courseUploadForm.get('modules').value; let i=index">
<mat-card-subtitle>
{{i+1}}
</mat-card-subtitle>
<div [formGroupName]="i">
<mat-form-field>
<mat-label>Module Name</mat-label>
**>>>** <input matInput placeholder="Module Name" formControlName="modulename">
<ng-container *ngFor="let control of getModulesControls(j)">
<mat-error *ngIf="!control.name.valid">Name Required</mat-error>
</ng-container>
</mat-form-field>
<div formArrayName="lectures">
<mat-card *ngFor="let lecture of module.get('lectures').value; let j=index">
<mat-card-subtitle>
Lecture {{i+1}}: {{lecture.name}}
</mat-card-subtitle>
<div [formGroupName]="j">
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput placeholder="Lecture Name" formControlName="lecturename">
<ng-container *ngFor="let control of getLecturesControls(j)">
<mat-error *ngIf="!control.name.valid">Name Required</mat-error>
</ng-container>
</mat-form-field>
<mat-form-field>
<mat-label>Description</mat-label>
<input matInput placeholder="Lecture Description" formControlName="description">
<ng-container *ngFor="let control of getLecturesControls(j)">
<mat-error *ngIf="!control.description.valid">Description Required</mat-error>
</ng-container>
</mat-form-field>
<mat-form-field>
<mat-label>Lecture</mat-label>
<input matInput placeholder="Lecture Video" formControlName="lecture">
<ng-container *ngFor="let control of getLecturesControls(j)">
<mat-error *ngIf="!control.lecture.valid">Lecture Video Required</mat-error>
</ng-container>
</mat-form-field>
<mat-card-actions>
<button mat-raised-button color="accent" (click)="addLecture()">Add Another
Lecture</button>
<button mat-raised-button color="warn"
*ngIf="module.get('lectures')['controls'].length > 1"
(click)="removeLecture(j)">Remove This Lecture</button>
</mat-card-actions>
</div>
</mat-card>
</div>
<mat-card-actions>
<button mat-raised-button color="accent" (click)="addModule()">Add Another Module</button>
<button mat-raised-button color="warn"
*ngIf="courseUploadForm.get('modules')['controls'].length > 1" (click)="removeModule(i)">Remove
This Module</button>
</mat-card-actions>
</div>
</mat-card>
</div>
</form>
I get the error:
Cannot read property 'controls' of undefined
at CourseUploadComponent.getModulesControls
at CourseUploadComponent_mat_card_2_Template
I've highlighted the lines that throw the error with ** > **
Some help?
j is not defined here it should have been smg different
<ng-container *ngFor="let control of getModulesControls(j)">
<mat-error *ngIf="!control.name.valid">Name Required</mat-error>
</ng-container>
I suppose you meant to use the variable i there: getModulesControls(i)
Also, line 5 of the HTML file the variable module is defined as an Object. Line 23 module.get('lectures') looks like you expect a FormGroup in the module variable. Take a look at this example from Angular docs. Pay attention to both HTML markup and TS. You will need to create several getters like get cities(): FormArray (:
I think you have to check your given array empty or not. If empty you should return null,
getModulesControls(i: number) {
if(this.courseUploadForm.controls.modules.length <1){
return null;
}
else{
return [(this.courseUploadForm.controls.modules as FormArray).controls[i]['controls']];
}
}
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);
}`
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.