ReactiveForm for dynamically updated form entries - html

How to check status of myform in component to check if all the fields are filled or not?
HTML:
<div [formGroup]="myform">
<div *ngFor="let question of questions">
<label>{{question.question}}</label>
<select required >
<option selected disabled="disabled">Option</option>
<option *ngFor="let option of question['options']">{{option}}</option>
</select>
</div>
</div>
</div>
Question JSON coming from API:
this.questions = [
{
question: 'What is your age?',
options: ['20', '30', '40']
},
{
question: 'How much quantity?',
options: ['1','2','3']
}]

If you use ReactiveForm, you need use a FormArray
A FormArray can be of FormControl or a FormGroup
FormArray of FormControls
constructor(private fb:FormBuilder) {}
ngOnInit() {
//We create an array of FormControl, each question a FormControl
let data:FormControl[]=this.questions.map(q=>new FormControl());
this.myform=this.fb.group({
questions:new FormArray(data)
})
}
//the .html
<!--we use *ngIf to show the form only when we create the form-->
<div *ngIf="myform" [formGroup]="myform">
<!--we iterate to myForm.get('questions').controls -->
<!--we use our variable "questions" to show the label and options-->
<div *ngFor="let question of myform.get('questions').controls;let i=index">
<label>{{questions[i].question}}</label>
<select required [formControl]="question" >
<option value="null" disabled="disabled">Option</option>
<option *ngFor="let option of questions[i].options">{{option}}</option>
</select>
</div>
</div>
<!--just for check-->
{{myform?.value |json}}
If we use an array of formGroup we change some things
constructor(private fb:FormBuilder) {}
ngOnInit() {
//we create and array of FormGroup
let data2:FormGroup[]=this.questions.map(q=>this.fb.group({
option:null
}));
this.myform2=this.fb.group({
questions:new FormArray(data2)
})
}
<div *ngIf="myform2" [formGroup]="myform2">
<!--see that we say to Angular the "formArrayName" -->
<div formArrayName="questions">
<div *ngFor="let question of myform2.get('questions').controls;
let i=index" [formGroupName]="i"> <!--don't forget formGroupName-->
<label>{{questions[i].question}}</label>
<!--the select use formControlName, our array is an array of FormGroup-->
<select required formControlName="option" >
<option value="null" disabled="disabled">Option</option>
<option *ngFor="let option of questions[i].options">{{option}}</option>
</select>
</div>
</div>
</div>
{{myform2?.value |json}}
Aclaration:#FrontEndDeveloper. One thing is the array question that we use to make the questions.(Perhafs I must be choose other names to the variables), other thing is the value of the form. The value of myform1={questions:["20","1"]}, the value of myform2={questions:[{option:"20"},{option:"2"}]}.
When we create an array of FormControl (or an array of FbGroup) I used map, equally I can do some like
let data:FormControl[]=[];
data.push(new FormControl());
data.push(new FormControl());
or
let data2:FormGroup[]=[];
data2.push(this.fb.group({
option:null
}));
data2.push(this.fb.group({
option:null
}));
Generally we have some data to initialize the form. (an object with some data) that we get from a dbs
//Imagine we have mydata{name:"A",options=["20","1"]}
//we can map this data to create the form
let data:FormControl[]=this.mydata.options.map(q=>new FormControl(q));
//or
let data2:FormGroup[]=this.mydata.options.map(q=>this.fb.group({
option:q
}));
//Imagine we have mydata{name:"A",options=[{option:"20"},{option:"1"}]}
//we can map this data to create the form
let data:FormControl[]=this.mydata.options.map(q=>new FormControl(q.option));
//or
let data2:FormGroup[]=this.mydata.options.map(q=>this.fb.group({
option:q.option
}));

This would help you to understand reactive form basic functionalities.
https://stackblitz.com/edit/objects-equality-check-edhyk5?file=src/app/app.component.ts
It will help to understand:
1. FormBulder,
2. FormGroup,
3. Form Value changes etc.

Related

Creating reactive form with array of objects value in Angular

I need to create form in Angular.
SampleJson={
[{type:'text',name:'firstname', value:'Test'},
{type:'text',name:'lastname', value:'Test'},
{type:'mail',name:'email', value:'Test'}],[{type:'text',name:'firstname', value:'Test'},
{type:'text',name:'lastname', value:'Test'},
{type:'mail',name:'email', value:'Test'}]}
ngOninit(){
this.form=this.fb.group({
emp:this.fb.array({})
});
this.buildForm();
}
buildForm(){
const controls = sampleJson.map(option => new FormControl(option.value));
const formArray = new FormArray(controls);
}
HTML:
<form [formGroup]="form">
<div *ngFor="let field of sampleJson">
<div *ngFor="let control of field">
<ng-container [ngSwitch]="control.type">
<ng-container *ngSwitchCase="'text'">
<input type="text" [formControlName]="control.name">
</ng-container>
</ng-container>
</div>
</div`
Kindly let me know how any have idea. I need to use reactive form method. I have tried lot of different approaches.
SampleJson.forEach(item=> {
this.formGroup.addControl(
item.name, // control name
new FormControl("",)
);
});
Use this approach of adding Control from Array Object.

How to get multiple dynamic drop-down option values in one single list in Angular 7

I am creating a dynamic multiple dropdown in angualar 8. I am receiving list of list from backend and I am running through the list of list to generate multiple dynamic dropdown. Now I need to send selected option values from all the dropdown to backend again in Angular 8. I am unable to send all the value.
I am getting list in this format from which I am looping through to generate dynamic drop-down
cat_dropdown = [['A','B','C'],['1','2','3']]
Based on above list my html code generate two drop-downs one with options A,B,C and another with 1,2,3.
My HTML Code:
<form (ngSubmit)="segmentSelection()" #ff="ngForm">
<div id="userSelection" ngModelGroup="userData" #userData="ngModelGroup">
<div *ngFor="let first of cat_dropdown">
<mat-form-field>
<mat-label>Choose Segment Key</mat-label>
<mat-select id="selectme" name="segmentKey">
<mat-option *ngFor="let segment of first" [value]="segment">
{{segment}}
</mat-option>
</mat-select>
</mat-form-field>
</div>
</form>
My component.ts code:
import { NgForm } from '#angular/forms';
export class myComponent implements OnInit {
#ViewChild("ff", { static: true }) segmentData: NgForm;
plotselection = {
segmentKey: []
}
segmentSelection(){
this.plotselection.segmentKey = this.segmentData.value.userData.segmentKey;
fetch("http://localhost:5000/data-decomposition", {
method: "POST",
headers: {
"Content-Type": "application/json"
},body: JSON.stringify({
segmentKey: this.segmentData.value.userData.segmentKey,
})
}).then(res => res.json()).then(myjson => {
console.log(myjson)
})
}
Now in my .ts component I have a dictionary name "plotselection" where I have key name 'segmentKey' which I defined as empty list.
As I have 2 dropdown in frontend so thought that i will receive two values from frontend and I will send those as a list to backend. When I select option from both the dropdowns for example from 1st dropdown I choose 'B' and from 2nd '3' and submit my selection, then when I console log the response I could only see the value '3' in my list not 'B' and '3' together. How can I have both the value to the list.
Thank you, and looking for your suggestion...
For my easiness I have used Select control instead of mat-select control.
In the form I have given specific name by appending the index to control while looping.
Html
<div *ngFor="let first of cat_dropdown; let num = index">
<div>
<Label>Choose Segment Key</Label>
<Select id="selectme{{num}}" [value]="segment" name="segmentKey{{num}}" (change)="ChangeCall($event.target)">
<option *ngFor="let segment of first" >
{{segment}}
</option>
</Select>
</div>
</div>
So that there are two controls with respective selected values.
There is also alternate solution is use to Change event on the Select Control.

Nothing selected in Select by default - how to fix that?

I'm using Angular 5 and I have a simple Select:
<select class="ui fluid dropdown" formControlName="device">
<option *ngFor="let device of getSenderDevices()" [ngValue]="device">{{device.Name}}</option>
</select>
My problem is the fact that by default nothing is selected, but I'd like the first option to be selected. I saw many threads like this, the solution that I thought would work, does not:
<select class="ui fluid dropdown" formControlName="device">
<option *ngFor="let device of getDevices(); let i = index" [ngValue]="device" [selected]="i==0">{{device.Name}}</option>
</select>
I also found some advices to use compareWith directive - but I wasn't able to understand how it works.
Maybe the problem is caused by getDevices(), which returns data with some delay, because the data is fetched from external server. In the beginning select has to be empty, because the data is not ready yet. When it arrives however, I'd like the select to show that by auto-selecting first option.
Not use a function getDevices() and not use [selected] in .html
//I imagine you have a serviceData that return an observable
export class DataService {
constructor(private httpClient HttpClient) {}
public getDevices():Observable<any>
{
return this.httpClient.get("....");
}
}
constructor(private fb:FormBuilder,private myservice:ServiceData){}
ngOnInit()
{
this.myService.getDevices().subscribe(res=>{
this.devices=res;
this.createForm();
})
//If getDevices() simple return an array, not from service
// this.devices=getServices();
// this.createForm();
}
createForm()
{
this.myForm=this.fb.group({device:this.device[0]})
}
<form [formGroup]="myForm">
<select class="ui fluid dropdown" formControlName="device">
<!--NOT use [selected], when "device" get the value, the select show the value-->
<!--I use the variables "devices"--->
<option *ngFor="let device of devices; let i = index"
[ngValue]="device">{{device.Name}}</option>
</select>
</form>

Angular 5: ngModel binding on select element doesn't update

I have this markup for a select element:
<select type="submit" ([ngModel])="selectedValue" #item (change)="onSelectItem(item.value)">
<option>Pick an option</option>
<option *ngFor="let object of objects">{{ object.value }}</option>
</select>
When I update the ngModel binding from typescript, nothing happens. Essentially I am just doing this, in the component:
ngOnInit() {
this.selectedValue = 'something' // One of the options from the list of objects
}
However, nothing happens.
The value I am trying to update it to, are in the list of objects (in the *ngFor) - if that matters.
Change ([ngModel])="selectedValue" to [(ngModel)]="selectedValue"
Just like the docs say:
Visualize a banana in a box to remember that the parentheses go inside the brackets.
Also you do not need the (change) listener if you are using ngModel. You can split your two way binding into [ngModel] and (ngModelChange)
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template: `
<select type="submit" [ngModel]="selectedValue" (ngModelChange)="onSelectedChange($event)">
<option *ngFor="let option of options">{{ option }}</option>
</select>
{{ selectedValue }}
` ,
styleUrls: ['./app.component.css']
})
export class AppComponent {
selectedValue = 1;
options = [1,2,3]
onSelectedChange(value: number) {
// do something else with the value
console.log(value);
// remember to update the selectedValue
this.selectedValue = value;
}
}
Live demo
Please change the definition of ([ngModel]) to [(ngModel)] and you should initialize the objects values before assign the value in selectedValue object

add div of two select tags on a button in angular?

I am new to angular. I am using typescript.
I want to achieve this task.
Here is the image of what I want do.
I want to add the date and location selector on add button click. I actually did that but I am unable to maintain its data when I remove its row.
Here is what I did in html:
<div id="" *ngFor = "let sp of specialityElementCount">
<select id="specialityDate"
class="textField"(change)='selectSpecialityDate($event.target.value)'>
<option>Select Date</option>
<option *ngFor = "let date of spDates"
[value]="date">{{date}}</option>
</select>
<select class="textField"
[ngClass]="{'specialityLocation':(sp=='speciality1'),'specialityLocationRemove':(sp!='speciality1')}"
(change)='selectSpecialityLocation($event.target.value, sp)'>
<option>Select Location</option>
<option *ngFor = "let location of specialityLocation"
[value]="location">{{location}}</option>
</select>
<div class= "removeImgDiv"[hidden]="sp=='speciality1'">
<img class="removeImg" (click)="removeRow(sp)" src="../../assets/img/remove.png">
</div>
</div>
and here is what I did in typescript:
specialityElementCount = ["speciality1"];
addRow(row:string){
console.log(row);
if(row.includes("speciality")){
this.specialityElementCount.push("speciality"+(this.specialityElementCount.length+1));
}
}
removeRow(row){
console.log(row);
if(row.includes("speciality")){
this.specialityElementCount.splice(this.tripLocations.indexOf(row), 1);
}
}
selectSpecialityDate(event){
console.log(event);
}
selectSpecialityLocation(event, text){
console.log(event);
console.log(text);
}
Please help me with this. That when I remove row it removes the correct row instead of remove the last row every time.
Thanks in advance.
What you can do use the index generated by *ngFor instead of searching for the row yourself.
It would look something like this:
#Component({
selector: 'my-app',
template: `
<div>
<div *ngFor="let row of data; let index = $index">
<div>{{row}}</div>
<div (click)="removeRow(index)">X</div>
</div>
</div>
`,
})
export class App {
data:string[];
constructor() {
this.data = ['a', 'b','c']
}
removeRow(index) {
this.data.splice(index, 1);
}
}
You can see a working plunkr with the example here.