I recently tried to create my own custom form control using angular 2. The custom control should have 2 inputs, and edit an existing object with a known structure. For example:
class model {
public fieldOne: number;
public fieldSec: number;
}
I followed the good explanation I found here: Guide For Writing custom form controls
It worked alright except the guide didn't mention how I can connect custom control form validation to the form that uses it. Lets look at an simplified example:
The custom control template looks somehow like this:
<form>
<input [(ngModel)]="existingModel.fieldOne">
<input [(ngModel)]="existingModel.fieldSec" required>
</form>
And we use it to edit an existing model which value is:
{
fieldOne: 20,
fieldSec: undefined
}
And we use it in some form in my app where we need the custom control to edit this model:
<form #formVar="ngForm">
<my-custom-control [(ngModel)]="existingModel" required>
</my-custom-control>
</form>
This kind of example works in my app in the matter that I can edit the model.
The problem is that I want to show the user when the form is invalid, and if I look at formVar.valid it will be true even though existingModel.fieldSec
is undefined and it has a required validation on it in the custom control form.
To add validation to a template-driven form, you add the same validation attributes as you would with native HTML form validation. Angular uses directives to match these attributes with validator functions in the framework.
The FormGroup class exposes an API that enables us to set validators dynamically. We need to listen to optionB value changes and based on that we add or remove the validators we require. We also call the control's updateValueAndValidity() method, as we need to recalculate the value and validation status of the control.
I don't know exactly how your custom control behaves, but the following solution is valid even if you're dynamically compiling the component itself.
Even in a scenario of template-driven forms (like yours), the underlying engine still works by exploiting reactive components (FormGroup
and FormControl
). Thus, you can always programmatically change the hierarchies of groups and child controls to propagate the changes as expected.
You could - for instance - expose among others a property for your custom control accepting an NgForm
:
@Input('form') peerForm : NgForm;
@Input('entity') model : any;
Then set the binding in your view:
<form #formVar="ngForm">
<my-custom-control [entity]="existingModel" [form]="formVar">
</my-custom-control></form>
Your component template should look like:
<div>
<input [(ngModel)]="model.fieldOne" #ctrl1="ngModel">
<input [(ngModel)]="model.fieldSec" required #ctrl2="ngModel">
</div>
And, again in your component's code:
@ViewChild('ctrl1') ngModel1 : NgModel;
@ViewChild('ctrl2') ngModel2 : NgModel;
...
ngAfterViewInit(){
// assuming the form does exist (TODO: check if set here)
this.peerForm.control.addControl('entity_fieldOne', this.ngModel1.control);
this.peerForm.control.addControl('entity_fieldSec', this.ngModel2.control);
}
That should suffice. See Plunker here: https://plnkr.co/gb3XroZNoGuZa05e76X0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With