问题
I have to create a control/template/common html which will have a drop down. This common control will call my data service and bind this drop down with data.
I can create a component and have it's HTML as drop down and call the selector of the component in all my parent component. But, what I want is I need to pass key to this common control and this key will be sent to service and service will give only matching record and as per these records Dropdown will get bind.
Here what my pseudo code code is :
<div class="form-group">
<label class="col-md-2 control-label">Forms</label>
<div class="col-md-3">
<select class="form-control">
<option value="Expression">Expression</option>
<option value="Tender">Tender</option>
<option value="Other">Other</option>
</select>
</div>
</div>
Instead of this i want something like this:
<div class="form-group">
<label class="col-md-2 control-label">Forms</label>
<div class="col-md-3">
COMMONCONTROL('KEY')
</div>
</div>
What is the way to achieve this in angular 2?
Thanks
回答1:
You should just create a component which implements the ControlValueAccessor interface and then connect it with the ngModel
of the select element. For better usage i would suggest to implement an abstract class first:
import { Provider, forwardRef, Type, OpaqueToken } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
export abstract class AbstractValueAccessor implements ControlValueAccessor {
private _value: any = null;
public onChange = (_) => { };
public onTouched = () => { };
public get value(): any {
return this._value;
}
public set value(v: any) {
if (v !== this._value) {
this._value = v;
this.onChange(v);
}
}
public writeValue(value: any) {
this._value = value;
this.onChange(value);
}
public registerOnChange(fn: (_: any) => void): void {
this.onChange = fn;
}
public registerOnTouched(fn: () => void): void {
this.onTouched = fn;
}
}
export function MakeProvider(type: any) {
return {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => type),
multi: true
};
}
More information about how an implemented value accessor looks like and works can be found here. For info about the various accessor implmentations in angular look here.
And then extend your component with it:
import { Component, ChangeDetectionStrategy, Input, SimpleChanges, OnChanges } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { AbstractValueAccessor, MakeProvider } from './abstract-value-accessor';
@Component({
selector: 'custom-select',
templateUrl: './custom-select.component.html',
providers: [MakeProvider(CustomSelectComponent)],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CustomSelectComponent extends AbstractValueAccessor implements OnChanges {
private data$$ = new BehaviorSubject<any[]>([]);
@Input()
public dataKey: string;
public data$ = this.data$$.asObservable();
public get hasData$() {
return this.data$.map(data => data.length > 0);
}
constructor(private dataService: MyDataService) {
super();
}
ngOnChanges(changes: SimpleChanges) {
if(changes.dataKey && changes.dataKey.currentValue) {
this.getData(changes.dataKey.currentValue);
}
}
private getData(dataKey: string) {
this.dataService.get(dataKey).take(1).subscribe(data => this.data$$.next(data));
}
}
And in your component's template you bind it to the select:
<select [(ngModel)]="value" *ngIf="hasData$ | async">
<option [value]="option.value" *ngFor="let option of data$ | async">{{ option.name }}</option>
</select>
Then you can use it where ever you want like:
<custom-select dataKey="test"></custom-select>
Note: This code is untested and only offers a way in the right direction.
来源:https://stackoverflow.com/questions/45480256/how-can-i-create-a-common-control-in-angular-2-4