I'm currently developing a component with the goal of streamlining and standardizing the appearance and functionality of our forms. The code snippet below illustrates the structure:
Sample Implementation
...
<my-form-input labelKey = "email" controlName="emailAddress" [form]="userForm">
<input myInput class="form-control" type="email" formControlName="emailAddress" />
</my-form-input>
...
In the code snippet above, "emailAddress"
is passed to MyFormInputComponent
as the controlName
and is then passed again to the FormControlName
directive on the <input>
element. I would like to streamline this process by only passing it once to enhance user experience.
Is there an effective way to achieve this, or should I simply accept this constraint? An explanation regarding the reason behind this constraint would be appreciated. The code is presented below.
I have explored two approaches:
- Implementing a
annotation in the@HostBinding("attr.formControlName")
MyInput
component. While this method allows me to manipulate an attribute called
for theformcontrolname</code, it does not trigger the Angular Forms directive required to properly register the control with the group.</li> <li>Requesting the user to provide <code>formControlName
<input>
element and extracting the value for the rest of the component. This approach may work, but it would involve accessing the DOM directly through anElementRef
, which is not recommended. The recommended method for DOM interaction --Renderer
-- does not appear to offer any attribute reading capabilities.
my-form-input.component.ts
@Component({
selector: 'my-form-input',
templateUrl: './my-form-input.component.html',
styleUrls: ['./my-form-input.component.scss']
})
export class MyFormInputComponent implements OnInit, AfterContentInit {
@Input()
labelKey: string;
@Input()
controlName: string;
@Input()
form: FormGroup;
@ContentChild(MyInputDirective)
input: MyInputDirective;
ngAfterContentInit(): void {
this.initInput();
}
/**
* Updates the input we project into the template
*/
private initInput() {
this.input.updatePlaceholder(this.labelKey);
// I would like to modify the FormControlName directive applied to the input here
}
}
my-form-input.component.html
<label>{{ labelKey | translate }}</label>
<ng-content></ng-content>
<my-input-error [control]="form.controls[controlName]" [name]="labelKey | translate" />
my-input.directive.ts
@Directive({
selector: '[myInput]'
})
export class myInputDirective implements OnInit {
private placeholderKey = "";
@HostBinding("placeholder")
private placeholder: string;
updatePlaceholder(placeholderKey: string) {
this.placeholderKey = placeholderKey;
this.placeholder = this.translateService.instant(this.placeholderKey);
}
constructor(private translateService: TranslateService) {
}
}
my-form-error.component.ts
// Not shown since not relevant.