Угловой материал - компонент пользовательского автозаполнения
Я пытаюсь создать свой собственный компонент углового материала, который мог бы работать с контролем mat-form-field
.
Добавив к этому, я бы хотел, чтобы элемент управления использовал директиву mat-autocomplete
.
Моя цель состоит в том, чтобы создать более mat-autocomplete
компонент mat-autocomplete
со встроенной кнопкой clear и кнопкой css, как показано на следующем рисунке. Я успешно получил его, используя стандартный компонент и добавил то, что хотел, но теперь хочу экспортировать его в общий компонент.
Я использую официальную документацию по угловым материалам для создания своего собственного поля управления полем, а также еще одну публикацию о ней, которая мне уже очень помогла:
В настоящее время я сталкиваюсь с рядом проблем, которые, как я считаю, связаны:
- Моя форма недействительна, даже если значение выбрано правильно.
- Заполнитель не настроен правильно после выбора опции.
- Опция автозаполнения фильтра не работает вообще
- Фокусировка не срабатывает правильно, если я не нажимаю на вход специально.
Я считаю, что мои первые три проблемы вызваны значением автозаполнения, которое не связано с моей реактивной формой правильно.
Вот прямая ссылка на персонализированный публичный репозиторий с проектом (так как проблема немного велика для отображения здесь): Git Repository: 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
внутри этой формы, чтобы заменить первое поле.
Вот моя попытка кода обобщенного компонента (упрощенного):
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
Наконец-то решил !!!
- Проблема здесь в том, что при создании поля ввода в дочернем элементе [SigaAutoCompleteComponent] родитель должен знать о значении, заполненном дочерним элементом [CreateDossierComponent], эта часть отсутствует, что является причиной, по которой она не может перейти в действительную, поскольку она считает, что вход поле не коснулось [остается недействительным] - решается путем испускания значения родительскому элементу, а затем при необходимости управляет элементом управления формой.
- Выделение поля формы мата и ввода вызвало проблему - решалось путем перемещения элемента mat-form-field к дочернему элементу, другой код остается нетронутым, и это решает как перекрытие заполнителя, так и щелчок на значке стрелки для отображения
-
Это может быть done- [один способ выполнения путем перепроектирования], путем ввода услуги в дочерний компонент и выполнения функции автозаполнения там [я этого не реализовал, но это будет работать, как только копирование поля отдела)
в 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);
создание-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 для динамического отображения [который зависит от значений formcontrol], мы можем передать formcontrol от родителя к дочернему, что даже исключает необходимость испускать входное значение от дочернего к родительскому, задавая значение в родительском компоненте и [поле mat-hint остается в самом родительском компоненте]