Single binding a FormControl?

460 Views Asked by At

I'm making a form group component for our location services team. This component can have city, state, county, country and zip fields depending on how it is configured.

I want developers to be able to use it either by passing in a single location object with a property for each field that can be bound to the form inputs, and I also want to support passing in a FormGroup that can be bound to the fields.

The issue I'm having is that all the values for each field are coming from an API. We want to auto-populate as much data as we can for our users.

For example, when a user selects a city, it should then populate the state and country but clear the county and zip fields (cities can be in multiple counties or zip codes) while also populating the select option lists for counties and zip codes with associated counties and zip for the selected city. I don't want to get into the business logic other than to say that each field has a set of unique and complicated rules whenever a form value changes.

Using the ngModel approach, I rolled my own store ala redux and dispatch actions on ngModelChange to update the entire location object according to our business rules. This works perfectly.

When I try something like this with the FormGroup on the individual FormControls, I hit the call stack limits as each change to the other fields triggers the updateAndValidate cycle on the FormGroup. I tried patchValue rather than setValue and I used the options to not emitEvent and set selfOnly to true, but still I hit the stack limits.

My template looks like:

<my-input formControlName="city"
          (ngModelChange)="updateControls(
                             { type: 'CITY_CHANGE',
                               value: $event })">
</my-input>

Then in the controller:

updateControls(action) {
  switch(action.type) {
    case "CITY_CHANGE":
      this.formgroup.controls.zip.setValue(undefined);
      this.formgroup.controls.county.setValue(undefined);
      this.formgroup.controls.city.setValue(action.value);
      this.formgroup.controls.state.setValue(// get state & set);
      this.formgroup.controls.country.setValue(//get country & set);
      break;
   case ... // you get the point
  }
}

As I said, I also tried this with patchValue(val, { emitEvent: false, onlySelf: true } and that didn't help me. I'm still hitting call stack limits.

I tried subscribing to valueChanges on the parent FormGroup itself, but it doesn't provide me a list of things that changed, just the new state of the form. This is unacceptable as I need to know what changed so I can update the other fields according to my business rules.

The ideal scenario would be to subscribe to the form and check which property changed, then update the entire state of the form per my business rules. Barring that, being able to single bind the formControlName to an input in such a way that when I register with ngModelChange, its not going to start an infinite loop.

Does anyone have any thoughts or suggestions on how I can deal with this? Am I missing something obvious? I would like to support FormGroup as I'm on a large enterprise project with many teams, and we want our components to integrate with their forms no matter which technique they use.

0

There are 0 best solutions below