I am having a hard time trying to get the select input to show the initially selected option using Angular. I have tried many options but none have worked so far. Not sure why this is causing me such a headache. Any help would be great, thank you.
The select input shows nothing until I select an option. Would like to dynamically select what option I want selected when app loads.
You can view what I have setup so far with the link below:
https://stackblitz.com/edit/angular-wga92l?file=app%2Fhello.component.ts
Component:
#Component({
selector: 'master-input',
template: `
<form>
<div class="form-group">
<label for="Master-Products">Select Master Product</label>
<select name="pow.name" (ngModelChange)="change($event)" [ngModel]="selectedValue" class="form-control" id="Master-Products">
<option *ngFor="let pow of powers" [ngValue]="pow.id" [selected]="pow.id == 3">{{pow.name}}</option>
</select>
</div>
</form>
`,
styleUrls: ['./master-input.component.scss']
})
export class MasterInputComponent {
#Output() hasChanged: EventEmitter<number> = new EventEmitter();
powers: any[] = [{ id: 1, name: 'Bead Ruptor Elite' }, { id: 2, name: 'Bead Ruptor 12' }, { id: 3, name: 'Bead Ruptor 96' }];
selectedValue: string = 'Bead Ruptor 96';
change(value: number) {
this.hasChanged.emit(value);
}
}
just Add ngOnInit function
powers: any[] = [ 'power1', 'power2', 'power3' ];
ngOnInit(){
this.selectedValue ='power3';
}
change(value:string) {
this.hasChanged.emit(value)
}
It was tested in your fiddle.
Please see working image
Related
I created a simple reusable component as:
TS
import {Component, Input, OnInit} from '#angular/core';
import {FormControl} from '#angular/forms';
#Component({
selector: 'app-select',
templateUrl: './select.component.html',
styleUrls: ['./select.component.css']
})
export class SelectComponent implements OnInit {
#Input() control: FormControl;
#Input() label: string;
#Input() options: [];
#Input() idAndForAttributes: string;
#Input() customClass: string;
constructor() { }
ngOnInit() {
}
}
HTML
<div class="form-group" [ngClass]="{'invalid': control.invalid && control.touched && control.dirty}">
<label [attr.for]="idAndForAttributes">{{ label }}:</label>
<select class="form-control" [ngClass]="customClass" [formControl]="control" [attr.id]="idAndForAttributes">
<option value="0">- Select -</option>
<option *ngFor="let item of options" [ngValue]="item.id">{{item.description}}</option>
</select>
<ng-container *ngIf="control.dirty && control.touched && control.invalid">
<div *ngIf="control.errors.required || (control.errors.min && control.value == 0)">
<small style="color: #c62828;">
Value is required.
</small>
</div>
</ng-container>
</div>
Now I'm trying to use it in my other html as:
<form [formGroup]="profileActivityForm">
<app-select [control]="profileActivityForm.get('activityType')" [idAndForAttributes]="'type'" [label]="'Type'"
[options]="profileActivityTypes"></app-select>
</form>
Then in TS
profileActivityTypes: string[] = [];
ngOnInit() {
this.profileActivityTypes.push('New')
this.profileActivityTypes.push('Update')
this.profileActivityForm = this.fb.group({
activityType: [0]
});
}
But it is showing invisible options like the following picture:
I think the problem is on the html of the reusable component <option *ngFor="let item of options" [ngValue]="item.id">{{item.description}}</option>
Because it is looking for a description, how can I send the item as a description from the child component?
UPDATE
I tried:
profileActivityTypes: [] = [];
....
let profileActivities = [{ description: 'New' }, { description: 'Update' }]
this.profileActivityTypes.push(profileActivities)
but it is throwing an error on push:
Argument of type '{ description: string; }[]' is not assignable to
parameter of type 'never'
In order to solve this, I changed the assignation of the profileActivities array instead of creating the array and then pushing it. I assign it directly as:
profileActivityTypes = [];
this.profileActivityTypes = [{ id: 1, description: 'New' }, {id: 2, description: 'Update'}]
I hope this works for more people!
I have few checkboxes which are coming from ngFor. I have a button which is disabled onload.
If I select/unselect only other checkboxes(which are not selected), button will be enable.
If I unselect any selected checkbox button will be enable.
If I select back again selected checkboxes button will be disable. Here is the code below https://stackblitz.com/edit/angular-mthf93?file=src%2Fapp%2Fapp.component.html
app.component.html
<hello name="{{ name }}"></hello>
<p>
Start editing to see some magic happen :)
</p>
<div>
<div *ngFor="let testitem of testVar">
<input type="checkbox" [checked]= "testitem.checked" (change)="changeit(testitem)" class="example-margin"/>
{{testitem.name}}
</div>
<button [disabled]="disabled">Submit</button>
</div>
app.component.ts
import { Component, OnInit } from "#angular/core";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
testVar: any;
disabled = true;
name = "Angular";
ngOnInit(): void {
this.testVar = [
{ id: 1, name: "apple", checked: false },
{ id: 2, name: "banana", checked: true },
{ id: 3, name: "orange", checked: false }
];
}
changeit(testitem) {
console.log(testitem.checked);
if (testitem.checked == false) {
this.disabled = false;
} else {
this.disabled = true;
}
}
}
From the discussion following requirement was elaborated:
Here main requirement is if you make any new change, button will only be enable.so that those new changes can be saved into DB by clicking the button.
Possible solution:
You have to get the initial state and the current state somehow. Probably you should use a formGroup and split the current form from the model and diff it with that. I Just hacked the requirement it into https://stackblitz.com/edit/angular-eysswr?file=src/app/app.component.ts - but in my opinion should you split your model from the form for this requirement. see
just checking the current element will never allow you to check if the button must be disabled.
So I have two ng-select in my code, and if I select first ng-select value, I want to hide the same select option from the second ng-select. But I dont know how to control it. I wanted to use Jquery with, but no chance.
Both sourceLangCodeList and targetLangCodeList has values [ EN, KR ]
So the thing I want to try, once I select the value of one of ng-select, hide the same value the second ng-select has. Plz help!
<td>
<div class="form-group">
<ng-select
id="sourceLangSelect"
bindLabel="language"
bindValue="language"
formControlName="sourceLangCode"
[items]="sourceLangCodeList"
(change)="onChange($event)"
></ng-select>
</div>
</td>
<td></td>
<td>
<div class="form-group">
<ng-select
id="targetLangSelect"
bindLabel="language"
bindValue="language"
formControlName="targetLangCode"
[items]="targetLangCodeList"
></ng-select>
</div>
</td>
Please add a ngModel to slectedSoureclang and Onchange Filter the targetLangCodeList
<ng-select
id="sourceLangSelect"
bindLabel="language"
bindValue="language"
formControlName="sourceLangCode"
[items]="sourceLangCodeList"
(change)="onChange($event)"
[(ngModel)]="selectedSourceLang"
></ng-select>
Change Event
onChange(event){
const newlist = targetLangCodeList.filter((lang)=>lang!==this.selectedSourceLang);
targetLangCodeList = [...newlist];
}
Since you are using ReactiveForms, I will answer in ReactiveForm way.
Check out the full code at https://stackblitz.com/edit/m3yevn-ng-select-demo
Template:
<div class="form-group" [formGroup]="formGroup">
<ng-select id="sourceLangSelect" bindLabel="language" bindValue="language" formControlName="sourceLangCode"
[items]="sourceLangCodeList"></ng-select>
<ng-select id=" targetLangSelect" bindLabel="language" bindValue="language" formControlName="targetLangCode"
[items]="targetLangCodeList"></ng-select>
</div>
Component
import { Component, Input } from "#angular/core";
import { FormControl, FormGroup } from "#angular/forms";
import { Subscription } from "rxjs";
#Component({
selector: "ng-select-demo",
templateUrl: "./ng-select-demo.component.html",
styles: [
`
h1 {
font-family: Lato;
}
`
]
})
export class NgSelectDemo {
#Input() name: string;
sub = new Subscription();
originalLangCodeList = [
{ language: "" },
{ language: "en" },
{ language: "kr" },
{ language: "zh-cn" },
{ language: "ru" }
];
targetLangCodeList = [...this.originalLangCodeList];
sourceLangCodeList = [...this.originalLangCodeList];
formGroup = new FormGroup({
sourceLangCode: new FormControl(""),
targetLangCode: new FormControl("")
});
ngOnInit() {
this.sub.add(
this.formGroup.controls["sourceLangCode"].valueChanges.subscribe(
value => {
this.targetLangCodeList = this.originalLangCodeList.filter(
langObj => langObj.language !== value
);
}
)
);
}
ngOnDestroy() {
this.sub.unsubscribe();
}
}
TLDR;
Use a form group to create two formControlName which you are apparently using.
In ngOnInit, subscribe the valueChanges of formGroup.controls['sourceLangCode'].
When value changes, filter the language which have been selected in targetLangCode.
In ngOnDestroy, don't forget to unsubscribe it.
I have a project that has a select of several questions and I would like the first one to appear by default.
I have seen an example of how to achieve this using the index, of the type:
<select>
<option *ngFor="let answer of answers; let i = index" [value]="answer.id" [selected]="i == 2">
{{answer.name}}
</option>
</select>
and it works, but when I want to bind the select to a property of the component, it is no longer selected:
<select [(ngModel)]=searchterms.answerId>
<option *ngFor="let answer of answers; let i = index" [value]="answer.id" [selected]="i == 2">
{{answer.name}}
</option>
</select>
You can see an example here:
https://stackblitz.com/edit/angular-rv9vqi
As say in some answers the solution is to set a default value to the serachterm, but the problem I have, (I am not able to reproduce it in the playground) is that I receive those answers from a service that asks to a back, and when the component is built it still does not have them and it gives me an error .... how can I make it assign those searchterms the value once they exist in the service?
You can use Angulars two-way data binding to bind to the value attribute of the <select> element. In your example it would look like this:
<select [(value)]="searchterms.answerId">
<option *ngFor="let answer of answers" [value]="answer.id">{{answer.name}}</option>
</select>
Notice how binding to the value attribute cleans up the option element, by enabling you to remove the [selected]="i == 2" and let i = index
However, like the others have mentioned you will want to initialize the desired default value in your component code.
Here is the working StackBlitz Demo with your code
You need to set the searchterms.answerId with a proper default value. In your case
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular 6';
answers = [
{name: "Answer 1", id: '1'},
{name: "Answer 2", id: '2'},
{name: "Answer 3", id: '3'},
{name: "Answer 4", id: '4'},
];
searchterms = {
answerId: this.answers[1].id //Set the default value properly
};
}
stackblitz
Then you should bind searchterms in your component.
you can do it inside constructor as follows.
this.searchterms.answerId = this.answers[1].id;
Demo
Angular 2,4 formbuilder setvalue() is not working as expected when used on dropdown selects.
I have the following dropdown select that gets populated with Github organizations:
<select name="org" formControlName="organizations">
<option *ngFor="let org of organizations" [ngValue]="org">{{org.organization.login}}</option>
</select>
Here is the javascript code that sets the Github organization that should be selected.
this.pipelineForm = fb.group({
name:'',
organizations:'',
repos: '',
branches:'',
runtime:'',
versions:''
});
userService.getOrganizations().subscribe((org:github)=>{
let organizationName = org.data[0];
this.organizations = org.data;
this.projects.subscribe((p)=> {
p[1].project.subscribe((f)=>{
this.pipelineForm.get("organizations").setValue(f.organizations, {onlySelf: true});
//this.pipelineForm.patchValue(f);
});
});
});
I expect the corresponding dropdown option to be selected when I pass the value to the setValue(). Instead, I get a blank option. I also tried with patchValue(). No luck.
I struggled with the same problem and found Igor's answer. It was too vague so I dug around more. I finally figured it out, and Igor's answer is correct albeit lacking in detail. Hopefully this will help anyone else in the future trying to get setValue() on dropdowns and object models to work happily together.
Generally, the object model passed into setValue() should be the same as the objects you inject into your dropdown selector. E.g. if of type Organization, then the setValue should be also be of type Organization. It's not necessary, however, to share completely identical properties.
To get the setValue() or patchValue() to work with an object model (as opposed to some primitive type), use the [compareWith] function as Igor pointed out.
From node_modules/#angular/material/select/typings/select.d.ts:
compareWith is a function to compare the option values with the selected values.
The first argument is a value from an option. The second is a value
from the selection. A boolean should be returned.
In your html template,
<select [compareWith]="compareOrgs" name="org" formControlName="organizations">
<option *ngFor="let org of organizations" [ngValue]="org">{{org.organization.login}}</option>
</select>
And in your component.ts file, define the compareOrgs() function:
compareOrgs(c1: any, c2: any): boolean {
return c1 && c2 ? c1.orgId === c2.orgId : c1 === c2;
}
Don't forget to call the setValue() function, for e.g. in ngOnInit() or in the callback function if it's async and you're fetching data.
E.g. in the callback from OP's code above,
// "organization" is whatever object you're trying to patch on the dropdown
this.pipelineForm.get("organizations").setValue(organization)
So what all this does is, the compareOrgs function will compare the value of the selected value (which is the object you passed into setValue, now labelled as c2 in compareOrgs) with each of the option values (c1). Specifically, it compares the orgId properties of the selected and option values. This is how the FormControl knows which value to pre-select on the dropdown.
You can now access the object with [ngValue].
This is a good resource that helped me: https://codeburst.io/angular-material-select-module-the-comparewith-function-9dfdb4035373
A little example, we want to save a number. see [ngValue]="org.id" and this.myForm.controls['organization'].setValue(value);
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormControl, FormGroup } from '#angular/forms';
interface Iorganization {
id: number;
name: string;
}
#Component({
selector: 'app-select',
template: `
<form [formGroup]="myForm" novalidate>
<select name="org" formControlName="organization">
<option *ngFor="let org of organizations" [ngValue]="org.id">{{org.name}}</option>
</select>
</form>
<button (click)="setOrg(1)">Org 1</button>
<button (click)="setOrg(2)">Org 2</button>
{{myForm.value |json}}
`,
styleUrls: ['./select.component.css']
})
export class SelectComponent implements OnInit {
organizations: Iorganization[];
myForm: FormGroup;
constructor(private fb: FormBuilder) { }
ngOnInit() {
this.organizations = [
{ id: 1, name: "org 1" },
{ id: 2, name: "org 2" },
{ id: 3, name: "org 3" },
{ id: 4, name: "org 4" }
];
this.myForm = this.fb.group({
organization: 1
})
}
setOrg(value: number) {
this.myForm.controls['organization'].setValue(value);
}
}
if we want to save a object -see {{myForm.value |json}}- we need make a few changes. see that we put [ngValue]="org", but the setOrg() function use a object to make setValue. And not, it's not valid setValue({id:1,name:'org1'})
import { Component, OnInit } from '#angular/core';
....
template: `
<form [formGroup]="myForm" novalidate>
<select name="org" formControlName="organization">
<option *ngFor="let org of organizations" [ngValue]="org">{{org.name}}</option>
</select>
....`
export class SelectComponent implements OnInit {
...
ngOnInit() {
this.organizations = [
{ id: 1, name: "org 1" },
....
];
this.myForm = this.fb.group({
organization: null
})
}
setOrg(value: number) {
let organization=this.organizations.find(o=>o.id==value);
this.myForm.controls['organization'].setValue(organization);
}
Use the attribute compareWith on your select tag:
<select ... [compareWith]="compareByName">
Then implement a simple function in your component
compareByName(org1: Org, org2: Org): boolean {
return org1 && org2 ? org1.name === org2.name : org1 === org2;
}