Package Exports
- ngx-sub-form
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (ngx-sub-form) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
NgxSubForm
Utility library for breaking down a form into multiple components.
Works well with polymorphic data structures.
ngx-sub-form
is here to help you avoid passing your formGroup
as inputs and tackle down the boilerplate of creating a custom ControlValueAccessor
.
Install
Install the npm-package:
yarn add ngx-sub-form
Setup
+ import { NgxSubFormModule } from 'ngx-sub-form';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
+ NgxSubFormModule
],
bootstrap: [AppComponent],
})
export class AppModule {}
Usage
Before we get started with how to use the library and give some examples, a complete demo is available on this repo, within the src
folder.
Demo is built around a concept of galatic sales. You can sell either Droids (Protocol, Medical, Astromech, Assassin) or Vehicules (Spaceship, Speeder). This will also be used for the following examples.
First component level
Within the component where the form will be handled, we have to define the top level structure of the form (as you'd normally do).
public sellForm: FormGroup = new FormGroup({
sell: new FormControl(null, { validators: [Validators.required] }),
});
Then we need to create a separated FormControl
to select the sell's type:
public selectSellType: FormControl = new FormControl();
and give access to our enum
from the component:
public SellType = SellType;
Just as a sidenote here, here's the SellType
enum:
export enum SellType {
VEHICULE = 'Vehicule',
DROID = 'Droid',
}
Then, within the .html
we create a select
tag to choose between the 2 types:
<select [formControl]="selectSellType">
<option *ngFor="let sellType of SellType | keyvalue" [value]="sellType.value">
{{ sellType.value }}
</option>
</select>
Now we need to create, based on the sell's type, either a DroidSellComponent
or a VehiculeSellComponent
:
<form [formGroup]="sellForm">
<div [ngSwitch]="selectSellType.value" ngxSubFormOptions>
<app-droid-sell *ngSwitchCase="SellType.DROID" ngxSubFormOption formControlName="sell"></app-droid-sell>
<app-vehicule-sell *ngSwitchCase="SellType.VEHICULE" ngxSubFormOption formControlName="sell"></app-vehicule-sell>
</div>
<button mat-raised-button (click)="upsertSell(sellForm.get('sell').value)" [disabled]="sellForm.invalid">
Upsert
</button>
</form>
3 things to notice above:
ngxSubFormOptions
(will explain later)ngxSubFormOption
(will explain later)formControlName="sell"
our sub form component IS a customControlValueAccessor
and let us bind our component to aformControlName
as we would with an input.
Second component level
This is where ngx-sub-form
is becoming useful. All you have to do is:
Add required providers using the utility function subformComponentProviders
:
+import { subformComponentProviders } from 'ngx-sub-form';
@Component({
selector: 'app-vehicule-sell',
templateUrl: './vehicule-sell.component.html',
styleUrls: ['./vehicule-sell.component.scss'],
+ providers: subformComponentProviders(VehiculeSellComponent),
})
export class VehiculeSellComponent {}
Make your original class extends NgxSubFormComponent
:
-import { subformComponentProviders } from 'ngx-sub-form';
+import { subformComponentProviders, NgxSubFormComponent } from 'ngx-sub-form';
-export class VehiculeSellComponent {}
+export class VehiculeSellComponent extends NgxSubFormComponent {}
Define the controls of your form:
private controls: Controls<VehiculeSell> = {
id: new FormControl(this.uuidService.generate(), { validators: [Validators.required] }),
price: new FormControl(null, { validators: [Validators.required] }),
};
public formGroup: FormGroup = new FormGroup(this.controls);
public controlsNames: ControlsNames<VehiculeSell> = getControlsNames(this.controls);
Simplified from the original example into src folder to keep the example as minimal as possible.
As you know, Angular reactive forms are not strongly typed. We're providing an interface (Controls<T>
) to at least set the correct names within the form (but it will not help you when using form.get('...').value
). It is still very useful and when making a refactor if your data structure changes and do not match the form structure Typescript compilation will fail.
We also provide a utility function called getControlsNames
which you can pass your controls
to. This will let you define your formControlName
s in the view in a safe way thanks to AoT. If you update your main interface, your form control but you forget about the view, you'll get an error.
Then within the .html
:
<fieldset [formGroup]="formGroup" class="container">
<input type="text" placeholder="ID" [formControlName]="controlsNames.id" />
<input type="number" placeholder="Price" [formControlName]="controlsNames.price" />
</fieldset>