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.
Related
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>
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 */);
}
}
}
Good day developers , im working in this app with angular , and now im trying to once one of the options
get clicked , to show a modal tag .
Basically what i did was create a paralell template equal to the item selected on the dropdown , and over this template using the a tag i set all the logic to show the modal, but guess isn't user friendly cause of couple extra clicks.Trying to set the a tag inside the options also wasn't viable cause the my dropdown didn't work.Here a mock about what i did:
HTML tag
<select [hidden]="!state" name="optionsInc" required [(ngModel)]="optionsInc" (change)="subItemSelected($event)">
<option value="select" [ngValue]="null" [disabled]="true">Select Income</option>
<option *ngFor="let item of allKeysIncomings" label="{{item}}" value="{{item}}"></option>
</select>====>DROPDOWN LIST LOGIC
<p [hidden]="!state"> <a *ngIf="incomeSelected"
href="#"
class="btn btn-primary btn-block"
data-toggle="modal"
data-target="#editItem"
>{{incomeSelected}}</a>
</p>====>PARALELL REFERENCE TO POP THE MODAL UP
<div class="modal fade" id='editItem'>======>MODAL
SOME TAGS AND CODE
</div>
then on my component i did this :
imports...
#Component({
selector: 'app-user-sheet-balance',
templateUrl: './user-sheet-balance.component.html',
styleUrls: ['./user-sheet-balance.component.css'],
})
export class UserSheetBalanceComponent implements OnInit {
allKeysIncomings: any;==>ITERABLE
incomeSelected: string;
constructor(some code) {}
ngOnInit(): void {some code}
async subItemSelected(event) {
SOME CODE
return (
await (this.incomeSelected = event.target.value),
);
}
All this process does the task on activate the modal once i click the tag a, but instead of creating that paralell reference to the dropdown, im wondering if is possible to do it straight from the dropdown in fact.
I have been watching some similar issues on the comunity like
:Open a Modal Using an Option from a Dropdown - Angular 2 + ngx
but doesn't work on my code specifications.
Any updated idea about this ?.Thanks in advance!!!!!!
if you have Component with dialog layout in ModalComponent it should work as follow
import { Injectable } from '#angular/core';
import { MatDialog, MatDialogRef } from '#angular/material/dialog';
import { ModalComponent } from './modal/modal.component';
#Injectable({
providedIn: 'root'
})
export class TestDialogService {
dialogRef: MatDialogRef<ModalComponent, any>;
constructor(public dialog: MatDialog) { }
open() {
if(this.dialogRef) {
this.dialogRef.close();
}
this.dialogRef = this.dialog.open(ModalComponent, {
panelClass: 'app-dialog'
});
}
close() {
if(this.dialogRef) {
this.dialogRef.close();
}
}
}
// html
<mat-form-field>
<mat-label>Favorite car</mat-label>
<select name="optionsInc"
matNativeControl
[(ngModel)]="optionsInc"
(ngModelChange)="onModelChange()">
<option value="select" [value]="null" [disabled]="true">Select Income</option>
<option *ngFor="let item of allKeysIncomings" [label]="item.viewValue"
[value]="item.value"></option>
</select>
</mat-form-field>
// ts
#Component({
selector: 'app-root',
templateUrl: "./app.component.html",
styleUrls: ["./app.component.scss"]
})
export class AppComponent {
state = false;
optionsInc = null;
allKeysIncomings = [
{value: 'volvo', viewValue: 'Volvo'},
{value: 'saab', viewValue: 'Saab'},
{value: 'mercedes', viewValue: 'Mercedes'}
];
constructor(
public testDialogService: TestDialogService) {
}
onModelChange() {
this.testDialogService.open();
}
}
example
In Angular(5), I'm trying to write a reusable group of form controls for a template driven form. It needs to be able to take a model from a parent component and pass it to the element for two-way data binding.
Here's what I have.
admin-panel.component.ts (Parent Form)
import { Component, OnInit } from '#angular/core';
import{ NgForm } from '#angular/forms';
#Component({
selector: 'admin-panel',
template: `
<label>Settings</label>
<!-- the below doesn't work, but is an example of how I'd like to use it -->
<settings name="settings" [(ngModel)]="preset.settings"></settings>
`
})
export class AdminPanelComponent implements OnInit
{
preset;
ngOnInit()
{
this.preset = {
name: '',
settings: {
settingOne: 'foo',
settingTwo: false,
settingThree: 14
}
}
}
settings.component.html
For the ngModels below, I've also tried to set it like model.settingOne,model.settingTwo, but this didn't work either.
<div [ngModelGroup]="group">
<select name="settingOne" [(ngModel)]="model.settingOne">
<option value="foo">Foo</option>
<option value="bar">Bar</option>
</select>
<input type="checkbox" name="settingTwo" [(ngModel)]="model.settingTwo">
</div>
settings.component.ts
import { Component, Input } from '#angular/core';
import { ControlContainer, NgForm } from '#angular/forms';
#Component({
selector: 'settings',
templateUrl: './settings.component.html',
viewProviders:[{provide: ControlContainer, useExisting: NgForm}]
})
export class SettingsComponent
{
#Input('name') group: string;
//#Input('model') model;
}
Ah.. silly mistake. The above code indeed works with a couple minor changes.
First, in admin-panel.component.ts, replacing the line
<settings name="settings" [(ngModel)]="preset.settings"></settings>
with
<settings name="settings" [(model)]="preset.settings"></settings>
to match the input variable in settings.component.ts was on the right track.
But, it appeared to not work because in my form I had
[ngFormOptions]="{ updateOn: 'submit' }", so nothing was getting updated. Removing that allowed the updates to come through.
If one implements "ControlValueAccessor" on any component then we can have two-way data binding automatically for your custom component.
Implementation here
I'm working with optgroup elements where I want selection of option elements from one group to react differently and pass along different data than others.
But the handler methods I've set aren't firing here. Am I missing something, or is setting click events on option elements not supported?
import {Component} from 'angular2/core';
import {NgFor} from 'angular2/common';
import {City, County, RegionalData} from './interfaces';
import {api} from './api.service';
#Component({
selector: 'geo-selector',
template: `
<div>
<select name="region-selector">
<option disabled="disabled" selected="selected">Select a city/county</option>
<optgroup label="Cities">
<option *ngFor="#city of cities" [value]="city" (click)="onCitySelect(city)">{{city.name}}</option>
</optgroup>
<optgroup label="Counties">
<option *ngFor="#county of counties" [value]="county" (click)="onCountySelect(county)">{{county.name}}</option>
</optgroup>
</select>
<label for="">Zip Search</label>
<input type="text" name="zip-search"/>
</div>
`,
providers: [api],
directives: [NgFor]
})
export class GeoSelector {
public cities: City[];
public counties: County[];
public selectedRegion: any;
constructor(private _api:api) {
this.getRegions();
}
getRegions(): void {
this._api.getRegions().then((data: RegionalData) => {
let cities = this.cities = data.cities;
let counties = this.counties = data.counties;
this.selectedRegion = cities[0] ? cities[0] : counties[0];
});
}
onCitySelect(region) : void {
console.log(region);
}
onCountySelect(region): void {
console.log(region);
}
}
The option element doesn't fire click events. This is not a limitation of Angular2 but a browser limitation.
You can listen to the change event of the select element instead and the from there figure out what option element was clicked.
This non-angular2 example works in Firefox 43.0.2, but not in Chrome 47. Perhaps you're seeing a similar behavior?
<select>
<option></option>
<option onclick="alert('option test');">Test Option</option> <!-- does not work -->
</select>
<button onclick="alert('button test');">Test Button</button> <!-- works -->
https://jsfiddle.net/t7xzxrtw/2/
(Note: Quick example, not production quality code)