How can I swap option values of two HTML <select> elements? - html

I am trying to swap two select-option values in my currency converter. I have link icon which activates swapOptions function which swaps two values. This part works fine. The question is how to set this new values to "select" element?
This is my typescript function and I can see in console new values which should be set in select option:
startSwap:any;
endSwap: any;
swapOptions(firstOption: any, secondOption: any){
for(let i = 0; i < this.parsedJson.length; i++){
if(firstOption == this.parsedJson[i].Srednji){
this.endSwap = this.parsedJson[i].SelectOption;
}
else if(secondOption == this.parsedJson[i].Srednji){
this.startSwap = this.parsedJson[i].SelectOption;
}
}
console.log("New start option : "+this.startSwap)
console.log("New end option : "+this.endSwap)
}
This is my html. I have tried with [(ngModel)] but on change i get empty select elements.
<select name="options" #firstSelect id="slc" class="form-control" [(ngModel)]="startSwap">
<option *ngFor="let a of parsedJson;" class="form-control" (click)="firstSelectValues(a.Jedinica, a.Valuta)" (click)="intervalCurrencyStats()" [(value)]="a.Srednji">{{a.SelectOption}}</option>
</select>
<p id="p">End value: </i></p>
<select name="options2" #secondSelect id="slc" class="form-control" [(ngModel)]="endSwap">
<option *ngFor="let a of parsedJson;" class="form-control" (click)="secondSelectValues(a.Jedinica, a.Valuta)" [(value)]="a.Srednji">{{a.SelectOption}}</option>
</select>
On this picture you can see two select option elements and the blue arrow which is link to swapOption function. The empty fields are result of clicking the arrow.
Any advice is welcome. Thanks in advance!
Angular CLI: 9.1.8
Node: 12.13.0

After some time I have figured this one out.
Now using formGroup and form control
import { FormControl, FormGroup, FormsModule, FormBuilder } from '#angular/forms';
Some changes in HTML:
<form [formGroup]="crForm">
<select name="options" #firstSelect id="slc" class="form-control" formControlName="crControl">
...
</select>
<select name="options2" #secondSelect id="slc" class="form-control" formControlName="crEndControl">
...
</select>
</form>
And in typescript added private fb: FormBuilder in constructor, and formbuilder group for setting new values in select-option (also added inside onInit() with hardcoded default values):
this.crForm = this.fb.group({
crControl: [this.startSwap],
crEndControl: [this.endSwap]
})

Related

Angular 9 - different formGroupName with same name of formControlName affects same formControlName

I created form: FormGroup variable where I defined group with a FormArray localizationData: FormArray like this in constructor of my service:
this.form = this.formBuilder.group({
localizationData: this.formBuilder.array([])
});
Then I pushing to that FormArray a FormGroup with specific FormControl with this function:
addLocalizedFormGroup(locale?: any){
locale = (locale == undefined) ? this.selectedLocale : locale;
let formArray = <FormArray>this.form.get('localizationData');
formArray.push(this.formBuilder.group({
locale: [locale.code],
codelistName: ['', Validators.required]
}));
locale.created = true;
this.createdLocales.push(locale);
}
In ngOnInit of component I called that function twice. Example of locale object variable:
{ "code": "SK", "name": "Slovenčina" }
To show to user specific format of that locale I using this function:
localeFormatter = (x: any) => x.code + " | " + x.name;
I implemented simple options list with localizations for user to select one:
<div class="form-group col-3">
<label>Lokalizácia
<select class="form-control" [(ngModel)]="this.defservice.selectedLocale">
<option *ngFor="let locale of this.defservice.createdLocales" [ngValue]="locale">{{this.defservice.localeFormatter(locale)}}</option>
</select>
</label>
</div>
That options list works fine. When I select different language, model change as a object. To find a FormGroup by locale I create very simple function for that:
localizationIndexOf(locale: any): number{
return (<FormArray>this.form.get('localizationData')).controls.findIndex(x => x.get('locale').value == locale.code);
}
And now, the problem what I am challenging is to show user value of FormControl, codelistName, as you can see defined in function addLocalizedFormGroup. Here is html:
<ng-container [formGroup]="defservice.form">
<ng-container formArrayName="localizationData">
<ng-container [formGroupName]="defservice.localizationIndexOf(defservice.selectedLocale)">
<div class="row">
<div class="form-group col-2">
<label>Lokalizácia
<input type="text" class="form-control" [value]="defservice.localeFormatter(defservice.selectedLocale)"
disabled="true">
</label>
</div>
<div class="form-group col-10">
<label>Názov <span style="color: red">*</span>
<input type="text" class="form-control" formControlName="codelistName">
</label>
</div>
</div>
</ng-container>
</ng-container>
</ng-container>
But after switch language in option list, localization value change correctly, localizationIndexOf(defservice.selectedLocale) return correct index but after edit codelistName of any index of FormmArray created with function addLocalizedFormGroup, what I added, only first one (with index 0) is changing. I print whole value of form and it show only changed value of first language. Event if I have 2 or 10 languages. I mean 2 or 10 pushed FormGroups into localizationData FormArray of form: FormGroup. It seems like on first load of component, formGroup never update his value, I ref to this line:
<ng-container [formGroupName]="defservice.localizationIndexOf(defservice.selectedLocale)">
Thank you for any advice.
I recreated the problem on my side but was unable to find a solution with *ControlName='' syntax. I found one with [formControl]='' but you will have to forget the formGroupName hierachy.
If you can keep a reference of the index of the current FormGroup of the localizationData: FormArray, you will be able to get the control from the array:
<input class="..." type="text" [formControl]="localizationData.at(currentLocaleIndex).get('codelistName')">
localizationData is a getter for the FormArray:
get localizationData(): FormArray {
return this.form.get('localizationData') as FormArray
}
And a solution to keep currentLocaleIndex up to date is to update the value when a change happens on the <select> like this:
<select (change)="updateCurrentLocaleIndex()" class="form-control" [(ngModel)]="this.defservice.selectedLocale">
<option *ngFor="let locale of this.defservice.createdLocales" [ngValue]="locale">{{this.defservice.localeFormatter(locale)}}</option>
</select>
In your component class :
updateCurrentLocaleIndex() {
this.currentLocaleIndex = this.localizationIndexOf(this.defservice.selectedLocale)
}

Why can I not set a pre selected value to my select list in HTML with getElementById?

I am trying to set a pre selected option based on a previous select list (that part is not shown below). It's working with getelementById() when I use it on an <input> field but not on a <select>. Why is that?
Working example:
<label for="goalcategory">Edit assigned category</label>
<select id="selectedGoalcategory" class="form-control" required>
<option></option>
<option *ngFor="let goalcategory of allGoalcategories">{{goalcategory.description}}</option>
</select>
<input class="input" style="width:10%;" id="TestInput" type="text"/>
Typsescript behind it:
getCategoriByGoal()
{
var preSelectedGoalcategory = this.selectedGoal.goalcategory;
this.selectedGoalcategory = preSelectedGoalcategory;
(<HTMLInputElement>document.getElementById("TestInput")).value =
this.selectedGoalcategory.description;
}
The way I want it to work (HTML):
<label for="goalcategory">Edit assigned category</label>
<select id="selectedGoalcategory" class="form-control" required>
<option></option>
<option *ngFor="let goalcategory of allGoalcategories">{{goalcategory.description}}</option>
</select>
The way I want it to work (Typscript):
getCategoryByGoal()
{
var preSelectedGoalcategory = this.selectedGoal.goalcategory;
this.selectedGoalcategory = preSelectedGoalcategory;
(<HTMLInputElement>document.getElementById("selectedGoalcategory")).value =
this.selectedGoalcategory.description;
}
If you dont mind adding 3rd party components, you could add ng-select, it would make your life easier for a lot of things.
Install ng-select from npm:
npm install --save #ng-select/ng-select
After that add it to your module along with FormsModule:
import { NgSelectModule } from '#ng-select/ng-select';
import { FormsModule } from '#angular/forms';
#NgModule({
declarations: [AppComponent],
imports: [NgSelectModule, FormsModule],
bootstrap: [AppComponent]
})
export class AppModule {}
And in your HTML you can use it like this:
<ng-select [items]="allGoalcategories" bindLabel="name" bindValue="id" name="goalcategories"
[(ngModel)]="selectedGoalcategory">
</ng-select>
Now every time you change selectedGoalcategory it will reflect in your other dropdown, and you dont need to use getElementById
As #wentjun said, there is no need to rely on document.getElementById() for this scenario.
To get the value of the dropdown you would need to bind the "selectedGoalcategory" to the dropdown and follow below:
<select [(ngModel)]="selectedGoalCategory">
<option></option>
<option *ngFor="let goalcategory of goalCategories" [value]="goalcategory.id">{{ goalcategory.name }}</option>
</select>
So "preselect" a value, you just set the value of "dropDownValue" to one of the values in the dropdown.
I'm not sure why you need the input field, but if you want the input field to reflect the value of the dropdown, then you would do this:
<input class="input" style="width:10%;" id="TestInput" type="text" [(ngModel)]="selectedGoalCategory" />

Angular 4 select element multiple value and

I am trying to add a select HTML element with multiple attribute set to it in a normal Angular 4 reactive form.
When checked in Chrome developer console, it shows following HTML:
Actual:
<select class="form-control ..." formcontrolname="..." id="..." multiple="" name="..." required="" ...>
<option value="0: 'MBA'" ng-reflect-value="MBA">MBA</option>
<option value="1: 'MSc'" ng-reflect-value="MSc">MSc</option>
</select>
Expected:
<select class="form-control ..." formcontrolname="..." id="..." multiple="" name="..." required="" ...>
<option value="MBA" ng-reflect-value="MBA">MBA</option>
<option value="MSc" ng-reflect-value="MSc">MSc</option>
</select>
Why is the value and ng-reflect-value different.
This is creating problem for me to get selected values, set default values, etc.
This does not happen when multiple attribute is removed.
Any idea what is going wrong here. Yes, I have google this issue but couldn't find any solution.
Edit:
In Component:
//variables
form: FormGroup;
degrees: FormControl;
degree_list = ['MBA', 'MCA', ...];
//through constructor parameters
private _fb = FormBuilder;
//in ngOnInit
this.form = this._fb.group({
...
degrees: this._fb.control('');
...
});
In Template file:
...
<select class="form-control" name="degrees" id="degrees" formControlName="degrees" multiple required>
<option *ngFor="let degree of degree_list" [value]="degree">{{degree}}</option>
</select>
...
According to this: https://github.com/angular/angular/issues/12074#issuecomment-251475820 you can initialize your formcontrol with an empty array. Also since you are using a reactive form, remove the required from your template and instead set the Validator when you build the form:
ngOnInit() {
this.form = this._fb.group({
degrees: [[''], Validators.required]
});
}
Also notice I removed this._fb.control I'm not sure why you are using that :)
StackBlitz

angular2 Select binding when options collection changes

I am using angular2 and have an HTML select like this:
<select class="form-control" id="myList" [(ngModel)]="mySelectedItem">
<option value="">{{ "ALL" }}</option>
<option *ngFor="let item of allItems" [value]="item">{{item}}</option>
</select>
For examples sake let myList= ["option1", "option2", "option3"]
This all works fine until the allItems collection is changed. When the user is first presented with the page, they select "option1" as their preferred option. Now the javascript modifies the allItems collection and removes "option1" as a valid option. At his point the Select shows nothing.
What I would like to happen is that the Select shows the first option "ALL" if the value is of the Select is not a known value in the AllItems collection.
The value of the myList collection are changing automatically based on some other logic in the javascript.
Can someone advise how to achieve this?
You may try below,
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
template: `<h3 class="title">Basic Angular 2</h3>
<hr />
<select class="form-control" id="myList" [(ngModel)]="mySelectedItem">
<option value="all">{{ "ALL" }}</option>
<option *ngFor="let item of allItems" [value]="item">{{item}}</option>
</select>
<button (click)="updateLIst()" >Update</button>
`
})
export class AppComponent {
mySelectedItem= 'all'
allItems = ["option1", "option2", "option3"];
constructor(){}
updateLIst(){
this.allItems = [ "option2", "option3"];
this.mySelectedItem = 'all';
}
}
wherever allItems changes set mySelectedItem to all in your java script code.
Here is the Plunker!!
Hope this helps!!

Angular 2 Dropdown Options Default Value

In Angular 1 I could select the default option for a drop down box using the following:
<select
data-ng-model="carSelection"
data-ng-options = "x.make for x in cars" data-ng-selected="$first">
</select>
In Angular 2 I have:
<select class="form-control" [(ngModel)]="selectedWorkout" (ngModelChange)="updateWorkout($event)">
<option *ngFor="#workout of workouts">{{workout.name}}</option>
</select>
How could I select a default option given my option data is:
[{name: 'arm'}, {name: 'back'}, {name:'leg'}] and my value I to default on on is back?
Add a binding to the selected property, like this:
<option *ngFor="#workout of workouts"
[selected]="workout.name == 'back'">{{workout.name}}</option>
If you assign the default value to selectedWorkout and use [ngValue] (which allows to use objects as value - otherwise only string is supported) then it should just do what you want:
<select class="form-control" name="sel"
[(ngModel)]="selectedWorkout"
(ngModelChange)="updateWorkout($event)">
<option *ngFor="let workout of workouts" [ngValue]="workout">
{{workout.name}}
</option>
</select>
Ensure that the value you assign to selectedWorkout is the same instance than the one used in workouts. Another object instance even with the same properties and values won't be recognized. Only object identity is checked.
update
Angular added support for compareWith, that makes it easier to set the default value when [ngValue] is used (for object values)
From the docs https://angular.io/api/forms/SelectControlValueAccessor
<select [compareWith]="compareFn" [(ngModel)]="selectedCountries">
<option *ngFor="let country of countries" [ngValue]="country">
{{country.name}}
</option>
</select>
compareFn(c1: Country, c2: Country): boolean {
return c1 && c2 ? c1.id === c2.id : c1 === c2;
}
This way a different (new) object instance can be set as default value and compareFn is used to figure out if they should be considered equal (for example if the id property is the same.
Add this Code at o position of the select list.
<option [ngValue]="undefined" selected>Select</option>
just set the value of the model to the default you want like this:
selectedWorkout = 'back'
I created a fork of #Douglas' plnkr here to demonstrate the various ways to get the desired behavior in angular2.
You Can approach this way:
<option *ngFor="let workout of workouts" [value]="workout.name">{{workout.name}}</option>
or this way:
<option *ngFor="let workout of workouts" [attr.value]="workout.name" [attr.selected]="workout.name == 'leg' ? true : null">{{workout.name}}</option>
or you can set default value this way:
<option [value]="null">Please Select</option>
<option *ngFor="let workout of workouts" [value]="workout.name">{{workout.name}}</option>
or
<option [value]="0">Please Select</option>
<option *ngFor="let workout of workouts" [value]="workout.name">{{workout.name}}</option>
Use index to show the first value as default
<option *ngFor="let workout of workouts; #i = index" [selected]="i == 0">{{workout.name}}</option>
According to https://angular.io/api/forms/SelectControlValueAccessor you
just need the following:
theView.html:
<select [compareWith]="compareFn" [(ngModel)]="selectedCountries">
<option *ngFor="let country of countries" [ngValue]="country">
{{country.name}}
</option>
</select>
theComponent.ts
import { SelectControlValueAccessor } from '#angular/forms';
compareFn(c1: Country, c2: Country): boolean {
return c1 && c2 ? c1.id === c2.id : c1 === c2;
}
Struggled a bit with this one, but ended up with the following solution... maybe it will help someone.
HTML template:
<select (change)="onValueChanged($event.target)">
<option *ngFor="let option of uifOptions" [value]="option.value" [selected]="option == uifSelected ? true : false">{{option.text}}</option>
</select>
Component:
import { Component, Input, Output, EventEmitter, OnInit } from '#angular/core';
export class UifDropdownComponent implements OnInit {
#Input() uifOptions: {value: string, text: string}[];
#Input() uifSelectedValue: string = '';
#Output() uifSelectedValueChange:EventEmitter<string> = new EventEmitter<string>();
uifSelected: {value: string, text: string} = {'value':'', 'text':''};
constructor() { }
onValueChanged(target: HTMLSelectElement):void {
this.uifSelectedValue = target.value;
this.uifSelectedValueChange.emit(this.uifSelectedValue);
}
ngOnInit() {
this.uifSelected = this.uifOptions.filter(o => o.value ==
this.uifSelectedValue)[0];
}
}
Fully fleshing out other posts, here is what works in Angular2 quickstart,
To set the DOM default: along with *ngFor, use a conditional statement in the <option>'s selected attribute.
To set the Control's default: use its constructor argument. Otherwise before an onchange when the user re-selects an option, which sets the control's value with the selected option's value attribute, the control value will be null.
script:
import {ControlGroup,Control} from '#angular/common';
...
export class MyComponent{
myForm: ControlGroup;
myArray: Array<Object> = [obj1,obj2,obj3];
myDefault: Object = myArray[1]; //or obj2
ngOnInit(){ //override
this.myForm = new ControlGroup({'myDropdown': new Control(this.myDefault)});
}
myOnSubmit(){
console.log(this.myForm.value.myDropdown); //returns the control's value
}
}
markup:
<form [ngFormModel]="myForm" (ngSubmit)="myOnSubmit()">
<select ngControl="myDropdown">
<option *ngFor="let eachObj of myArray" selected="eachObj==={{myDefault}}"
value="{{eachObj}}">{{eachObj.myText}}</option>
</select>
<br>
<button type="submit">Save</button>
</form>
You can Use that [ngModel] instead of [(ngModel)]and it is Ok
<select class="form-control" **[ngModel]="selectedWorkout"** (ngModelChange)="updateWorkout($event)">
<option *ngFor="#workout of workouts">{{workout.name}}</option>
</select>
You can do as above:
<select class="form-control"
[(ngModel)]="selectedWorkout"
(ngModelChange)="updateWorkout($event)">
<option *ngFor="#workout of workouts;
let itemIndex = index"
[attr.selected]="itemIndex == 0">
{{workout.name}}
</option>
</select>
In above code as you can see, selected attribute of the repeating option is set on checking index of the repeating loop of list. [attr.< html attribute name >] is used for setting html attribute in angular2.
Another approach will be setting model value in typescript file as :
this.selectedWorkout = this.workouts.length > 0
? this.workouts[0].name
: 'No data found';//'arm'
Add on to #Matthijs 's answer, please make sure your select element has a name attribute and its name is unique in your html template. Angular 2 is using input name to update changes. Thus, if there are duplicated names or there is no name attached to input element, the binding will fail.
I faced the same problem while using angular 11. But finally found a solution.
<option disabled selected value="undefined">Select an Option</option>
complete example with ngFor.
<select name="types" id="types" [(ngModel)]="model.type" #type="ngModel">
<option class="" disabled selected value="undefined">Select an Option</option>
<option *ngFor="let item of course_types; let x = index" [ngValue]="type.id">
{{ item.name }} </option>
</select>
Add binding property selected, but make sure to make it null, for other fields e.g:
<option *ngFor="#workout of workouts" [selected]="workout.name =='back' ? true: null">{{workout.name}}</option>
Now it will work
<select class="form-control" name='someting' [ngModel]="selectedWorkout" (ngModelChange)="updateWorkout($event)">
<option value="{{workout.name}}" *ngFor="#workout of workouts">{{workout.name}}</option>
</select>
If you are using form there should be name field inside select tag.
All you need to do is just add value to the option tag.
selectedWorkout value should be "back" , and its done.
If you don't want the 2-way binding via [(ngModel)], do this:
<select (change)="selectedAccountName = $event.target.value">
<option *ngFor="let acct of accountsList" [ngValue]="acct">{{ acct.name }}</option>
</select>
Just tested on my project on Angular 4 and it works! The accountsList is an array of Account objects in which name is a property of Account.
Interesting observation:
[ngValue]="acct" exerts the same result as [ngValue]="acct.name".
Don't know how Angular 4 accomplish it!
Step: 1 Create Properties declare class
export class Task {
title: string;
priority: Array<any>;
comment: string;
constructor() {
this.title = '';
this.priority = [];
this.comment = '';
}
}
Stem: 2 Your Component Class
import { Task } from './task';
export class TaskComponent implements OnInit {
priorityList: Array<any> = [
{ value: 0, label: '✪' },
{ value: 1, label: '★' },
{ value: 2, label: '★★' },
{ value: 3, label: '★★★' },
{ value: 4, label: '★★★★' },
{ value: 5, label: '★★★★★' }
];
taskModel: Task = new Task();
constructor(private taskService: TaskService) { }
ngOnInit() {
this.taskModel.priority = [3]; // index number
}
}
Step: 3 View File .html
<select class="form-control" name="priority" [(ngModel)]="taskModel.priority" required>
<option *ngFor="let list of priorityList" [value]="list.value">
{{list.label}}
</option>
</select>
Output:
You just need to put the ngModel and the value you want selected:
<select id="typeUser" ngModel="Advanced" name="typeUser">
<option>Basic</option>
<option>Advanced</option>
<option>Pro</option>
</select>
For me, I define some properties:
disabledFirstOption = true;
get isIEOrEdge(): boolean {
return /msie\s|trident\/|edge\//i.test(window.navigator.userAgent)
}
Then in the constructor and ngOnInit
constructor() {
this.disabledFirstOption = false;
}
ngOnInit() {
setTimeout(() => {
this.disabledFirstOption = true;
});
}
And in the template I add this as the first option inside the select element
<option *ngIf="isIEOrEdge" [value]="undefined" [disabled]="disabledFirstOption" selected></option>
If you allow to select the first option you can just remove the usage of the property disabledFirstOption
In my case, here this.selectedtestSubmitResultView is set with default value based on conditions and an variable testSubmitResultView must be one and same as testSubmitResultView. This indeed worked for me
<select class="form-control" name="testSubmitResultView" [(ngModel)]="selectedtestSubmitResultView" (ngModelChange)="updatetestSubmitResultView($event)">
<option *ngFor="let testSubmitResultView of testSubmitResultViewArry" [ngValue]="testSubmitResultView" >
{{testSubmitResultView.testSubmitResultViewName}}
</option>
</select>
For More Information,
testSubmitResultViewArry: Array<any> = [];
selectedtestSubmitResultView: string;
getTestSubmitResultViewList() {
try {
this.examService.getTestSubmitResultViewDetails().subscribe(response => {
if (response != null && response !== undefined && response.length > 0) {
response.forEach(x => {
if (x.isDeleted === false) {
this.testSubmitResultViewArry.push(x);
}
if (x.isDefault === true) {
this.selectedtestSubmitResultView = x;
}
})
}
});
} catch (ex) {
console.log('Method: getTestSubmitResultViewList' + ex.message);
}
}
I faced this Issue before and I fixed it with vary simple workaround way
For your Component.html
<select class="form-control" ngValue="op1" (change)="gotit($event.target.value)">
<option *ngFor="let workout of workouts" value="{{workout.name}}" name="op1" >{{workout.name}}</option>
</select>
Then in your component.ts you can detect the selected option by
gotit(name:string) {
//Use it from hare
console.log(name);
}
works great as seen below:
<select class="form-control" id="selectTipoDocumento" formControlName="tipoDocumento" [compareWith]="equals"
[class.is-valid]="this.docForm.controls['tipoDocumento'].valid &&
(this.docForm.controls['tipoDocumento'].touched || this.docForm.controls['tipoDocumento'].dirty)"
[class.is-invalid]="!this.docForm.controls['tipoDocumento'].valid &&
(this.docForm.controls['tipoDocumento'].touched || this.docForm.controls['tipoDocumento'].dirty)">
<option value="">Selecione um tipo</option>
<option *ngFor="let tipo of tiposDocumento" [ngValue]="tipo">{{tipo?.nome}}</option>
</select>