Reputation: 5720
I want to implement my own Property
directive which should act similar to the existing FormControlName
directive.
It should use the name
of the given Property
to act as if it where given as formControlName
.
interface Property {
name: string;
// more
}
Basically I want these two to be equivalent:
<form [formGroup]="myFormGroup">
<input type="text" [property]="myProperty" />
</form>
<form [formGroup]="myFormGroup">
<input type="text" [formControlName]="myProperty.name" />
</form>
My approach was to extend FormControlName
but that did not work out, as the <input>
element stayed empty.
@Directive({
selector: '[property]',
providers: [
{
provide: NgControl,
useExisting: forwardRef(() => PropertyDirective),
}
],
})
export class PropertyDirective<TProperty extends Property<SimpleValue, string, string>> extends FormControlName {
public readonly property = input.required<TProperty>();
constructor(
@Optional() @Host() @SkipSelf() parent: ControlContainer,
@Optional() @Self() @Inject(NG_VALIDATORS) validators: (Validator | ValidatorFn)[],
@Optional()
@Self()
@Inject(NG_ASYNC_VALIDATORS)
asyncValidators: (AsyncValidator | AsyncValidatorFn)[],
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[],
) {
super(parent, validators, asyncValidators, valueAccessors, null);
effect(() => {
const property = this.property();
if (!property) {
return;
}
this.name = property.name;
});
}
}
How can I create a directive that works like the example template given above?
Upvotes: 1
Views: 60
Reputation: 289
Property
directive with overriding the ngOnChanges
hook to initialize the control without errors, since ngOnChanges
for a formControlName
with an empty name
property will work before effect
.Code:
@Directive({
selector: '[property]',
})
export class PropertyDirective<TProperty extends Property> extends FormControlName{
public readonly property = input.required<TProperty>();
constructor(
@Optional() @Host() @SkipSelf() parent: ControlContainer,
@Optional() @Self() @Inject(NG_VALIDATORS) validators: (Validator | ValidatorFn)[],
@Optional()
@Self()
@Inject(NG_ASYNC_VALIDATORS)
asyncValidators: (AsyncValidator | AsyncValidatorFn)[],
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR) valueAccessors: ControlValueAccessor[],
) {
super(parent, validators, asyncValidators, valueAccessors, null);
}
override ngOnChanges(changes:SimpleChanges) {
this.name = this.property().name;
super.ngOnChanges(changes);
}
}
controlValueAccessor
for Property
since defaultValueAccessor
is bound to specific selectors using formControlName
(example: input:[formControlName]
).Code:
@Directive({
selector: '[property]',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => PropertyValueAccessorDirective),
multi: true,
}
]
})
export class PropertyValueAccessorDirective extends DefaultValueAccessor {}
Upvotes: 1