使用 Material 设计和 ReactiveForms 的 Angular2 级联下拉菜单
Angular2 Cascading Dropdowns with Material Design and ReactiveForms
我正在使用 Material Design 和 ReactiveForms 处理级联下拉菜单。该代码有一个州的下拉列表,一旦选择该州将过滤城市的下拉列表。
我找到了这个例子 http://www.talkingdotnet.com/cascading-dropdown-select-list-using-angular-js-2/ 但它没有使用 reactiveForms。
当前屏幕加载没有错误。州的下拉菜单有一个州列表。选择状态时会发生这种情况... ./MainComponent class MainComponent 中的错误 - 内联 template:11:16 原因:无法读取未定义的 属性 'value'
这是状态的界面
export interface IState {
state: string;
}
这是城市的界面
export interface ICity {
state: string;
city: string;
}
这是城市服务
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/distinct';
import 'rxjs/add/operator/catch';
import { ICity } from './city.interface'
@Injectable()
export class CityService {
private _urlCity = '../../api/city.json';
constructor(private _http: Http) { }
getCity(stateName:string): Observable<ICity[]> {
return this._http.get(this._urlCity)
.map((response: Response) => <ICity[]>response.json())
.catch(this.handleError);
}
private handleError(error: Response) {
console.error('I found something bad');
console.error(error);
return Observable.throw(error.json().error || 'Server error ...');
}
}
这是主要成分的HTML`
<div class="card-container">
<md-card>
<md-card-title>
<h3>Testing Cascade Material Design</h3>
</md-card-title>
<md-card-content>
<div *ngIf='allStates'>
<form novalidate [formGroup]="myForm">
<div class="flex-container" fxLayout="row" fxLayoutAlign="left left">
<div class="flex-container" fxLayout="row" fxLayoutGap="20px" fxLayoutAlign="space-around space-around" fxFlex="97%">
<div class="flex-oneStudent" fxFlex="10%">
<md-select placeholder="State" formControlName="state" required="true" (change)="onSelect($event.target.value)">
<md-option *ngFor="let oneState of allStates" [value]="oneState.state">
{{ oneState.state }}
</md-option>
</md-select>
</div>
<div class="flex-oneStudent" fxFlex="20%">
<md-select placeholder="City" formControlName="city" required="true">
<md-option *ngFor="let oneCity of cityByState" [value]="oneCity.city">
{{ oneCity.city }}
</md-option>
</md-select>
</div>
<div fxFlex="67%"> </div>
</div>
</div>
</form>
</div>
</md-card-content>
</md-card>
这里是主要组件
import { Component, OnInit } from '@angular/core';
import { AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { StateService } from '../State/state.service';
import { CityService } from '../City/city.service';
import { IState } from '../State/state.interface';
import { ICity } from '../City/city.interface';
import * as _ from 'lodash';
@Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css']
})
export class MainComponent implements OnInit {
myForm: FormGroup;
allStates: IState[];
cityByState: ICity[];
constructor(public fb: FormBuilder,
private _StateService: StateService,
private _CityService: CityService
) { }
ngOnInit() {
this.myForm = this.fb.group({
state: '',
city: ''
});
this._StateService.getState()
.subscribe(
stateData => this.allStates = _.uniqBy(stateData, 'state')
);
}
onSelect(stateName) {
console.log ('User selected ' + stateName);
this._CityService.getCity(stateName)
.subscribe(
cityData => this.cityByState = _.filter(cityData, function(o) { return o.state == stateName})
);
}
}
这里是 github 的完整代码。
https://github.com/ATXGearHead12/cascade
你需要看看material源代码https://github.com/angular/material2/blob/master/src/lib/select/select.ts#L247
@Output() change: EventEmitter<MdSelectChange> = new EventEmitter<MdSelectChange>();
如您所见,发出的事件带有 MdSelectChange
有效载荷
export class MdSelectChange {
constructor(public source: MdSelect, public value: any) { }
}
所以替换
(change)="onSelect($event.target.value)"
和
(change)="onSelect($event.value)"
* since Angular 6. (change) 贬值以支持 (selectionChange)
see material 2 Breaking Changes > deprecations
(selectionChange)="onSelect($event.value)"
如果您的数据来自数据库并且您想在 angular material 中填充级联下拉列表,这里是完整代码:
html:
<div class="card card-header bg-secondary text-light">Primary address</div>
<div class="form-row">
<div class="form-group col-md-6 senecaform-container">
<mat-form-field>
<mat-label>Country</mat-label>
<mat-select formControlName="primaryAddressCountryCtrl" required>
<mat-option></mat-option>
<mat-option *ngFor="let citem of country" [value]="citem.Id">
{{citem.Name}}
</mat-option>
</mat-select>
<mat-error *ngIf="primaryAddressCountryCtrl.hasError('required')">Please choose birth
country</mat-error>
</mat-form-field>
</div>
<div class="form-group col-md-2 senecaform-container ">
<mat-form-field>
<mat-label>Country</mat-label>
<mat-select formControlName="StateProvince" required>
<mat-option></mat-option>
<mat-option *ngFor="let pitem of provincestatelist" [value]="pitem.Id">
{{pitem.Name}}
</mat-option>
</mat-select>
<!-- <mat-error *ngIf="StateProvince.hasError('required')">Please choose birth
country</mat-error> -->
</mat-form-field>
</div>
你的组件代码:
firstFormGroup: FormGroup;
secondFormGroup: FormGroup;
thirdFormGroup: FormGroup;
country;
provincestatelist;
constructor(
private staticService: StaticService,
private agentService: AgentService,
private _formBuilder: FormBuilder,
private _activatedRoute: ActivatedRoute,
public formValidationService: FormValidationService
) { }
this.staticService.getCountry().subscribe((country: ICountry) => {
this.country = country;
});
this.firstFormGroup = this._formBuilder.group({
primaryAddressCountryCtrl:['', Validators.required],
StateProvince: ['',null]
});
//-----this bellow code is used to set your cascade dropdown----//
this.firstFormGroup.get('primaryAddressCountryCtrl').valueChanges.subscribe(item=>
{
//console.log(item);
this.staticService.getProvinceState(item).subscribe((province: IProvinceState) =>
{
this.provincestatelist = province;
});
});
我正在使用 Material Design 和 ReactiveForms 处理级联下拉菜单。该代码有一个州的下拉列表,一旦选择该州将过滤城市的下拉列表。
我找到了这个例子 http://www.talkingdotnet.com/cascading-dropdown-select-list-using-angular-js-2/ 但它没有使用 reactiveForms。
当前屏幕加载没有错误。州的下拉菜单有一个州列表。选择状态时会发生这种情况... ./MainComponent class MainComponent 中的错误 - 内联 template:11:16 原因:无法读取未定义的 属性 'value'
这是状态的界面
export interface IState {
state: string;
}
这是城市的界面
export interface ICity {
state: string;
city: string;
}
这是城市服务
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/distinct';
import 'rxjs/add/operator/catch';
import { ICity } from './city.interface'
@Injectable()
export class CityService {
private _urlCity = '../../api/city.json';
constructor(private _http: Http) { }
getCity(stateName:string): Observable<ICity[]> {
return this._http.get(this._urlCity)
.map((response: Response) => <ICity[]>response.json())
.catch(this.handleError);
}
private handleError(error: Response) {
console.error('I found something bad');
console.error(error);
return Observable.throw(error.json().error || 'Server error ...');
}
}
这是主要成分的HTML`
<div class="card-container">
<md-card>
<md-card-title>
<h3>Testing Cascade Material Design</h3>
</md-card-title>
<md-card-content>
<div *ngIf='allStates'>
<form novalidate [formGroup]="myForm">
<div class="flex-container" fxLayout="row" fxLayoutAlign="left left">
<div class="flex-container" fxLayout="row" fxLayoutGap="20px" fxLayoutAlign="space-around space-around" fxFlex="97%">
<div class="flex-oneStudent" fxFlex="10%">
<md-select placeholder="State" formControlName="state" required="true" (change)="onSelect($event.target.value)">
<md-option *ngFor="let oneState of allStates" [value]="oneState.state">
{{ oneState.state }}
</md-option>
</md-select>
</div>
<div class="flex-oneStudent" fxFlex="20%">
<md-select placeholder="City" formControlName="city" required="true">
<md-option *ngFor="let oneCity of cityByState" [value]="oneCity.city">
{{ oneCity.city }}
</md-option>
</md-select>
</div>
<div fxFlex="67%"> </div>
</div>
</div>
</form>
</div>
</md-card-content>
</md-card>
这里是主要组件
import { Component, OnInit } from '@angular/core';
import { AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { StateService } from '../State/state.service';
import { CityService } from '../City/city.service';
import { IState } from '../State/state.interface';
import { ICity } from '../City/city.interface';
import * as _ from 'lodash';
@Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css']
})
export class MainComponent implements OnInit {
myForm: FormGroup;
allStates: IState[];
cityByState: ICity[];
constructor(public fb: FormBuilder,
private _StateService: StateService,
private _CityService: CityService
) { }
ngOnInit() {
this.myForm = this.fb.group({
state: '',
city: ''
});
this._StateService.getState()
.subscribe(
stateData => this.allStates = _.uniqBy(stateData, 'state')
);
}
onSelect(stateName) {
console.log ('User selected ' + stateName);
this._CityService.getCity(stateName)
.subscribe(
cityData => this.cityByState = _.filter(cityData, function(o) { return o.state == stateName})
);
}
}
这里是 github 的完整代码。 https://github.com/ATXGearHead12/cascade
你需要看看material源代码https://github.com/angular/material2/blob/master/src/lib/select/select.ts#L247
@Output() change: EventEmitter<MdSelectChange> = new EventEmitter<MdSelectChange>();
如您所见,发出的事件带有 MdSelectChange
有效载荷
export class MdSelectChange {
constructor(public source: MdSelect, public value: any) { }
}
所以替换
(change)="onSelect($event.target.value)"
和
(change)="onSelect($event.value)"
* since Angular 6. (change) 贬值以支持 (selectionChange)
see material 2 Breaking Changes > deprecations
(selectionChange)="onSelect($event.value)"
如果您的数据来自数据库并且您想在 angular material 中填充级联下拉列表,这里是完整代码:
html:
<div class="card card-header bg-secondary text-light">Primary address</div>
<div class="form-row">
<div class="form-group col-md-6 senecaform-container">
<mat-form-field>
<mat-label>Country</mat-label>
<mat-select formControlName="primaryAddressCountryCtrl" required>
<mat-option></mat-option>
<mat-option *ngFor="let citem of country" [value]="citem.Id">
{{citem.Name}}
</mat-option>
</mat-select>
<mat-error *ngIf="primaryAddressCountryCtrl.hasError('required')">Please choose birth
country</mat-error>
</mat-form-field>
</div>
<div class="form-group col-md-2 senecaform-container ">
<mat-form-field>
<mat-label>Country</mat-label>
<mat-select formControlName="StateProvince" required>
<mat-option></mat-option>
<mat-option *ngFor="let pitem of provincestatelist" [value]="pitem.Id">
{{pitem.Name}}
</mat-option>
</mat-select>
<!-- <mat-error *ngIf="StateProvince.hasError('required')">Please choose birth
country</mat-error> -->
</mat-form-field>
</div>
你的组件代码:
firstFormGroup: FormGroup;
secondFormGroup: FormGroup;
thirdFormGroup: FormGroup;
country;
provincestatelist;
constructor(
private staticService: StaticService,
private agentService: AgentService,
private _formBuilder: FormBuilder,
private _activatedRoute: ActivatedRoute,
public formValidationService: FormValidationService
) { }
this.staticService.getCountry().subscribe((country: ICountry) => {
this.country = country;
});
this.firstFormGroup = this._formBuilder.group({
primaryAddressCountryCtrl:['', Validators.required],
StateProvince: ['',null]
});
//-----this bellow code is used to set your cascade dropdown----//
this.firstFormGroup.get('primaryAddressCountryCtrl').valueChanges.subscribe(item=>
{
//console.log(item);
this.staticService.getProvinceState(item).subscribe((province: IProvinceState) =>
{
this.provincestatelist = province;
});
});