1.0.1 • Published 7 months ago

@skynet-ng/dynamic-form v1.0.1

Weekly downloads
-
License
MIT
Repository
github
Last release
7 months ago

@skynet-ng/dynamic-form

An extension for Angular Reactive forms to render forms dynamically.

About

Reactive forms compatible dynamic form called upon to facilitate work with form, render a form from a config (from JSON, from Back-end response, etc). Works with ControlValueAccessor powered components. Supports all the functionality owned by Angular Reactive Forms.

Showcase

See showcase here

Showcase source code

Get started

Step 1: Install the lib.

npm install @skynet-ng/dynamic-form

Step 2: Import DynamicFormModule into your module

import { NgModule } from '@angular/core';
import { DynamicFormModule } from '@skynet-ng/dynamic-form';

@NgModule({
  imports: [
    ...
    DynamicFormModule
  ],
  ...
})
export class SomeModule {}

Step 3: Create ControlValueAccessor component. Example of such a component is here. Let's implement TextfieldComponent.

/* eslint-disable @typescript-eslint/no-empty-function */
import { Component, forwardRef, Input } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'textfield-control',
  template: `
    <div class="flex flex-col gap-1">
      <label class="block text-sm font-medium text-gray-700">{{ label }}</label>
      <input
        class="block w-full border border-gray-300 rounded-md sm:text-sm shadow-sm outline-indigo-500 p-3"
        [disabled]="isDisabled"
        [type]="type"
        [(ngModel)]="value"
        [placeholder]="placeholder"
        [disabled]="isDisabled"
      />
    </div>
  `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => TextfieldComponent),
      multi: true,
    },
  ],
})
export class TextfieldComponent implements ControlValueAccessor {
  private _value: string;

  get value(): any {
    return this._value;
  }
  set value(value: any) {
    if (value !== this._value) {
      this._value = value;
      this.onChange(value);
    }
  }

  isDisabled: boolean;

  @Input()
  label: string;

  @Input()
  placeholder = '';

  @Input()
  type: 'text' | 'number' = 'text';

  onChange = (_) => {};

  onTouch = () => {};

  writeValue(value: any): void {
    this._value = value;
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouch = fn;
  }

  setDisabledState(isDisabled: boolean) {
    this.isDisabled = isDisabled;
  }
}

Step 4: In your component's ts file you want to implement form in, define form via DynamicFormGroup. After dynamic form is defined, pass it through dynamicFormGroup input in dynamic-form-outlet component.

import { Component } from '@angular/core';
import { DynamicControl, DynamicFormGroup } from '@skynet-ng/dynamic-form';
import { TextfieldComponent } from './components';

@Component({
  selector: 'example-app-root',
  template: `
    <div class="flex justify-center items-center absolute w-full h-full bg-gray-200">
      <div class="bg-white flex flex-col w-96 border-gray-300 border border-solid p-5 rounded-md gap-4">
        <form class="flex flex-col gap-4">
          <dynamic-form-outlet [dynamicFormGroup]="dynamicFormGroup"></dynamic-form-outlet>
        </form>
        <button class="self-end h-10 inline-flex justify-center rounded-md border border-transparent bg-indigo-600 py-2 px-4 text-sm font-medium text-white shadow-sm hover:bg-indigo-700">
          Submit
        </button>
      </div>
    </div>
  `
})
export class AppComponent {
  dynamicFormGroup = new DynamicFormGroup({
    name: new DynamicControl({
      initialInputs: {
        label: 'Name',
        placeholder: 'Enter your name'
      }
    }, TextfieldComponent),
    surname: new DynamicControl({
      initialInputs: {
        label: 'Surname',
        placeholder: 'Enter your surname'
      }
    }, TextfieldComponent),
    age: new DynamicControl({
      initialInputs: {
        label: 'Age',
        placeholder: 'Enter your age',
        type: 'number'
      }
    }, TextfieldComponent)
  });
}

You can play with it in StackBlitz. Also, this example is availabe in GitHub.

Usage notes

  • Controls are being rendered outside dynamic-form-outlet element. This is done this way in order to allow outer element to apply layout styles for controls. dynamic-form-outlet element gets removed from DOM so it does not participate in layout. See screenshot from the dev tools:

image

  • The rendering system adds ids for each control. It takes the name of a control which it's defined in DynamicFormGroup with (how it looks like in dev tools can bee seen on the screenshot from the previous bullet). This part of functionality is needed for automation tests so that they can select specific control HTML elements. Example of names the rendering system use:

image

  • The rendering system puts dynamic-control empty attribute for each being rendered controls. Might be useful for selection purposes.

image

1.0.1

7 months ago

1.0.0

1 year ago

1.0.0-alpha2

2 years ago

1.0.0-alpha3

2 years ago

1.0.0-alpha4

2 years ago

1.0.0-alpha5

2 years ago

1.0.0-alpha6

2 years ago

1.0.0-alpha7

2 years ago

1.0.0-alpha8

2 years ago

1.0.0-alpha9

1 year ago

1.0.0-alpha1

2 years ago