私は、独自のカスタムangular mat-form-field
コントロールで動作できる素材コンポーネントを作成しようとしています。
それに加えて、コントロールでmat-autocomplete
ディレクティブを使用したいと思います。
私の目的は、統合されたclear-buttonとカスタムcss矢印で見栄えの良いmat-autocomplete
コンポーネントを作成することです。次の画像のように。標準コンポーネントを使用して正常に取得し、必要なものを追加しましたが、今は汎用コンポーネントにエクスポートします。
私は公式のangularマテリアルドキュメントを使用して、独自のフォームフィールドコントロールを作成し、別のSO
私は現在、私がリンクしていると思ういくつかの問題に直面しています:
最初の3つの問題は、リアクティブフォームに正しくリンクされていないオートコンプリート値が原因であると考えています。
ここに、プロジェクトの個人公開リポジトリへの直接リンクがあります(ここでは問題が少し大きいため): Gitリポジトリ:https://github.com/Tenmak/material 。
基本的に、アイデアはこれを変換することです:
<mat-form-field>
<div fxLayout="row">
<input matInput placeholder="Thématique" [matAutocomplete]="thematicAutoComplete" formControlName="thematique" tabindex="1">
<div class="mat-select-arrow-wrapper">
<div class="mat-select-arrow" [ngClass]="{'mat-select-arrow-down': !thematicAutoComplete.isOpen, 'mat-select-arrow-up': thematicAutoComplete.isOpen}"></div>
</div>
</div>
<button mat-button *ngIf="formDossier.get('thematique').value" matSuffix mat-icon-button aria-label="Clear" (click)="formDossier.get('thematique').setValue('')">
<mat-icon>close</mat-icon>
</button>
<mat-hint class="material-hint-error" *ngIf="!formDossier.get('thematique').hasError('required') && formDossier.get('thematique').touched && formDossier.get('thematique').hasError('thematiqueNotFound')">
<strong>
Veuillez sélectionner un des choix parmi les options possibles.
</strong>
</mat-hint>
</mat-form-field>
<mat-autocomplete #thematicAutoComplete="matAutocomplete" [displayWith]="displayThematique">
<mat-option *ngFor="let thematique of filteredThematiques | async" [value]="thematique">
<span> {{thematique.code}} </span>
<span> - </span>
<span> {{thematique.libelle}} </span>
</mat-option>
</mat-autocomplete>
これに:
<mat-form-field>
<siga-auto-complete placeholder="Thématique" [tabIndex]="1" [autoCompleteControl]="thematicAutoComplete" formControlName="thematique">
</siga-auto-complete>
<mat-hint class="material-hint-error" *ngIf="!formDossier.get('thematique').hasError('required') && formDossier.get('thematique').touched && formDossier.get('thematique').hasError('thematiqueNotFound')">
<strong>
Veuillez sélectionner un des choix parmi les options possibles.
</strong>
</mat-hint>
</mat-form-field>
<mat-autocomplete #thematicAutoComplete="matAutocomplete" [displayWith]="displayThematique">
<mat-option *ngFor="let thematique of filteredThematiques | async" [value]="thematique">
<span> {{thematique.code}} </span>
<span> - </span>
<span> {{thematique.libelle}} </span>
</mat-option>
</mat-autocomplete>
現在、最初のリアクティブフォームを表示する「関係書類」フォルダで作業しています。そして、このフォーム内でカスタムコンポーネントautocomplete.component.ts
を直接使用して、最初のフィールドを置き換えています。
汎用コンポーネント(simplified)のコードでの私の試みは次のとおりです。
class AutoCompleteInput {
constructor(public testValue: string) {
}
}
@Component({
selector: 'siga-auto-complete',
templateUrl: './autocomplete.component.html',
styleUrls: ['./autocomplete.component.scss'],
providers: [
{
provide: MatFormFieldControl,
useExisting: SigaAutoCompleteComponent
},
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SigaAutoCompleteComponent),
multi: true
}
],
})
export class SigaAutoCompleteComponent implements MatFormFieldControl<AutoCompleteInput>, AfterViewInit, OnDestroy, ControlValueAccessor {
...
parts: FormGroup;
ngControl = null;
...
@Input()
get value(): AutoCompleteInput | null {
const n = this.parts.value as AutoCompleteInput;
if (n.testValue) {
return new AutoCompleteInput(n.testValue);
}
return null;
}
set value(value: AutoCompleteInput | null) {
// Should set the value in the form through this.writeValue() ??
console.log(value);
this.writeValue(value.testValue);
this.stateChanges.next();
}
@Input()
set formControlName(formName) {
this._formControlName = formName;
}
private _formControlName: string;
// ADDITIONNAL
@Input() autoCompleteControl: MatAutocomplete;
@Input() tabIndex: string;
private subs: Subscription[] = [];
constructor(fb: FormBuilder, private fm: FocusMonitor, private elRef: ElementRef) {
this.subs.Push(
fm.monitor(elRef.nativeElement, true).subscribe((Origin) => {
this.focused = !!Origin;
this.stateChanges.next();
})
);
this.parts = fb.group({
'singleValue': '',
});
this.subs.Push(this.parts.valueChanges.subscribe((value: string) => {
this.propagateChange(value);
}));
}
ngAfterViewInit() {
// Wrong approach but some idea ?
console.log(this.autoCompleteControl);
this.autoCompleteControl.optionSelected.subscribe((event: MatAutocompleteSelectedEvent) => {
console.log(event.option.value);
this.value = event.option.value;
})
}
ngOnDestroy() {
this.stateChanges.complete();
this.subs.forEach(s => s.unsubscribe());
this.fm.stopMonitoring(this.elRef.nativeElement);
}
...
// CONTROL VALUE ACCESSOR
private propagateChange = (_: any) => { };
public writeValue(a: string) {
console.log('wtf');
if (a && a !== '') {
console.log('value => ', a);
this.parts.setValue({
'singleValue': a
});
}
}
public registerOnChange(fn: any) {
this.propagateChange = fn;
}
public registerOnTouched(fn: any): void {
return;
}
public setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
}
}
最後に解決しました!!!
これは、[再設計による1つの方法]で実行できます。サービスを子コンポーネントに挿入し、オートコンプリート機能を実行します[これは実装していませんが、部門フィールドのコピーとして機能します]
create-doiser.component.htmlで
<!-- </div> -->
<mat-autocomplete #thematicAutoComplete="matAutocomplete" [displayWith]="displayThematique">
<mat-option *ngFor="let thematique of filteredThematiques | async" [value]="thematique">
<span> {{thematique.code}} </span>
<span> - </span>
<span> {{thematique.libelle}} </span>
</mat-option>
</mat-autocomplete>
autocomplete.component.html内
<mat-form-field style="display:block;transition:none ">
<div fxLayout="row">
<input matInput placeholder="Thématique" [matAutocomplete]="autoCompleteControl" (optionSelected)="test($event)" tabindex="{{tabIndex}}">
<div class="mat-select-arrow-wrapper" (click)="focus()">
<div class="mat-select-arrow" [ngClass]="{'mat-select-arrow-down': !autoCompleteControl.isOpen, 'mat-select-arrow-up': autoCompleteControl.isOpen}"></div>
</div>
</div>
</mat-form-field>
autocomplete.component.ts内
in set value emit the value to parent
this.em.emit(value);
create-dosier.component.ts
this.thematique = new FormControl( ['', [Validators.required, this.thematiqueValidator]]
);
this.formDossier.addControl('thematique',this.thematique);
call(event){
this.thematique.setValue(event);
this.thematique.validator=this.thematiqueValidator();
this.thematique.markAsTouched();
this.thematique.markAsDirty();
}
}
これですべての問題が解決します。githubにプッシュしてほしい場合はお知らせください。ありがとう!!
UPDATE:オートコンプリート、mat-hintはすべて正常に動作しています。
入力とmat-form-fieldを一緒にしたくないことを理解しています
しかし、mat-hintが動的に表示される場合(フォームコントロールの値に依存)、フォームコントロールを親から子に渡すことができます。これにより、子から親に入力値を送信する必要がなくなり、親コンポーネントの値を設定できますおよび[mat-hintフィールドは親コンポーネント自体にとどまります]