I have a <form>
that uses the Template Driven approach.
The form contains a mix of native controls, like <input>
and <select>
, and also wrapper components, like <text-control>
and <checkbox-control>
that contains the native <input>
element inside of it.
How can the form access the <text-control>
's native element to read its error and touched state?
Also, if I want to place a validator directive on that wrapper component and have it pipe-down to the native <input>
, how can I approach this?
I tried to use ngModel
on the wrapper component, but it doesn't work, since ngModel
is hooking to the wrapper component, not the underlying <input>
element.
Another approach was to use <ng-content>
, but the underlying native element takes a lot of attributes, so much that it'll be a pain to copy-n-paste it everywhere.
Example:
<checkbox-control>
<!-- Now, it's easy for the form to access the control. -->
<!-- But I've too many attributes and properties that go here. -->
<input type="checkbox">
</checkbox-control>
PS: I am not looking to use ElementRef to access the native element, I just want the <form>
to be aware of the native elements' error state, so that I can tell whether the form is valid or not.
Example:
<form #editor="ngForm">
<input type="text" validateText />
<select validateSelect>
<option value="1"></option>
<option value="2"></option>
</select>
<!-- Does not work. -->
<checkbox-control validateCheckbox></checkbox-control>
</form>
Thanks in advance.
CodePudding user response:
You can use element's local reference in template approach. Please refer to this code I just wrote to explain :
https://stackblitz.com/edit/angular-ivy-7no9ok?file=src/app/app.component.html
CodePudding user response:
I found a reasonable solution that works without writing much code, and without editing too many components.
Step 1: Create your validator
@Directive({ selector: '[assertNoSpecialChars]' })
export class SpecialCharacterValidator implements Validator {
// The directive will have a reference for the name of the form's control.
@Input() assertNoSpecialChars: string = '';
validate(group: FormGroup): ValidationErrors | null {
const control = group.controls[this.assertNoSpecialChars];
// For simplicity, let's say we don't want the dollar sign in our input.
if (control.value.includes('$')) {
return { invalid: true };
} else {
return null;
}
}
}
Step 2: Apply the directive on your form
<form #f="ngForm" [assertNoSpecialChars]="'username'">
<text-control></text-control>
</form>
Step 3: Bind the input event of your component to the hosting form component
<form #f="ngForm" [assertNoSpecialChars]="'username'">
<text-control (input)="updateFormManually($event, 'username')"></text-control>
</form>
Step 4: Get a reference for your NgForm, and implement the update mechanism
@Component({})
export class FormComponent implements AfterViewInit {
// Grab a reference for your NgForm.
@ViewChild('f', { static: true }) f!: NgForm;
// Create your form control.
username: FormControl = new FormControl();
// Register your control to the NgForm.
ngAfterViewInit(): void {
this.f.form.addControl('username', this.username);
}
// Update the control manually.
updateFormManually(event: any, controlName: string): void {
this.f.form.controls[controlName].setValue(event.target.value);
}
}
Now, the form's validity state, and error messages will be correctly bound.