• Stars
    star
    115
  • Rank 299,885 (Top 7 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 6 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

FormBuilder + class-transformer + class-validator = dynamic form group builder for Angular

ngx-dynamic-form-builder

Build Status npm version monthly downloads

FormBuilder + class-transformer-global-storage + class-validator-multi-lang = dynamic form group builder for Angular

Installation

npm i --save class-transformer-global-storage class-validator-multi-lang ngx-dynamic-form-builder

BREAKING CHANGE !!!

Version above 2 has a completely rewritten code, partially backwards compatible

Now @Expose and @Exclude decorators are used to define model fields, the new version is rigidly dependent on class-transform

Dependencies are not used original, but forks with additional necessary properties, when using this library, you need to replace all original imports with forks with modifications

Fork class-validator-multi-lang - adds translation capability for errors (PR:typestack/class-validator#743)

Fork class-transformer-global-storage - adds the ability to get meta information about all used classes (PR:typestack/class-transformer#929)

For correct parse metadata, need remove compilerOptions.downlevelIteration and append compilerOptions.emitDecoratorMetadata: true in tsconfig.json

Links

Demo - Demo application with ngx-dynamic-form-builder.

Stackblitz - Simply sample of usage on https://stackblitz.com

Usage

company.ts

import { Validate, IsNotEmptym } from 'class-validator-multi-lang';
import { TextLengthMore15 } from '../utils/custom-validators';
import { marker } from '@ngneat/transloco-keys-manager/marker';
import { Expose, Type } from 'class-transformer-global-storage';

export class Company {
  @Expose()
  id: number;

  @Validate(TextLengthMore15, {
    message: marker('The company name must be longer than 15'),
  })
  @IsNotEmpty()
  @Expose()
  name: string;

  constructor(data?: any) {
    if (data === undefined) {
      data = {};
    }
    this.id = data.id;
    this.name = data.name;
  }
}

app.module.ts

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CompanyPanelComponent } from './company-panel.component';

@NgModule({
  imports: [
    ...
    FormsModule,
    ReactiveFormsModule,
    ...
  ],
  declarations: [
    ...
    CompanyPanelComponent,
    ...
  ],
  ...
})
export class AppModule {}

company-panel.component.html

<form [formGroup]="form" *ngIf="form?.customValidateErrors | async as errors" novalidate>
  <input formControlName="name" placeholder="Name" />
  <p *ngIf="errors.name?.length">Error: {{errors.name[0]}}</p>
  <p>Form status: {{ form.status | json }}</p>
  <p>Form class-validator-multi-lang errors: {{errors|json}}</p>
  <p *ngIf="savedItem">Saved item: {{savedItem|json}}</p>
  <button (click)="onLoadClick()">Load</button>
  <button (click)="onClearClick()">Clear</button>
  <button (click)="onSaveClick()" [disabled]="!form.valid">Save</button>
</form>

company-panel.component.ts

import { DynamicFormGroup, DynamicFormBuilder } from 'ngx-dynamic-form-builder';
import { Company } from './../../shared/models/company';
import { Input, Component } from '@angular/core';
import { Validators } from '@angular/forms';

@Component({
  selector: 'company-panel',
  templateUrl: './company-panel.component.html',
})
export class CompanyPanelComponent {
  form: DynamicFormGroup<Company>;

  @Input()
  item = new Company({
    id: 11,
    name: '123456789012345',
  });

  fb = new DynamicFormBuilder();

  savedItem?: Company;

  constructor() {
    this.form = this.fb.rootFormGroup(Company, {
      name: '',
    });
  }
  onLoadClick(): void {
    this.savedItem = undefined;
    this.form.object = this.item;
  }
  onClearClick(): void {
    this.savedItem = undefined;
    this.form.object = new Company();
  }
  onSaveClick(): void {
    if (this.form.valid) {
      this.savedItem = this.form.object;
    } else {
      this.savedItem = undefined;
    }
  }
}

custom-validators.ts

import { ValidatorConstraintInterface, ValidatorConstraint } from 'class-validator-multi-lang';

@ValidatorConstraint()
export class TextLengthMore15 implements ValidatorConstraintInterface {
  validate(text: string) {
    return text ? text.length > 15 : false;
  }
}

Support multi-language translate validation errors (I18n)

Because multi-language supported in class-validator-multi-lang, now ngx-dynamic-form-builder also support this feature

set validation messages as settings when create form group

this.form = this.fb.rootFormGroup(
  Company,
  {
    name: '',
  },
  {
    classValidatorOptions: {
      messages: {
        'The company name must be longer than 15': 'company name must be longer than 15 (translate on other language)',
      },
    },
  }
);

set validation messages on runtime after for exists form group

this.form.patchDynamicFormBuilderOptions({
  classValidatorOptions: {
    messages: {
      'The company name must be longer than 15': 'company name must be longer than 15 (translate on other language)',
    },
  },
});

set translate property name in error

this.form.patchDynamicFormBuilderOptions({
  classValidatorOptions: {
    titles: {
      regionNum: 'number of region (translate property name in error on other language)',
    },
  },
});

set validation messages and properties name global for all instance of form group in project

setGlobalDynamicFormBuilderOptions({
  classValidatorOptions: {
    messages: {
      'The company name must be longer than 15': 'company name must be longer than 15 (translate on other language)',
    },
    titles: {
      regionNum: 'number of region (translate property name in error on other language)',
    },
  },
});

Observable Errors

The customValidateErrors property can be subscribed for cases in which your code should act on changes in errors

company-panel.component.html

<form [formGroup]="form" *ngIf="form?.customValidateErrors | async as errors" novalidate>
  <input formControlName="name" placeholder="Name" />
  <p *ngIf="errors.name?.length">Error: {{errors.name[0]}}</p>
  <p>Form status: {{ form.status | json }}</p>
  <p>Observable validation errors: {{errors|json}}</p>
  <p *ngIf="savedItem">Saved item: {{savedItem|json}}</p>
  <button (click)="onLoadClick()">Load</button>
  <button (click)="onClearClick()">Clear</button>
  <button (click)="onSaveClick()" [disabled]="!form.valid">Save</button>
</form>

company-panel.component.ts

import { DynamicFormGroup, DynamicFormBuilder } from 'ngx-dynamic-form-builder';
import { Company } from './../../shared/models/company';
import { Input, Component } from '@angular/core';
import { Validators } from '@angular/forms';
import { Subscription } from 'rxjs';

@Component({
  selector: 'company-panel',
  templateUrl: './company-panel.component.html',
})
export class CompanyPanelComponent implements onDestroy {
  form: DynamicFormGroup<Company>;

  @Input()
  item = new Company({
    id: 11,
    name: '123456789012345',
  });

  @Input()
  strings = Company.strings;

  fb = new DynamicFormBuilder();

  savedItem?: Company;

  errorChangeSubscription: Subscription;

  constructor() {
    this.form = this.fb.rootFormGroup(Company, {
      name: '',
    });

    this.errorChangeSubscription = this.form.customValidateErrors.subscribe((allErrors) => {
      console.log(`Errors changed: ${allErrors}`);
    });
  }
  ngOnDestroy() {
    if (this.errorChangeSubscription != null && this.errorChangeSubscription.closed === false) {
      this.errorChangeSubscription.unsubscribe();
    }
  }
  onLoadClick(): void {
    this.savedItem = undefined;
    this.form.object = this.item;
    this.form.validateAllFormFields();
  }
  onClearClick(): void {
    this.savedItem = undefined;
    this.form.object = new Company();
    this.form.validateAllFormFields();
  }
  onSaveClick(): void {
    this.form.validateAllFormFields();
    if (this.form.valid) {
      this.savedItem = this.form.object;
    } else {
      this.savedItem = undefined;
    }
  }
}

License

MIT

More Repositories

1

nest-permissions-seed

A simple application demonstrating the basic usage of permissions with NestJS.
TypeScript
140
star
2

typegraphql-prisma-nestjs-example

Example use NestJS + Prisma2 + Typegraphql
TypeScript
27
star
3

kaufman-bot

Simple bot for telegram
TypeScript
20
star
4

ngx-bind-io

Directives for auto binding Input() and Output() from host component to inner in Angular9+ application
TypeScript
9
star
5

nestjs-custom-injector

Custom injecting logic for NestJS with support multi providing
TypeScript
9
star
6

nestjs-translates

NestJS module for adding translations to the application, with a pipe for translating validation errors
TypeScript
5
star
7

ngx-repository

Custom repository service for Angular9+, for easy work with the REST backend, with switch on fly from REST backend to the MOCK backend with save and use all CRUD operations
TypeScript
4
star
8

nestjs-custom-injector-example

TypeScript
2
star
9

django-postgres-angularjs-blog

Simple blog written on django + angularjs and use postgre sql as database
JavaScript
2
star
10

schematics-readme

Generator README.md file for Angular Schematics collection
TypeScript
1
star
11

ngx-bind-io-cli

Tools for check Angular7+ components for use ngx-bind-io directives
TypeScript
1
star
12

angular2-electron-issue-client

angular2-electron-isuse-client
JavaScript
1
star
13

ngx-remote-config

Remote configurations for Angular9+ applications, with built-in interceptor for mock REST data and non-permanent api
TypeScript
1
star
14

ngx-cold

Two small directives for work with observable in Angular9+ without subscribe
TypeScript
1
star