I have a add button which will keep on adding a div container which consists of two dropdowns. On selecting on dropdown we are setting other dropdown data.
When I click on add div it will add a second div with both dropdowns. But, change in one dropdown is affecting other dropdown.
app.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
private map = new Map<string, string[]>([
['Poland', ['Warszawa', 'Krakow']],
['USA', ['New York', 'Austin']],
]);
filtersList: any = [{}];
country: string;
city: string;
get countries(): string[] {
return Array.from(this.map.keys());
}
get cities(): string[] | undefined {
return this.map.get(this.country);
}
addFilter() {
this.filtersList.push({});
}
removeFilter(index) {
this.filtersList.splice(index, 1);
}
trackByIndex(index, item) {
return index;
}
}
app.component.html
<div *ngFor="let num of filtersList; let i = index; trackBy: trackByIndex">
<select [(ngModel)]="country">
<option *ngFor="let country of countries" [value]="country">
{{ country }}
</option>
</select>
<select *ngIf="country" [(ngModel)]="city" [value]="city">
<option *ngFor="let city of cities">{{ city }}</option>
</select>
<button (click)="removeFilter()">Remove</button>
</div>
<button (click)="addFilter()">Add more filters</button>
After adding div, each dropdown should have respective selected values.
So, when I click on add div it will add a second div with both dropdowns. But, change in one dropdown is affecting other dropdown.
That's because these two dropdowns share the same variable/storage. You need to give them separate storage. In this case, we create a list of object with selectedCountry and selectedCity.
app.component.ts
// import stuff here
type Filter = {
selectedCountry: string;
selectedCity: string;
};
#Component({
...
})
export class AppComponent {
...
filterList: Filter[] = [];
addFilter() {
this.filterList.push({selectedCountry: '', selectedCity: ''});
}
}
app.component.html
<div *ngFor="let filter of filterList">
<select [(ngModel)]="filter.selectedCountry">
<option *ngFor="let country of countries" [value]="country">
{{ country }}
</option>
</select>
<select *ngIf="country" [(ngModel)]="filter.selectedCity" [value]="city">
<option *ngFor="let city of cities">{{ city }}</option>
</select>
<button (click)="removeFilter()">Remove</button>
</div>
<button (click)="addFilter()">Add more filters</button>
Related
A project contains a component and a service.
Component injects the service and uses services field filter. There is a select in the components hmtl. [(ngModel)] of the select is binded to filter.sizes.width.
The component:
#Component({
selector: 'app-facet-sizes-static',
templateUrl: './facet-sizes-static.component.html',
styleUrls: ['./facet-sizes-static.component.scss']
})
export class FacetSizesStaticComponent implements OnInit {
constructor(
private Search: SearchService
) {
this.filter = Search.filter;
}
clearFilter() {
this.filter.sizes.width = 0;
}
}
Its template:
<div (click)="clearFilter()"></div>
<!-- Comment_01 -->
{{filter.sizes.width}}
<div *ngIf="filter.sizes">
<select name="width"
[(ngModel)]="filter.sizes.width">
<option [ngValue]="null">~</option>
<option *ngFor="let item of facet.sizes[0].parts.width; let x = index"
[ngValue]=item>
{{ item }}
</option>
</select>
</div>
The service:
export class SearchService {
filter: SearchServiceFilter = null;
constructor() {
this.filter = {
sizes: {
width: 0
},
};
}
}
When I change option in select it changes filter.sizes.width as I exapt. But there are two problems:
when component is inited there is no value in the select but filter.sizes.width has 0 value. I can check it watching the string {{filter.sizes.width}} under Comment_01
when the method clearFilter() changes filter.sizes.width there is becomes again no value in the select.
How do I solve it?
It sounds like your facet.sizes[0].parts.width array does not contain a value === 0. I see your option with a value of null, but null does not equal 0 and will not be selected. If you add an option with a value === 0; it should select it.
I am using a filter and need to read the value in order to send an API request with the values in the url.
I use this API. I am able to filter both of the categories. After selecting two, we wanna send an API request with both selected values in the url.
We generated a backend-side script to filter, all I need to do is sending a request with the modified url.
app.component.ts
import { Component } from "#angular/core";
import { HttpClient } from "#angular/common/http";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
lines: any[];
filteredLines: any[];
filterBy;
constructor(private http: HttpClient) {}
ngOnInit() {
this.http.get("https://api.mocki.io/v1/26fce6b9").subscribe(lines => {
this.lines = lines;
this.filteredLines = [...this.lines];
});
}
filter() {
this.filteredLines = [
...this.lines.filter(dropdown => dropdown.name.includes(this.filterBy))
];
}
/** Here I need a script onClick button that reads the selected values and sending a get-request of API link above added with the filtered values:
With click on the submit-button, I want to send the API request.
If api.com/data is the link, the request link would be like api.com/data?line=A&workCenter=1
The "?" is for category Line, and "&" for workCenter.
**/
}
}
app.component.html
<select>
<option>Line</option>
<option *ngFor="let dropdown of filteredLines" (keyup)="filter()">
{{dropdown.line}}
</option>
</select>
<select>
<option>Work Center</option>
<option *ngFor="let dropdown of filteredLines" (keyup)="filter()">
{{dropdown.workCenter}}
</option>
</select>
<form action="" method="post">
<input type="submit" name="request" value="Submit" />
</form>
I have created a Stackblitz project for better understanding.
From your example, you need to add [(ngModel)] which will bind to the selected value.
You can find more info on how to properly use a select here More info
<select [(ngModel)]="selectedLine">
<option>Line</option>
<option *ngFor="let dropdown of filteredLines" (keyup)="filter()">
{{dropdown.line}}
</option>
</select>
<select [(ngModel)]="selectedWorkCenter">
<option>Work Center</option>
<option *ngFor="let dropdown of filteredLines" (keyup)="filter()">
{{dropdown.workCenter}}
</option>
</select>
<form action="" method="post">
<input type="submit" name="request" value="Submit" (click)="Submit()" />
</form>
Then in your ts file, declare the select variable and access value as in the click function Submit.
import { Component } from "#angular/core";
import { HttpClient } from "#angular/common/http";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
lines: any[];
filteredLines: any[];
filterBy;
selectedLine; // For first select
selectedWorkCenter; // For second select
constructor(private http: HttpClient) {}
ngOnInit() {
this.http.get("https://api.mocki.io/v1/26fce6b9").subscribe(lines => {
this.lines = lines;
this.filteredLines = [...this.lines];
});
}
filter() {
this.filteredLines = [
...this.lines.filter(dropdown => dropdown.name.includes(this.filterBy))
];
}
requestAnDieAPI() {
console.log(this.filteredLines); // hier muss mein API post request hin
}
Submit() {
var baseUrl = `https://api.mocki.io/`;
var url = `${baseUrl}data?line=${this.selectedLine}&workCenter=${
this.selectedWorkCenter
}`;
this.http.get(url).subscribe(response => {
// response
});
}
}
It is not clear for me if you want to send multiple { line, workCenter } objects to the API call, but from how you setted up the Stackblitz project I will assume you want to send just a single one.
Never seen a (keyup) on an <option> element, use [(ngModel)] on the <select> instead:
<select [(ngModel)]="selected.line">
<option [value]="null">Line</option>
<option *ngFor="let dropdown of filteredLines" [value]="dropdown.line">
{{dropdown.line}}
</option>
</select>
<select [(ngModel)]="selected.workCenter">
<option [value]="null">Work Center</option>
<option *ngFor="let dropdown of filteredLines" [value]="dropdown.workCenter">
{{dropdown.workCenter}}
</option>
</select>
<!-- the use of the form in this case is not required -->
<!-- since you are using requestAnDieAPI to do the api call -->
<form action="" method="post" (submit)="requestAnDieAPI()">
<input type="submit" name="request" value="Submit" />
</form>
<!-- You can also use just a simple button -->
<button (click)="requestAnDieAPI()">Submit</button>
Then your component (model) should contain a selected variable that map the interface (view)
import { Component } from "#angular/core";
import { HttpClient } from "#angular/common/http";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
lines: any[];
filteredLines: any[];
filterBy;
selected = {
line: null,
workCenter: null
};
constructor(private http: HttpClient) {}
ngOnInit() {
this.http.get("https://api.mocki.io/v1/26fce6b9").subscribe((lines: any[]) => {
this.lines = lines;
// this.filteredLines = [...this.lines];
// better:
this.filteredLines = this.lines.slice();
// or either this.filter() directly
});
}
filter() {
// this.filteredLines = [
// ...this.lines.filter(dropdown => dropdown.name.includes(this.filterBy))
// ];
// this is redundant, better:
this.filteredLines = this.lines.filter(dropdown => dropdown.name.includes(this.filterBy))
}
requestAnDieAPI() {
if (this.selected.line != null && this.selected.workCenter != null) {
let apiUrl = "https://api.com/data?line=" + this.selected.line +
"&workCenter=" + this.selected.workCenter
this.http.get(apiUrl).subscribe(/* Your optional response callback/subscriber here */);
}
}
}
I have worked on angular 4 project, In this project, I have a requirement to set the first option as selected where all options are created dynamically by loop.
html code:
<select [(ngModel)]="selectedServiceType" [ngModelOptions]="{standalone: true}" (ngModelChange)="getServiceType($event)">
<ng-container *ngFor="let service of services">
<option [ngValue]="service">{{service.name}}</option>
</ng-container>
</select>
If anyone know about let me know. Thanks in advance!
Try like this :
<select class="form-control" (change)="onChange($event)">
<option *ngFor="let service of services; let itemIndex = index" [selected]="itemIndex == 0" [ngValue]="service.value">{{service.name}}</option>
</select>
component.ts
export class HomeComponent implements OnInit {
private selectedServiceType: any;
private services: Array<any> = [];
constructor() {
this.services = [{
name: "Harish",
value: 5000
}, {
name: "Chandru",
value: 5001
}]
}
onChange(e) {
this.selectedServiceType = e.target.value;
}
}
Just in your ts, inside ngOnInit
selectedServiceType : any;
ngOnInit() {
//make sure you have values for **`services`**
this.selectedServiceType = services[0];
}
add this code
<select (change)="onChange($event.target.value)" value={{selectedService}}>
<ng-container>
<option *ngFor="let service of services" >{{service.name}}</option>
</ng-container>
</select>
and you component.ts should be
export class YourClass implements OnInit {
selectedService: any;
services:any = [];
--your API call code set values to services array
this.services=this.service.APImethod()
onChange(newValue) {
this.selectedService=newValue;
}
}
When I use a multiple attribute on the select tag, Angular alters the value of the option tags. for instance:
this.options = [
{ id: 1, name: 'me' },
{ id: 2, name: 'you' }
];
<select [(ngModel)]="model" multiple>
<option *ngFor="let o of options" [value]="o.id"> {{o.name}} </option>
</select>
Results in =>
<option value="0: '1'">me</option>
<option value="1: '2'">you</option>
Angular prepends the index of the object in the array to the value of the options tags. Is there anyway to stop this behaviour?
Ok, I ended up with custom directive. I called it multipleNormalize and used it like [multipleNormalize]="o.id"
Typescript
import {Directive, ElementRef} from '#angular/core';
import {Input} from '#angular/core';
#Directive({
selector: '[multipleNormalize]',
})
export class MultipleNormalizeDirective {
#Input('multipleNormalize') model:any;
constructor(private elementRef:ElementRef) {
}
ngOnInit(): void {
this.elementRef.nativeElement.value = this.model;
}
}
HTML
<select [(ngModel)]="testModel" multiple>
<option *ngFor="let o of optionItems" [ngValue]="o.id" [multipleNormalize]="o.id">{{o.name}}</option>
</select>
Use [ngValue] instead of [value].
<select [(ngModel)]="model" multiple>
<option *ngFor="let o of options" [ngValue]="o.id"> {{o.name}} </option>
</select>
Please, can you help me? It is supposed to be easy, but I can't find the solution. There is a form with two selects. When #select1 changes, #select2 needs to show data according to the value of #select1. For example, get cities of each state. Kind of :
//html
<select (change)="select2.getCities($event)" ng-control="userState">
<option *ng-for="#state of states" [value]="state">{{state}}</option>
</select>
<select #select2 ng-control="userCity">
<option *ng-for="#city of cities" [value]="city">{{city}}</option>
</select>
//the Component
#Component({ selector: 'user-management', appInjector: [FormBuilder] });
#View({ templateUrl: 'user-management.html', directives: [NgFor] });
export class userManagement {
constructor(fb: FormBuilder){
this.userForm = fb.group({
userState: [],
userCity: []
});
this.states = ['New York', 'Pennsylvania'];
this.cities = {'New York': ['Albany', 'Buffalo'], 'Pennsylvania':['Pittsburgh', 'Philadelphia']};
}
getCities($event){
return this.cities[$event.target.value];
}
}
This, of course, doesn't work. PLEASE, do you know how it should be done? It's in alpha28.
Great! I found out how to make it work! :) The only thing that was missing, was the form model passed to the event. It should be like this:
<form [ng-form-model]="userForm">
<select (change)="select2.getCities($event, userForm)" ng-control="userState">
<option *ng-for="#state of states" [value]="state">{{state}}</option>
</select>
Answering with Angular 2 latest template syntax and Typescript component
//The Component Type script
import {Component} from 'angular2/core';
import {NgForm} from 'angular2/common';
#Component({ selector: 'states-cities',
template: `
<form (ngSubmit)="onSubmit()" #heroForm="ngForm">
<select ngControl="state" #state="ngForm" (change)="getCities(state)">
<option *ngFor="#state of states" [value]="state" >{{state}}</option>
</select>
<select ngControl="userCity" #select2="ngForm">
<option *ngFor="#city of cities" [value]="city">{{city}}</option>
</select>
</form>
`
})
export class stateCitiesComponent {
states= ['New York', 'Pennsylvania'];
cities = [];
citiesData={'New York': ['Albany', 'Buffalo'], 'Pennsylvania':['Pittsburgh', 'Philadelphia']};
getCities(state){
this.cities=this.citiesData[state.value];
}
}
This is how I would do it on Angular 2 RC5, with a model-driven approach and Observables. This could also be a search field where you then use debounceTime() to not hit your backend on every keystroke or manipulate the input further.
//The Component Type script
import { Component } from '#angular/core';
import { FormControl, FormGroup, FormBuilder } from '#angular/forms';
#Component({
moduleId: module.id,
selector: 'states-cities',
template: `
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
<select formControlName="state">
<option *ngFor="let state of states" [value]="state" >{{state}}</option>
</select>
<select formControlName="userCity">
<option *ngFor="let city of cities" [value]="city">{{city}}</option>
</select>
</form>
`
})
export class stateCitiesComponent {
states:Array<string>;
cities:Array<string>;
citiesData:any;
myForm:FormGroup;
constructor(private formBuilder: FormBuilder) {
this.states = ['New York', 'Pennsylvania'];
this.cities = [];
this.citiesData = {'New York': ['Albany', 'Buffalo'], 'Pennsylvania':['Pittsburgh', 'Philadelphia']};
// Setup Form
this.myForm = this.formBuilder.group({
state: [''],
userCity: ['']
});
// Now setup change detection with an Observable
this.myForm.controls["state"].valueChanges
.debounceTime(100) // wait a litle after the user input (ms)
.subscribe(state => {
this.cities = this.citiesData[state];
});
}
onSubmit() {
// do something
}
}
You can read more about change detection here.