2017-03-01 53 views
1

我正在嘗試爲Angular 2創建一個自定義複合控件。我的需求是我需要創建一個通用文件選擇器控件,允許用戶使用html5輸入選擇文件或者 [通過向文件輸入一個URL來輸入文件類型=文件] Angular 2自定義複合控件

我決定創建通用表單控件,爲兩個子控件實現ControlValueAccessor接口,因爲它們將在別處單獨使用。

我試圖將它們都包裝在文件選擇器本地或遠程(缺乏更好的單詞)控制。這個外部控制應該負責發佈所選文件的內容,而不關心如何挑選文件。

將價值一直傳播到消費形式很容易。每當子控件向中間控件傳播一個值時,中間控件就會使用registerChange回調函數將其傳遞給消費者。

但是,我無法傳播可能發生在子控件中的驗證錯誤。我需要將錯誤一直傳播到消費形式,以便它們可以被本地化。

E.g.如果用戶在遠程文件選取器子控件中輸入了無效的URL,則該子控件的驗證功能會觸發正確的錯誤。這個錯誤是給中介控制的。我怎樣才能將這個無效URL錯誤一直傳播到消費形式?

更廣泛地說,是否有關於如何在Angular 2中創建複合控件的具體指導?我無法找到包裝其他自定義控件的自定義控件的任何示例,因此我不確定我是否正確地進行了自定義控件。

換句話說,給出:

形式:

outerForm = new FormGroup({ 
    file: new FormControl(null, Validators.required) 
}); 

<form [formGroup]="outerForm"> 
    <File-Picker-Local-Or-Remote formControlName="file"></File-Picker-Local-Or-Remote> 
    <span class="error">******???******</span> 
</form> 

文件選取器-LOCAL-OR-遠程

innerForm = new FormGroup({ 
    local: new FormControl(), 
    remote: new FormControl(null, Validators.pattern('http://...')) 
}); 

<input type="file" formControlName="local" /> 
<input type="text" formControlName="remote" /> 

當遠程子控件驗證失敗,它給它的錯誤代碼到innerForm。如何將這些錯誤消息傳播到外部窗體,以便我可以用適當的驗證消息來替換****** ???

編輯:我要指出,有對我來說相當饑荒早期預警系統的方法來破解溶液或繞過這個問題,包括使用事件的發射器構建自己的解決方案,沒有擺在首位的複合控制等

什麼我真正感興趣的是創造,消費者可以像任何其他形式的控制與互動可重複使用和可擴展的表單控件的角2路,並可以在其他開發者進一步建立打造更高端級別控制。

+0

如果內部表單拋出錯誤並在父窗體中捕獲它,您可以利用事件發射器 –

+0

我已經澄清了我的問題以迴應您的評論。有大量的編程問題和無數的臨時解決方案。我想知道的是創建可重用和可擴展的表單控件的Angular 2方法。例如。將來,我可能希望將包裝成一個名爲的全新控件,該控件還包含一個富文本區。我不應該修改現有的工作表單控件來爲其他父控件提供不同的接口。還是應該? –

+0

我猜你在談論自定義窗體控件? –

回答

2

一位同事和我前一陣子想通了這一點,但這裏是任何人碰到這個絆腳石的解決方案。

的關鍵是在複合部件實現兩個ControlValueAccessor和驗證程序接口。

E.g.

定製日期控制,實現ControlValueAccessor

@Component({ 
    ... 
    providers: [{ 
    provide: NG_VALUE_ACCESSOR, 
    useExisting: CustomDateControl), 
    multi: true 
    }] 
}) 
export class CustomDateControl implements ControlValueAccessor { 
    // implement ControlValueAccessor 
} 

定製時間控制,實現ControlValueAccessor

@Component({ 
    ... 
    providers: [{ 
    provide: NG_VALUE_ACCESSOR, 
    useExisting: CustomTimeControl), 
    multi: true 
    }] 
}) 
export class CustomTimeControl implements ControlValueAccessor { 
    // implement ControlValueAccessor 
} 

定製複合控制的dateTime,同時實現了ControlValueAccessor 驗證

@Component({ 
    ... 
    providers: [{ 
    provide: NG_VALIDATORS, 
    useExisting: CustomDateTimeControl, 
    multi: true 
    }, { 
    provide: NG_VALUE_ACCESSOR, 
    useExisting: CustomDateTimeControl, 
    multi: true 
    }] 
}) 
export class CustomDateTimeControl implements OnInit, ControlValueAccessor, Validator { 
    private propagateChange = function (change) { }; 
    private propagateTouched = function() { }; 

    // Inner controls (you can also use an internal FormGroup for this) 
    public date = new FormControl(); 
    public time = new FormControl(); 

    constructor() {} 

    ngOnInit() { 
    this.date.valueChanges 
     .subscribe(value => { 
     this.propagateChange(value + ' ' + this.time.value); 
     this.propagateTouched(); 
     } 

    this.time.valueChanges 
     .subscribe(value => { 
     this.propagateChange(this.date.value + ' ' + value); 
     this.propagateTouched(); 
     } 
    } 

    writeValue(value) { 
    // Need to update the inner controls, but don't use setValue/patchValue, 
    // as that will trigger valueChanges in the above subscriptions, 
    // incorrectly calling touched 
    } 

    registerOnChange(fn) { 
    this.propagateChange = fn; 
    } 

    registerOnTouched(fn) { 
    this.propagateTouched = fn; 
    } 

    validate(control) { 
    // Custom logic to validate the parent control. In this case, 
    // we may choose to union all childrens' errors. 

    let errors = Object.assign(this.localControl.errors || {}, this.remoteControl.errors || {}); 
    return Object.keys(errors).length ? errors : null; 
    } 
} 

要我回答我自己最初的問題,一個好辦法,泡沫錯誤了複合控件鏈是實現驗證這些複合控件,並有自己的驗證函數返回的子控件的錯誤的某種組合。

我希望這對他人有用。