Reactive form with dynamic data change from component - html

I am setting up a reactive form in angular 6, where I have 2 input boxes(one input is an optional entry) and a submit button. If I enter a value to one input box and press submit, I need to fill the other input box by setting corresponding value from component side. If I enter values to both input boxes, then another function is called. If so how is two-way data bindin possible in form controls? I tried using ngModel, which is not working as expected and from stackoverflow answers, came to know that using ngmodel with form controls is soon to be deprecated. How Can I achieve the same if so? Below is the code snippet I am using:
Component.ts:
export class myComponent implements OnInit {
converterForm: FormGroup;
model: myModel = new MyModel();
constructor(private formBuilder: FormBuilder, ) {
this.myForm = this.formBuilder.group({
vOne: [this.model.vOne],
vTwo: [this.model.vTwo],
});
}
onSubmit(searchInputs) {
this.model.vTwo= "new"; //I need to edit the form value and reflect it in html.. two-waybinding
this.converterForm.value.vOne = "edited";
console.log("Submit called");
}
}
html file:
<div>
<div>
<form [formGroup]="myForm" (ngSubmit)="onSubmit(myForm.value)">
<div>
<div>
<mat-form-field>
<input id="vOne" matInput formControlName="vOne" [(ngModel)]="model.vOne">
</mat-form-field>
</div>
<div>
<mat-form-field>
<input id="vTwo" matInput formControlName="vTwo" [(ngModel)]="model.vTwo">
</mat-form-field>
</div>
</div>
<div>
<button mat-raised-button color="primary" type="submit" (click)="search()">
<mat-icon aria-label="Search icon">search </mat-icon>
Search
</button>
</div>
</form>
</div>
thanks in advance.

Using valueChanges for access to live changes, and using setValue func for setting value per input.
in ts file try:
export class myComponent implements OnInit {
myForm: FormGroup;
constructor(private formBuilder: FormBuilder) {
this.myForm = this.formBuilder.group({
vOne: [null],
vTwo: [null],
});
searchHandler();
}
searchHandler() {
const searchInputs = {
vOne: '',
vTwo: '',
};
for (const propertyName in searchInputs) {
const property = this.form.get(propertyName);
property.valueChanges
.subscribe((searchText) => {
// this.form.controls.vOne.setValue('whatever');
// this.form.controls.vTwo.setValue('whatever');
// searchText is what keypress in input tag
});
}
}
onSubmit() {
// this.form.controls.vOne.setValue('whatever');
// this.form.controls.vTwo.setValue('whatever');
}
}
in html file try:
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
<div>
<mat-form-field>
<input matInput formControlName="vOne">
</mat-form-field>
</div>
<div>
<mat-form-field>
<input matInput formControlName="vTwo">
</mat-form-field>
</div>
</form>

Related

form fields in angular not displaying already populated data when trying to create an update function

I am creating a MEAN application that can perform CRUD operations, however when i am creating the update function the form fields are not populated with the already existing data to be updated.
HTML:
<button
mat-button
color="basic"
[routerLink]="['/moc-report', mocreport._id]"
>
Update Status
</button>
This is the button but to update but the form does not contain the data needed to update:
HTML of the form to contain the data to be updated:
<div class="container">
<!--Navbar-->
<mat-toolbar color="primary">
<div class="centerNavBar">
<a mat-button href="fa-dashboard">Back</a>
<span class="headSpacer">Damage Assessment Tool</span>
<a mat-button href="">Logout</a>
</div>
</mat-toolbar>
</div>
<div>
<mat-sidenav-container class="MainContainter">
<!--SideNav-->
<mat-sidenav mode="side" opened>
<div>
<a mat-button href="">Message Board</a>
</div>
</mat-sidenav>
<mat-sidenav-content class="MainContent">
<mat-card>
<mat-card-header class="sect">Create report:</mat-card-header>
<br /><br />
<mat-card-content class="centerAlign">
<!--Div for form-->
<div>
<form [formGroup]="form" (submit)="addMOCForm()">
<mat-form-field class="formwidth">
<input
matInput
class="form-control"
formControlName="MoCReportDateTime"
type="Date"
required
/>
</mat-form-field>
<br />
<mat-form-field>
<mat-label>Comment</mat-label>
<input
placeholder="--"
matInput
formControlName="MoCDescription"
class="form-control"
type="string"
required
/>
</mat-form-field>
<br />
<mat-form-field>
<mat-label>Facility In Question</mat-label>
<input
placeholder="--"
matInput
formControlName="facilityName"
class="form-control"
type="string"
required
/>
</mat-form-field>
<mat-card class="centerAlign">
<mat-card-actions>
<!--Div for buttons-->
<div>
<input
style="display: none"
#ImageInput
type="file"
(change)="onFileSelected($event)"
/>
<button
mat-raised-button
type="button"
(click)="ImageInput.click()"
>
Upload Images
</button>
<button mat-raised-button color="primary" type="submit">
Add
</button>
</div>
</mat-card-actions>
</mat-card>
</form>
</div>
</mat-card-content>
</mat-card>
</mat-sidenav-content>
</mat-sidenav-container>
</div>
TS:
import { AfterViewInit, Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import {
FormBuilder,
FormGroup,
FormControl,
} from '#angular/forms';
import { HttpClient } from '#angular/common/http';
import { MOCReportService } from 'src/app/service/mocreport.service';
#Component({
selector: 'app-moc-report',
templateUrl: './moc-report.component.html',
styleUrls: ['./moc-report.component.css'],
})
export class MocReportComponent implements OnInit {
image: any;
Image = [];
imageData: any;
constructor(
private mocreportservice: MOCReportService,
//private mapService: MocMapService,
private router: Router,
private fb: FormBuilder,
private http: HttpClient
) {}
form = new FormGroup({
facilityName: new FormControl(''),
MoCDescription: new FormControl(''),
MoCReportDateTime: new FormControl(''),
});
onFileSelected(event: any) {
const file = (event.target as HTMLInputElement).files;
this.form.patchValue({ Image: file });
const allowedMimeTypes = ['image/png', 'image/jpeg', 'image/jpg'];
{
const reader = new FileReader();
reader.onload = () => {
this.imageData = reader.result as string;
};
if (file) {
reader.readAsDataURL(file[0]);
}
}
console.log(event.target.files[0]);
const Image = event.target.files[0];
this.image = Image;
}
addMOCForm() {
console.log('adding');this.MoCReportDateTime, this.mocImage);
const formData = new FormData();
formData.append('facilityName', this.form.value.facilityName);
formData.append('MoCDescription', this.form.value.MoCDescription);
formData.append(
'MoCReportDateTimeString',
this.form.value.MoCReportDateTimeString
);
formData.append('mocImage', this.image);
this.mocreportservice.postMOCForm(formData).subscribe((d) => {
console.log(d);
});
this.router.navigate(['/message-board']);
}
ngOnInit(): void {}
}
your question is missing two things - 1.code to update the form values is missing. 2.what values you want to update in the form and where are they in the code snippet provided above.
Still I am providing you the solution
1.First update your html and add a click event which will be used for updating the form on click of button
HTML:
<button
mat-button
color="basic"
[routerLink]="['/moc-report', mocreport._id]"
(click)="onUpdateClik()">
Update Status
</button>
TS:
onUpdateClik(){
//I'm assuming you want to update the form values which you are getting in form from the template.
//If you are getting the form values from any API then append those values and you'll be done.
this.form.patchValue({
facilityName: this.form.value.facilityName, //update your respective values
MoCDescription: this.form.value.MoCDescription, //update your respective values
MoCReportDateTime: this.form.value.MoCReportDateTimeString, //update your respective values
})
}

ERROR TypeError: can't assign to property "validator" on "12345": not an object

I want to build a pre filled form, as of now I am getting this error when i try to display the form page. I have tried various solutions that was given but that doesn't seem to work here.
My hardwareRequest.ts
hardwareReviewForm: FormGroup;
assetSerialNumber = new FormControl('');
modelType = new FormControl('');
ngOnInit(): void {
this.data = []
this.service.loadRequestData(this.requestId).subscribe(
(response: any) => {
this.data = response
console.log(this.data)
this.assetSerialNumber = this.data.assetSerialNumber
this.modelType = this.data.modelType
},
(error) => {
}
)
}
My HTML file
<form class="example-container" [formGroup]="hardwareReviewForm" (ngSubmit)="onFormSubmit()">
<div class="ds-col-12" style="margin-bottom: 10px;">
<div class="ds-col-6">
<mat-form-field appearance="outline">
<mat-label>Asset Serial Number: </mat-label>
<input formControlName="assetSerialNumber" readonly matInput type="text">
</mat-form-field>
</div>
<div class="ds-col-6">
<mat-form-field appearance="outline">
<mat-label>Part No./Model Type: </mat-label>
<input formControlName="modelType" readonly matInput type="text">
</mat-form-field>
</div>
</div>
</form>
You can make a 'View Model' of your fields and then reset your form with it.
export class testVm {
assetSerialNumber : number;
modelType : string;
}
var model: testVm = this.data;
this.hardwareReviewForm.reset(model)
Or if you want to assign value separately you should act like this:
this.hardwareReviewForm.controls["assetSerialNumber"].reset(this.data.assetSerialNumber.toString())
Firstly, you have to define form controls inside formGroup;
hardwareReviewForm = new FormGroup({
assetSerialNumber = new FormControl('');
modelType = new FormControl('');
});
Secondly, you set the values to controls like below;
this.hardwareReviewForm.get('assetSerialNumber').setValue(this.data.assetSerialNumber);
I figured this out.. It was a silly mistake of .setValue for form controls.
setting my value like below works fine.
this.Number.setValue(this.data.Number)
this.Type.setValue(this.data.Type)

How to validate multiple preselected checkbox with angular 8 reactive form

I have few checkboxes whose values are coming from loop,Here I am validating those checkboxes using reactive form.My validation is atleast one checkboxes should be selected.when I check and uncheck the checkbox validation is working fine,but when my all checkboxes are already preselected and click submit,even though its showing empty message.Is there any solution for it.Here is the code below.
home.component.html
<div>
<p>Form 1</p>
<form [formGroup]="registerForm">
<div *ngFor="let grpdata of statusdata">
<input type="checkbox" formControlName="title" value="{{grpdata.groupid}}" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.title.errors }">{{grpdata.groupname}}<br>
</div>
<div *ngIf="submitted && f.title.errors" class="invalid-feedback">
<div *ngIf="f.title.errors.required">Title is required</div>
</div>
<button type="submit" (click)="getSelectedVal()">Click here</button>
</form>
</div>
<div>
<p>Form 2</p>
<form [formGroup]="editForm">
<input type="textbox" disabled formControlName="edithidden" [(ngModel)]="hello" class="form-control"><br>
<div *ngFor="let grpdata of statusdata">
<input type="checkbox" formControlName="edittitle" [checked]=true value="{{grpdata.groupid}}" class="form-control" [ngClass]="{ 'is-invalid': submitted1 && g.edittitle.errors }">{{grpdata.groupname}}<br>
</div>
<div *ngIf="submitted1 && g.edittitle.errors" class="invalid-feedback">
<div *ngIf="g.edittitle.errors.required">Title is required</div>
</div>
<button type="submit" (click)="editSelectedVal()">Click here</button>
</form>
</div>
home.component.ts
import { Component, OnInit } from '#angular/core';
import { CommonserviceService } from './../utilities/services/commonservice.service';
import { FormBuilder, FormControl, FormGroup, Validators } from '#angular/forms';
declare var $: any;
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
submitted = false;
submitted1 = false;
getListData: any;
registerForm: FormGroup;
editForm: FormGroup;
statusdata: any;
constructor(private commonserviceService: CommonserviceService,private formBuilder: FormBuilder)
{
this.registerForm = this.formBuilder.group({
title: [false, Validators.requiredTrue],
});
this.editForm = this.formBuilder.group({
edittitle: [false, Validators.requiredTrue],
edithidden: new FormControl()
});
}
ngOnInit() {
this.statusdata = [{"groupid":1,"groupname":"project1"},{"groupid":2,"groupname":"project2"},{"groupid":3,"groupname":"project3"}];
}
get f() { return this.registerForm.controls; }
get g() { return this.editForm.controls; }
getSelectedVal(){
this.submitted = true;
// stop here if form is invalid
if (this.registerForm.invalid) {
return;
}
console.log('submitted');
}
editSelectedVal(){
this.submitted1 = true;
// stop here if form is invalid
if (this.editForm.invalid) {
return;
}
console.log('submitted edit');
}
}
<input type="checkbox" formControlName="edittitle" [checked]=true...
You shouldn't try to set the value from outside of the form. You never know when it is actually attached. When you want to have the checkbox to be preselected use the form value instead.
this.editForm = this.formBuilder.group({
edittitle: [true, Validators.requiredTrue], // true here, you had false here
edithidden: new FormControl()
});

Angular material Modal dialog not able to pass event to parent?

I have a component which has one child component. Child component has a button which will open a material dialog Box.
In dialog we have form, username and passwrod and submit button. When I submit i am calling backend REST api.
this is getting called in child component:
dialogRef.afterClosed().subscribe(result => {
console.log("result", result);
this.onModalClosePassData.emit(result);//emit to parent
});
which is sending event to parent. updateComment() is getting called and I can see the data in console.
But when I fill the form and click on submit. It calls submitForm method which is asynchronus call and I am closing dialog after successful login.But then event is not emmiting. updateComment() is not getting called.
See the full code:
parent component.html
<ng-template #showTextOnly>
<child-component [branch]="releaseBranch" [date]="dateString"
(onModalClosePassData)="updateComment($event)">
</child-component>
</ng-template>
parent component.ts
//This method is getting called when i click on backDrop,
but If i logged in successfully this is not getting called
updateComment(event:any){
consile.log(event);
}
child-component.html
<button class="btn btn-default" (click)="openDialog()">Login</button>
child-component.ts
export class ChildComponent implements OnInit {
#Output() onModalClosePassData = new EventEmitter();
constructor(public dialog: MatDialog) { }
openDialog(): void {
const dialogConfig = new MatDialogConfig();
dialogConfig.disableClose = false;
dialogConfig.autoFocus = false;
dialogConfig.hasBackdrop= true;
dialogConfig.width = '300px';
dialogConfig.autoFocus=true;
dialogConfig.data = {branch: this.branch, date: this.date};
const dialogRef = this.dialog.open(LoginDialog, dialogConfig);
dialogRef.afterClosed().subscribe(result => {
console.log("result", result); //result is getting called in both cases
this.onModalClosePassData.emit(result);
});
}
}
LoginDialog.component.ts
import {MatDialogRef, MAT_DIALOG_DATA} from '#angular/material/dialog';
export class LoginDialog implements OnInit{
constructor(private loginService: LoginService, public dialogRef: MatDialogRef<LoginDialog>,
#Inject(MAT_DIALOG_DATA) public data: any) {}
public submitForm = (formValue: any) => {
if (this.noteForm.valid) {
let encryptData = btoa(`${formValue.username}:${formValue.password}`);
this.loginService.login(encryptData)
.subscribe((response:any)=>{
if(response.STATUS === "FAILED"){
} else {
this.dialogRef.close(this.noteDetail);
}
})
}
}
}
LoginDialog.component.html
<form [formGroup]="noteForm" autocomplete="off" novalidate (ngSubmit)="submitForm(noteForm.value)">
<mat-dialog-content class="mat-typography">
<mat-form-field>
<mat-label>User Name</mat-label>
<input matInput type="text" formControlName="username" id="username">
</mat-form-field>
<mat-form-field>
<mat-label>Password</mat-label>
<input matInput type="password" formControlName="password">
</mat-form-field>
</mat-dialog-content>
<mat-dialog-actions align="center">
<button mat-raised-button color="primary" [disabled]="!noteForm.valid">Submit</button>
</mat-dialog-actions>
</form>
I have faced same issue and figured it out, may be this is usefull for others.
We will get this issue when we used custom component on modal. For example, we have formComponent on pop up modal and on submit we need to close the modal and emit the form value, this should work but we can face the issue when our formComponent is destroyed before emitting the value.
This is because we opened our formComponent on Modal later on form submit we closed the modal which contains formComponent and opened success modal then trying to emit the value.
Solution is: Don't close modal which contains formComponent before emmiting the value or else use a service to trigger.

Angular Radio Buttons form with unknown number of elements

I am new to Angular and I have an issue with the radio buttons.
I have an array of strings which I want to use to create a form with radio buttons. I do not know the length or the content of the array - the values are taken from an external service.
How can I do this using a form builder in my Angular Component and in html file? Can it be something like this?
question-dto.ts
export class QuestionDto {
questionText: string;
questionOptions: string[];
}
quiz.component.ts
question: QuestionDto = new QuestionDto();
questionOptionsForm: any;
constructor(private quizService: QuizService,
private formBuilder: FormBuilder) { }
ngOnInit() {
this.initForm();
}
initForm() {
this.questionOptionsForm = this.formBuilder.group({
//INIT RADIO BUTTONS HERE
})
}
quiz.component.html
<div>
<p>
Answers
</p>
<form *ngIf="questionOptionsForm">
<div *ngFor="let option of question.questionOptions">
<label>
<input type="radio" class="form-control">
{{option}}
</label>
</div>
</form>
</div>
You should try FormArray https://alligator.io/angular/reactive-forms-formarray-dynamic-fields/
code would look like:
initForm() {
this.questionOptionsForm = this.formBuilder.group({
options: this.formBuilder.array([this.createOption()])
});
}
createOption():FormGroup {
return this.formBuilder.group({
name: '',
description: ''
});
}