Allowing `undefined` as a valid select menu option (in Angular)? - html

I'm unsure if this is an Angular issue, or a general restriction of HTML forms.
I would like a select form with three values: true, false, and, undefined. See this Stackblitz or code below.
<mat-form-field [formGroup]="form">
<mat-select formControlName="test" value="undefined">
<mat-option *ngFor="let v of values"
[value]="v.value">{{ v.display }}</mat-option>
</mat-select>
</mat-form-field>
...and the corresponding .ts file:
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
public form: FormGroup;
public readonly values: any[] = [
{
display: 'true',
value: true
},
{
display: 'false',
value: false
},
{
display: '--',
value: undefined
}
];
constructor(private readonly fb: FormBuilder) {
this.form = this.fb.group({
test: undefined
});
}
}
However, while hard-coding test to true or false works:
this.form = this.fb.group({
test: true // 'true' or 'false' selected in form.
});
...undefined does not:
this.form = this.fb.group({
test: undefined // '--' option is *not* selected.
});

Setting anything to undefined is considered a bad practice.
In this particular case, probably the best solution is to use empty string as a default value.
If you look into material's code you will see that null/undefined is treated as special reset value: Material Source

Related

reactive form control with json as default value

Hi I am facing an issue with reactive form control usage. For mat-select I have a default JSON array with name and id. Of which I am showing only name in the drop down. I want to set a name as default using similar JSON but I am not able to do that. Kindly suggest:
import { Component, OnInit } from '#angular/core';
import { FormGroup, FormBuilder } from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
form: FormGroup
data: any
constructor(
private formBuilder: FormBuilder
) {}
ngOnInit() {
this.stateList = [{
name: 'def',
id: '1'
}, {
name: 'ghi',
id: '2'
}];
this.form = this.formBuilder.group({
state: {
name: 'abc',
id: '1'
}
})
}
}
<form [formGroup]="form">
<mat-form-field>
<mat-label>Color</mat-label>
<mat-select [(value)]="state.name" formControlName="state">
<mat-option *ngFor="let state of stateList" [value]="state">
{{ state.name }}</mat-option>
</mat-select>
</mat-form-field>
</form>

How to pass the object value dynamically as parameter into new FormControl() in angular 8

I have a dropdown whose data is coming from loop.Here I need to get the selected value and selected text on page load and on click a span.I am using reactive form here.To make default selection I am using new FormControl(1) ,Every thing is working correct I am getting the result in console.But when I am removing first object of array from my json and reload the page its creating error,since I hardcoded to 1 in FormControl,if I change to FormControl(2) its working fine.But I need to pass it dynamically so that it can work with any number of object. Here is the code below and demo example I created https://stackblitz.com/edit/angular-lwrvvz?file=src%2Fapp%2Fapp.component.ts
app.component.ts
import { Component } from '#angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
registerForm: FormGroup;
statusdata: any;
constructor(private formBuilder: FormBuilder)
{
this.registerForm = this.formBuilder.group({
title: new FormControl(1)
});
}
ngOnInit() {
this.statusdata = [{"groupid":1,"groupname":"project1"},{"groupid":2,"groupname":"project2"},{"groupid":3,"groupname":"project3"}];
console.log('selected Value', this.registerForm.get('title').value);
console.log('selected name', this.statusdata.filter(v => v.groupid == this.registerForm.get('title').value)[0].groupname);
}
getSelectedVal(){
console.log('selected Value', this.registerForm.get('title').value);
console.log('selected name', this.statusdata.filter(v => v.groupid == this.registerForm.get('title').value)[0].groupname);
}
}
app.component.html
-----------------
<hello name="{{ name }}"></hello>
<p>
Start editing to see some magic happenss :)
</p>
<div>
<form [formGroup]="registerForm">
<select formControlName="title" class="form-control">
<option *ngFor="let grpdata of statusdata" value="{{grpdata.groupid}}">{{grpdata.groupname}}</option>
</select>
</form>
</div>
<p>home works!</p>
<div><span (click)="getSelectedVal()">Click here</span></div>
You can update the form to using patchValue method to select the first group in statusdata array. This way you don't have to hard-code anything.
Example: https://stackblitz.com/edit/angular-ggnhzq
(I've added a null condition for retrieving groupname in case there are 0 groups)

How to get selected value of dropdown in angular 6 on load(not onchange)

I have a select dropdown ,I am getting option's data and value from an array through loop. Here I need to get the value of selected drop down when page loads without onchange(in this case Recent). Here is code below.
app.component.html
<select>
<option *ngFor="let v of values" [value]="v.id">
{{v.name}}
</option>
</select>
app.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
title = 'projectchart';
public values = [
{ id: 3432, name: "Recent" },
{ id: 3442, name: "Most Popular" },
{ id: 3352, name: "Rating" }
];
ngOnInit(){
alert('The default selected value is');
}
}
you could make use of reactive forms
constructor(private fb: FormBuilder) { }
// create a form group using the form builder service
formName = this.fb.group({
name: ['']
})
in the template
<form [formGroup]="formName">
<select formControlName="name">
<option *ngFor="let v of values" [value]="v.id">
{{v.name}}
</option>
</select>
</>
and then get the value in the ts:
this.formName.controls['name'].value

Dynamic form using *ngFor and submitting values from it

I have a stackblitz as a guide.
I am wanting to display a list of material cards that I click an 'edit' button, to which I can edit text fields, and when I click on the 'save' icon, it of course saves by triggering an function etc.
I am struggling however to get to grips with how this all works within Angular and the Material nature of my app.
html
<form id="myForm" [formGroup]="thisIsMyForm">
<mat-card [formGroup]="x" *ngFor="let x of data; let i = index">
<mat-form-field>
<label for="{{x.name}}">Name</label>
<input formControlName="name" id="{{x.name}}" matInput value="{{x.name}}">
</mat-form-field>
<mat-form-field>
<label for="{{x.type}}">Type</label>
<input formControlName="type" id="{{x.type}}" matInput value="{{x.type}}"/>
</mat-form-field>
</mat-card>
</form>
ts
import { Component, ViewChild } from '#angular/core';
import {MatSnackBar} from '#angular/material';
import {FormArray, FormBuilder, FormGroup, Validators} from '#angular/forms';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
thisIsMyForm: FormGroup
data = [
{name:"one", type:"one"},
{name:"two", type:"two"},
{name:"three", type:"three"},
];
constructor(private formBuilder: FormBuilder) {}
onSubmit() {
// Here I would like to be able to access the values of the 'forms'
}
}
You are diving into the deep end for sure, trying to build a dynamic reactive form within the scope of an *ngFor is a challenge. I will walk you through it the best I can.
You will need to create an array for controls, in your constructor create your form setting formArrayName as an empty array using this.formBuild.array([])... call this whatever you want, I just used formArrayName for demonstration purposes.
After the form is instantiated with an empty array call this.buildForm()
constructor(private formBuilder: FormBuilder) {
this.thisIsMyForm = new FormGroup({
formArrayName: this.formBuilder.array([])
})
this.buildForm();
}
In your buildForm() iterate over your data[] and push controls for each index while assigning the default value and a default state of disabled.
buildForm() {
const controlArray = this.thisIsMyForm.get('formArrayName') as FormArray;
Object.keys(this.data).forEach((i) => {
controlArray.push(
this.formBuilder.group({
name: new FormControl({ value: this.data[i].name, disabled: true }),
type: new FormControl({ value: this.data[i].type, disabled: true })
})
)
})
console.log(controlArray)
}
Please Note: console.log(controlArray.controls) results in the following output... each index is a FormGroup with two controls name and type
0: FormGroup
1: FormGroup
2: FormGroup
In your html you will need to establish a container hierarchy that mimics the thisIsMyForm you just created.
parent:thisIsMyForm
child:formArrayName
grandchild:i as formGroupName
grandchild is important because it matches the console log of controlArray.controls in previous step
<form id="myForm" [formGroup]="thisIsMyForm">
<div [formArrayName]="'formArrayName'">
<mat-card *ngFor="let x of data; let i = index">
<div [formGroupName]="i">
Create edit and save buttons based on control disabled state
<button *ngIf="formControlState(i)" (click)="toggleEdit(i)">Enable Edit</button>
<button *ngIf="!formControlState(i)" (click)="toggleEdit(i)">Save</button>
Create methods in component to receive the index as an argument and handle logic to hide buttons and toggle input fields enable and disable state.
toggleEdit(i) {
const controlArray = this.thisIsMyForm.get('formArrayName') as FormArray;
if(controlArray.controls[i].status === 'DISABLED'){
controlArray.controls[i].enable()
}else{
controlArray.controls[i].disable()
}
}
formControlState(i){
const controlArray = this.thisIsMyForm.get('formArrayName') as FormArray;
return controlArray.controls[i].disabled
}
Submit button that console.log's the form value when clicked... also disable button while any of the input formControls are in enabled state.
<button [disabled]="thisIsMyForm.get('formArrayName').enabled" (click)="onSubmit()">Submit Form</button>
onSubmit() {
// Here I would like to be able to access the values of the 'forms'
console.log(this.thisIsMyForm.value)
}
Stackblitz
https://stackblitz.com/edit/dynamic-form-ngfor-otbuzn?embed=1&file=src/app/app.component.ts
Doing it with QueryList:
your html (this is an example):
<ng-container *ngFor="let x of data; let i = index">
<div class="ctr">
<span #names class="item">{{x.name}}</span>
<span #types class="item">{{x.type}}</span>
<span class="button" (click)="showData(i)">Show data</span>
</div>
</ng-container>
<h2>Selected values: </h2>
Selected name: {{selectedName}} <br>
Selected type: {{selectedType}}
some css just for the style
.ctr{
display: flex;
flex-direction: row;
margin-bottom: 20px;
}
.item{
margin-right:40px;
}
.button{
border: 1px solid black;
padding: 2px 5px 2px 5px;
cursor: pointer;
}
the component:
import { Component, QueryList, ViewChildren } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
#ViewChildren('names') names:QueryList<any>;
#ViewChildren('types') types:QueryList<any>;
selectedName: string;
selectedType: string;
data = [
{name:"one", type:1},
{name:"two", type:2},
{name:"three", type:3},
];
showData(index){
let namesArray = this.names.toArray();
let typesArray = this.types.toArray();
this.selectedName = namesArray[index].nativeElement.innerHTML;
this.selectedType = typesArray[index].nativeElement.innerHTML;
}
}
Working stackblitz: https://stackblitz.com/edit/angular-j2n398?file=src%2Fapp%2Fapp.component.ts

HTML Variables ignores double brackets

The actually problem was that my angular is not working at all
if you want to test this do this :
import { Component, OnInit} from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
ngOnInit() {
console.log('test');
}
}
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
/////////////////////////////////////////
I'm trying to create a dropdownlist with a name and value different from each other. In Visual studio 2015 the brackets at ">{{directionOption.name}}<" are shown grey as plain text
<select [(ngModel)]="direction">
<option *ngFor="#directionOption of directionOptions" [value]="directionOption.value">{{directionOption.name}}</option>
</select>
same result
<select [(ngModel)]="direction">
<option *ngFor="let directionOption of directionOptions" [value]="directionOption.value">{{directionOption.name}}</option>
</select>
export class AppComponent implements OnInit {
direction: any = { 'Left': 'L' };
directionOptions: any = [{ name: 'Left', value: 'L' }, { name: 'Right', value: 'R' }, { name: 'None', value: 'N' }];
}
The current result inside browser F12
<option *ngfor="#directionOption of directionOptions" [value]="directionOption.value">{{directionOption.name}}</option>
Use let keyword instead of #,
<option *ngfor="let directionOption of directionOptions" [value]="directionOption.value">{{directionOption.name}}</option>
update
Angular2 doesn't process bindings added dynamically to the DOM, only when they are added statically to a components template.
original
directionOptions should look like
directionOptions: any = [{ name: 'Left' , value: 'L' }, { name: 'Right' , value: 'R' }, { name: 'None' , value: 'N' }];