ng-dynamic-forms icon indicating copy to clipboard operation
ng-dynamic-forms copied to clipboard

Dynamically populate a controls list or options based on a related controls value

Open steverhoades opened this issue 5 years ago • 2 comments

The purpose of this PR is to allow for the dynamic population of related input values.

Goals:

  • Allow the value of one input to automatically and asynchronously populate the list or options for another input

Configuration

This PR introduces another configuration attribute on the DynamicFormControlModel. The dataProvider attribute contains the following properties.

Name Description
relation Similar to how relations work, you provide a rootPath or id to the related control
relation.id ID of the input control
relation.rootPath The path to the input control from the form group.
service The service that will be used to fetch the data on related input control value change

Usage

Example Configuration

car-details.form.ts
import { CarDataProviderService } from './car-data-provider.service.ts';

export function CarDetailsForm(): DynamicFormControlModel[] {
  return [
    new DynamicSelectModel({
      id: 'make',
      label: 'Make',
      value: '',
      options: [
        {value: '', label: '-- Select a Make'},
        {value: 'chevy', label: 'Chevrolet'},
        {value: 'ford', label: 'Ford'},
        {value: 'toyota', label: 'Toyota'},
      ],
      validators: {
        required: null,
      },
      errorMessages: {
        required: '{{ label }} is required',
      },
    }),
    new DynamicSelectModel({
      id: 'model',
      label: 'Model',
      value: '',
      options: [
        {value: '', label: '-- Select a Model'},
      ],
      dataProvider: {
        relation: {id: 'make'},
        service: CarDataProviderService,
      },
      relations: [
        {
          match: MATCH_DISABLED,
          when: [
            {
              id: 'make', value: '',
            },
          ],
        },
      ],
      validators: {
        required: null,
      },
      errorMessages: {
        required: '{{ label }} is required',
      },
    }),
  ];
}

Create a data provider. The data provider must implement one of two service interfaces DynamicFormControlListDataProvider or DynamicFormControlOptionsDataProvider. Note: The service must return an Observable.

car-data-provider.service.ts
import {Observable, of} from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class CarDataProviderService implements DynamicFormControlOptionDataProvider<string> {

  fetchOptions(value: string): Observer<DynamicFormOptionConfig<string>[]> {
    switch (value) {
      case 'chevy':
        return of([
          {
            value: 'silverado',
            label: 'Silverado',
          },
          {
            value: 'traverse',
            label: 'Traverse',
          },
        ]);
        break;
      case 'ford':
        return of([
          {
            value: 'f150',
            label: 'F150',
          },
          {
            value: 'f250',
            label: 'f250',
          },
        ]);        
        break;
      case 'toyota':
        return of([
          {
            value: 'tacoma',
            label: 'Tacoma',
          },
          {
            value: 'tundra',
            label: 'Tundra',
          },
        ]);
        break;        
    }
  }
}

Note

This PR is based off my previous PR #1054

steverhoades avatar Jan 13 '20 18:01 steverhoades

Codecov Report

Merging #1055 into master will increase coverage by 0.16%. The diff coverage is 95.65%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1055      +/-   ##
==========================================
+ Coverage   90.49%   90.65%   +0.16%     
==========================================
  Files         170      171       +1     
  Lines        2673     2719      +46     
  Branches      247      257      +10     
==========================================
+ Hits         2419     2465      +46     
+ Misses        196      193       -3     
- Partials       58       61       +3
Impacted Files Coverage Δ
...-ngx-bootstrap-form-control-container.component.ts 100% <100%> (ø) :arrow_up:
.../dynamic-basic-form-control-container.component.ts 100% <100%> (ø) :arrow_up:
...c-ng-bootstrap-form-control-container.component.ts 97.5% <100%> (+0.06%) :arrow_up:
.../dynamic-ionic-form-control-container.component.ts 65.78% <100%> (+0.92%) :arrow_up:
...s/core/src/lib/model/dynamic-form-control.model.ts 97.22% <100%> (+0.16%) :arrow_up:
...ynamic-primeng-form-control-container.component.ts 82.35% <100%> (+0.35%) :arrow_up:
...namic-material-form-control-container.component.ts 95% <100%> (+0.12%) :arrow_up:
.../dynamic-kendo-form-control-container.component.ts 77.35% <100%> (+0.43%) :arrow_up:
...ms/core/src/lib/service/dynamic-form-validators.ts 100% <100%> (ø) :arrow_up:
...amic-bootstrap-form-control-container.component.ts 100% <100%> (ø) :arrow_up:
... and 6 more

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 4ea5ace...dff4256. Read the comment docs.

codecov-io avatar Jan 13 '20 18:01 codecov-io

Hi @steverhoades Do you mind to share your experience with dynamic forms in the discussions of a new library to implement advanced features? https://dev.to/myndpm/a-new-approach-to-have-dynamic-forms-in-angular-5d11 Thanks in advance!

matheo avatar Apr 16 '21 03:04 matheo