javascript - 如何在 Angular 8 中取消選取 select 元素中的已選取 option 元素
我有以下要求:
- 我有兩個
FormControl物件,分別用於下拉選單mainSelect和subSelect,它們都是required的。 subSelect會根據mainSelect的值而改變。- 當
mainSelect改變為一個subSelect的值未包含的值時,subSelect需要變成invalid,這樣兩個FormControl所屬的FormGroup也會變成invalid。 - 但是,如果來自
subSelect的值被包含,subSelect需要保持它的實際值。
(在 StackBlitz 連結之後描述了一個具體例子。)
我解決這個需求的問題:
如果 mainSelect 的值改變,而 subSelect 的值不包含在其中,subSelect 將取列表中的第一個值,而不是變為 null/invalid。
所以,解決方案將是在瀏覽器中,如果 'subSelect' 的選擇值變為 null 且沒有選擇任何值時。
到目前為止我嘗試的:
我試著創建一個元件並實作 ControlValueAccessor 介面。看起來問題可能出在這裡。我想我並不完全理解它是如何運作的。
我在 YouTube 上觀看了以下影片,並閱讀了與 ControlValueAccessor 相關的文章(1, 2),但仍然無法解決我的問題。
這是我程式碼的一部分:
你也可以在 StackBlitz 上找到它
範例
如果在瀏覽器中,MainSelect 的值是 thirdMainSelect,而 SubSelect 的值是 fifthSubSelect,然後 MainSelect 將其值更改為 firstMainSelect,則 SubSelect 應該沒有選中任何值。
select.component.ts
export class SomeObject {
value: string;
parameters: {[parameterName: string]: string} = {};
}
@Component({
selector: "app-select",
templateUrl: "./select.component.html",
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: SelectComponent,
multi: true
}]
})
export class SelectComponent implements ControlValueAccessor, OnChanges {
@ViewChild("select", {static: true}) select: ElementRef;
@Input() tableId: string;
@Input() filter: { [parameterName: string]: string};
returnedTable: SomeObject[];
onChange: (_: any) => void;
onTouched: () => void;
selected: string;
constructor(private tableService: TableService) { }
loadTable(): void {
this.tableService.getTable(this.tableId, this.filter)
.subscribe(table => {
this.returnedTable = table;
});
}
ngOnChanges(): void {
this.loadTable();
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
writeValue(value: string): void {
this.selected = value;
}
}
select.component.html
<select class="form-control" #select (change)="onChange($event.target.value)">
<option *ngFor="let item of returnedTable" [value]="item.value" [selected]="selected === item.value">{{item.value}}</option>
</select>
app.component.ts
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.scss"]
})
export class AppComponent implements OnInit {
form: FormGroup;
containerObject: ContainerObject;
selectedMainValue: string;
constructor(private tableService: TableService,
private formBuilder: FormBuilder) {
}
ngOnInit(): void {
this.tableService.getContainerObject()
.subscribe(containerObject => {
this.containerObject = containerObject;
this.selectedMainValue = containerObject.mainSelect;
this.initForm();
});
}
private initForm(): void {
this.form = this.formBuilder.group({
mainSelect: [this.containerObject.mainSelect, Validators.required],
subSelect: [this.containerObject.subSelect, Validators.required]
});
this.subscribeToMainSelectChanged();
this.subscribeToSubSelectChanged();
}
onSubmit(): void {
if (this.form.valid) {
this.containerObject.mainSelect = this.form.get("mainSelect").value;
this.containerObject.subSelect = this.form.get("subSelect").value;
this.tableService.saveContainerObject(this.containerObject);
}
}
private subscribeToMainSelectChanged() {
this.form.get("mainSelect").valueChanges
.subscribe(mainSelect => {
this.selectedMainValue = mainSelect;
console.log(this.form.status);
});
}
private subscribeToSubSelectChanged() {
this.form.get("subSelect").valueChanges
.subscribe(() => {
console.log(this.form.status);
});
}
}
app.component.html
<div>
<form id="wrapper" [formGroup]="form" (ngSubmit)="onSubmit()">
<div id="left" class="form-group row">
<label for="mainSelect" class="col-form-label col-sm-2">MainSelect</label>
<div class="col-sm-6">
<app-select
id="mainSelect"
formControlName="mainSelect"
[tableId]="'mainSelectTable'"
[filter]="{firstType: 'firstParameter'}"
></app-select>
</div>
</div>
<div id="right" class="form-group row">
<label for="subSelect" class="col-form-label col-sm-2">SubSelect</label>
<div class="col-sm-6">
<app-select
id="subSelect"
formControlName="subSelect"
[tableId]="'firstParameter'"
[filter]="{firstType: 'firstParameter', secondType: selectedMainValue}"></app-select>
</div>
</div>
<p></p>
<button id="button" type="submit">Submit</button>
</form>
</div>
答案#1
我認為它只使用 valuesChange。如果你有兩個陣列 data 和 subdata,以及一個表單像
form = new FormGroup({
prop1: new FormControl(),
prop2: new FormControl()
});
簡單的
this.form.get("prop1").valueChanges.subscribe(res => {
this.dataService.getData(res).subscribe(data=>{
this.subdata=data;
if (!this.subdata.find(x=>x.value==this.form.get("prop2").value))
this.form.get("prop2").setValue(null);
}).unsubscribe()
});
應該夠了,看看 stackblitz
答案#2
在嘗試僅使用 SelectComponent 尤其是 writeValue 來解決這個問題一段時間後,以下代碼完成了工作:
我將 select.component.ts 修改如下:
export class SomeObject {
value: string;
parameters: {[parameterName: string]: string} = {};
}
@Component({
selector: "app-select",
templateUrl: "./select.component.html",
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: SelectComponent,
multi: true
}]
})
export class SelectComponent implements ControlValueAccessor, OnChanges {
@ViewChild("select", {static: true}) select: ElementRef;
@Input() tableId: string;
@Input() filter: { [parameterName: string]: string};
returnedTable: SomeObject[];
onChange: (_: any) => void;
onTouched: () => void;
selected: string;
constructor(private tableService: TableService) { }
loadTable(): void {
this.tableService.getTable(this.tableId, this.filter)
.subscribe(table => {
this.returnedTable = table;
if (!!this.select && !!this.select.nativeElement.value) {
this.writeValue(this.select.nativeElement.value);
this.onChange(this.selected);
}
});
}
ngOnChanges(): void {
this.loadTable();
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
writeValue(value: string): void {
if (!!this.returnedTable && !this.returnedTable.some(item => item.value === value)) {
this.selected = null;
} else {
this.selected = value;
}
}
}
而 select.component.html 如下所示:
<select class="form-control" #select (change)="onChange($event.target.value)">
<option hidden *ngIf="!selected" value=""></option>
<option *ngFor="let item of returnedTable" [value]="item.value" [selected]="selected === item.value">{{item.value}}</option>
</select>
