0

I do have a custom component for template driven form where I use NG_VALUE_ACCESSOR. It works really good. Now I would like to switch to reactive form. StackBlitz Demo

The issue is that how to use a reactive form as a custom component and pass formGroup and formControlName and also how to pass the rest of the other attributes dynamically to the parent component such as required etc.

The required attribute as fare as I know, is being defined in the *.component.ts file. Therefore it would not be possible to set dynamically in the parent as an attribute binding like in template driven form.

I have been searching but unfortunately no significant result.

Any Idea how could I implement reactive form as custom component using NG_VALUE_ACCESSOR and passing attribute dynamically?

k.vincent
  • 3,743
  • 8
  • 37
  • 74

1 Answers1

1

Really I don't know what do you want. Your custom form control work with reactive Forms too (see your custom form control using reactive form)

<form [formGroup]="myForm">
   <app-elements-input formControlName="name" ...>
   </app-elements-input>
</form>
//and 
myForm=new FormGroup(
    {
        name:new FormControl()
    }
  )

Well some times it's necesary know when the control is invalid, touched...

A easy way is add as provider NG_VALIDATORS

{provide: NG_VALIDATORS,
      useExisting:forwardRef(() => InputComponent), 
       multi: true} 

Add one variable, e.g.

control:any=null;

e implements Validator, that's add in the declaration and create a function validate where we give value to "control"

export class InputComponent implements ...,Validator {..}

public validate(c: FormControl) {
  if (!this.control)
    this.control=c;
  return null;
 // return (!this._value && !this.required)?{required:true}:null
}

So you can use in html some like

<span *ngIf="control?.invalid">*</span>

Don't forget indicate when the control is touched, in the example, we can use the event (blur) of the input

<input ... (blur)="onTouched()">

If you want make a component to control a formgroup it's only pass as `@Input()' the fromGroup or the formControl

<form [formGroup]="myForm">
       <children [argformControl]="myForm.get('name')">
       </children>
</form>
//and
@Input() argformControl:FormControl

or

<form [formGroup]="myForm">
       <children [argFormGroup]="myForm">
       </children>
</form>
//and
@Input() argFormGroup:FormGroup

Update this allow as, e.g. if we has an array of object like

data=[{name:'name',label:'Name'},{name:'surname',label:'Surname'}]

make some like

<!--I add a "clasic" *ngIf to avoid initialize problems
   It can be placed in the form, or in children-->
<form *ngIf="myForm" [formGroup]="myForm">
   <children [argFormGroup]="myForm" [data]="data">
   </children>
</form>

And our children becomes

<div *ngFor="let item of data">
{{item.label}}<input [formControl]="argFormGroup.get(item.name)">
</div>
//and 
 @Input() argFormGroup:FormGroup
 @Input() data:any[]

A custom formControl is a "black box". You send a value -a string, a object...- and you can modify this string or object. It's look like a "complex input" (a mat-date-picker, e.g. is a custom form control)

Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • Thanks. But I think I missed to mention the case where I would use more than one input text in the parent component. So, let's say I do have two text fields with two different names. How would 2 different names attributes works? The idea behind this concept is to be able to embed much input fields as want and pass them different names. And I don't need to make any changes in the `*.component.ts` file. I think the `formControlName="name"` should be dynamic. – k.vincent Jun 13 '19 at 09:12
  • I updated my answer. The key is use [formControl], not [formControlName] – Eliseo Jun 13 '19 at 09:50
  • Unfortunately still not working. When I add the input twice with 2 different names e.g. `
    `. the console throws: `ERROR TypeError: Cannot create property 'validator' on string 'name'` and `ERROR TypeError: Cannot create property 'validator' on string 'name2'`. I have been testing it on your StackBlitz Demo.
    – k.vincent Jun 13 '19 at 12:08
  • @k.vincent, You're rigth, we need put a *ngIf to avoid initialize problems -exists "data", but not exist myForm2. So, `
    `. I've just updated the stackblitz. -another way is in children use `....`
    – Eliseo Jun 13 '19 at 15:16
  • Thanks again. Still not there. I want to make this part `name:new FormControl('',Validators.required), surname:new FormControl('',Validators.required)` dynamic. I don't want to touch the `app.component.ts` to add an extra `formControlName` and also the `required` attribute should be defined in the View if I need to make the filed mandatory like` ` not in the TS file. – k.vincent Jun 14 '19 at 07:04
  • again I updated the stackblitz to create the formgroup acording the array data. It's the principle of create dinamic form in Angular. But not try "reinventing the wheel", there a lot of information about this concept – Eliseo Jun 14 '19 at 07:44
  • e.g. in https://stackoverflow.com/questions/56419065/angular-reative-forms-user-customized-forms-error/56432968#56432968 HobojoeBr, ask about similiar question – Eliseo Jun 14 '19 at 08:12
  • I appreciate your help, Thanks!. But to be honest, compared to my concept, this is not working. The form and the input fields including their attributes (important) must be dynamic and using the custom component with `NG_VALUE_ACCESSOR`. In this case and your demo, none of these is possible. The formControlNames are hard coded in the TS file and the attributes can't be added dynamically at all based on `input.component.ts` and `input.component.html`. I would say that custom component with a `NG_VALUE_ACCESSOR` is not a concept for reactive form. – k.vincent Jun 14 '19 at 11:44
  • ...and for the `myForm2` the custom component `input.component.ts` is not being used and instead we are using other directive which is `children` and I do have no idea where it comes form and it function that way. – k.vincent Jun 14 '19 at 11:49