I have a library which contains a component that is a wrapper around a primeng-component. Everything is based on Angular 10.
The primeng-component has a ngModel
. I want to set the ngModel
in the parent-component which accesses the wrapper-component and I want the ngModel
to be 'reflected' back to the parent-component when it's changed down in the primeng-component.
The parent includes the wrapper like this:
parent.component.html
<div class="col">
<wrapper-autocomplete
[suggestions]="projects"
ngDefaultControl
[ngModel]="selectedProject"
(ngModelChange)="changeSelectedProject($event)"
[field]="'name'"
(completeMethod)="getProjects($event)"
[forceSelection]="true">
...
</wrapper-autocomplete>
</div>
In the parent I set [ngModel]
to a variable with the name selectedProject
which is part of the parent-component. The component reacts to changes by using the ngModelChange
where I insert my own function:
parent.component.ts
changeSelectedProject(event: any) {
this.selectedProject = event;
}
In the wrapper-component I include the primeng-component like this (abstracted, doesn't show all properties for readability):
wrapper.component.html
<p-autoComplete
...
[ngModel]="ngModel"
(ngModelChange)="ngModelChangeCallback($event)">
...
</p-autoComplete>
And the ts-part of the code looks like this (also cut down for readability):
wrapper.component.ts
@Component({
selector: 'wrapper-autocomplete',
templateUrl: './autocomplete-list.component.html',
styleUrls: ['./autocomplete-list.component.css']
})
export class AutocompleteListComponent {
@Input() ngModel: any;
@Output() ngModelChange: EventEmitter<any> = new EventEmitter<any>();
ngModelChangeCallback(event: any): void {
this.ngModelChange.emit(event);
}
}
So, this works and does reflect the changes back to the parent, but it is not really what I want to achieve. The component is supposed to be used by other people in the future and I want them to be able to bind the model with only using [(ngModel)]
instead of [ngModel]
and (ngModelChange)=...
.
Do you have any suggestions on what I am doing wrong? I found out about the ControlValueAccessor which seems to be what I need, but I was unable to properly implement it in my code.
Thanks in advance!
As you already know you can implement custom ControlValueAccessor. so just let me just do it for you here
<p-autoComplete
...
[ngModel]="value"
(ngModelChange)="ngModelChangeCallback($event)">
...
</p-autoComplete>
@Component({
selector: 'wrapper-autocomplete',
templateUrl: './autocomplete-list.component.html',
styleUrls: ['./autocomplete-list.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AutocompleteListComponent ),
multi: true,
}
],
})
export class AutocompleteListComponent implements ControlValueAccessor {
onValueChange: any;
value: any;
writeValue(val: any): void {
this.value = val;
}
registerOnChange(fn: any): void {
this.onValueChange = fn;
}
registerOnTouched(fn: any): void {}
setDisabledState?(isDisabled: boolean): void {}
ngModelChangeCallback(event: any): void {
this.onValueChange(event);
}
}
// then in use it like this
<wrapper-autocomplete
[suggestions]="projects"
ngDefaultControl
[(ngModel)]="selectedProject"
[field]="'name'"
(completeMethod)="getProjects($event)"
[forceSelection]="true">
...
</wrapper-autocomplete>
What I did above is implements a custom Control Value Processor. It has 4 methods
Another easy way is to use Banana in a box trick
Just replace your input output to other name instead of ngModel
export class AutocompleteListComponent {
@Input() input: any;
@Output() inputChange: EventEmitter<any> = new EventEmitter<any>();
ngModelChangeCallback(event: any): void {
this.inputChange.emit(event);
}
}
<wrapper-autocomplete
[suggestions]="projects"
[(input)]="selectedProject"> </wrapper-autocomplete>
Yes, ControlValueAccessor
is what you need, but there is another trick for this issues with less code.
Just rename the ngModel
in your child component with Smth
or anything you want and the output will be SmthChange
. Then you can bind [(Smth)]
magically.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.