Nitrokit Nitrokit - Ship faster with AI - No more heavy lifting, build Angular apps at NITROSPEED!

Documentation

Forms

Documentation on forms

Nitrokit is a boilerplate designed to help developers ship Angular applications faster, focusing on reducing boilerplate code. Nitrokit leverages ngx-vest-forms for form validation, using Vest.js behind the scenes to handle asynchronous and reusable form validation. It integrates seamlessly with Angular's template-driven forms, enabling developers to create powerful, complex forms with minimal configuration and hassle.

Key Features

  • Quick and easy setup for Angular applications.
  • Seamless form validation using ngx-vest-forms and vest.js.
  • Template-driven forms with unidirectional data flow.
  • Automatic creation and validation of form controls and groups.
  • Support for conditional validations, composable validations, and more.

Getting Started with Forms

With Nitrokit, you can quickly set up forms with built-in validation powered by ngx-vest-forms and Vest.js. Below is an example that demonstrates how to create a form with Nitrokit:

Step 1: Create a Basic Form

In this example, we'll create a form with two fields: firstName and lastName.

  1. Import the vestForms directive from ngx-vest-forms into your component.
  2. Apply the scVestForm directive to the form, listen for form changes, and handle form submission.
import { vestForms, DeepPartial } from 'ngx-vest-forms';

type MyFormModel = DeepPartial<{
  generalInfo: {
    firstName: string;
    lastName: string;
  };
}>;

@Component({
  imports: [vestForms],
  template: `
    <form scVestForm 
          (formValueChange)="formValue.set($event)"
          (ngSubmit)="onSubmit()">
      <div ngModelGroup="generalInfo">
        <label>First name</label>
        <input type="text" name="firstName" [ngModel]="formValue().generalInfo?.firstName"/>
      
        <label>Last name</label>
        <input type="text" name="lastName" [ngModel]="formValue().generalInfo?.lastName"/>
      </div>
    </form>
  `
})
export class MyComponent {
  protected readonly formValue = signal<MyFormModel>({});
  
  onSubmit() {
    console.log(this.formValue());
  }
}

Step 2: Add Validation

To add validation, define a Vest.js validation suite and pass it to the form. This suite defines the validation logic for the form fields.

import { enforce, staticSuite, test } from 'vest';

export const myFormModelSuite = staticSuite((model: MyFormModel, field?: string) => {
  if (field) {
    only(field);
  }
  test('firstName', 'First name is required', () => {
    enforce(model.firstName).isNotBlank();
  });
  test('lastName', 'Last name is required', () => {
    enforce(model.lastName).isNotBlank();
  });
});

Bind this suite to your form component:

@Component({
  ...
  template: `
    <form scVestForm 
          [suite]="myFormModelSuite"
          (formValueChange)="formValue.set($event)"
          (ngSubmit)="onSubmit()">
      <div ngModelGroup="generalInfo">
        <label>First name</label>
        <input type="text" name="firstName" [ngModel]="formValue().generalInfo?.firstName"/>
      
        <label>Last name</label>
        <input type="text" name="lastName" [ngModel]="formValue().generalInfo?.lastName"/>
      </div>
    </form>
  `
})
export class MyComponent {
  protected readonly formValue = signal<MyFormModel>({});
  protected readonly suite = myFormModelSuite;
}

Now, whenever the user types into the form, validation will trigger automatically, and any validation errors will be managed by the form controls.

Advanced Features

Conditional Fields

Using computed signals, you can easily show or hide form fields based on conditions in the form. For instance, to hide the lastName field if firstName is not filled out:

class MyComponent {
  ...
  protected readonly lastNameAvailable = computed(() => !!this.formValue().generalInfo?.firstName);
}
<div ngModelGroup="generalInfo">
  <label>First name</label>
  <input type="text" name="firstName" [ngModel]="formValue().generalInfo?.firstName"/>
  
  @if(lastNameAvailable()) {
    <label>Last name</label>
    <input type="text" name="lastName" [ngModel]="formValue().generalInfo?.lastName"/>
  }
</div>

Composable Validations

Vest allows you to compose validation suites, making it easier to reuse validation logic for form fields or groups. For example, you can create a reusable address validation suite and include it in different parts of your forms.

export function addressValidations(model: AddressModel | undefined, field: string): void {
  test(`${field}.street`, 'Street is required', () => {
    enforce(model?.street).isNotBlank();
  });
  // Other address-related validations...
}

export const myFormSuite = staticSuite((model: MyFormModel, field?: string) => {
  if (field) {
    only(field);
  }
  addressValidations(model.address, 'address');
});

Reactive Disabling

You can also dynamically enable or disable form fields based on the state of other form fields using computed signals. For example, disable the lastName field if firstName is not filled:

class MyComponent {
  protected readonly lastNameDisabled = computed(() => !this.formValue().generalInfo?.firstName);
}
<input type="text" name="lastName" [disabled]="lastNameDisabled()" [ngModel]="formValue().generalInfo?.lastName"/>

Showing Validation Errors

With Nitrokit, you can automatically show validation errors using the sc-control-wrapper directive. This directive helps manage validation errors on blur and form submission.

<div ngModelGroup="generalInfo" sc-control-wrapper>
  <div sc-control-wrapper>
    <label>First name</label>
    <input type="text" name="firstName" [ngModel]="formValue().generalInfo?.firstName"/>
  </div>

  <div sc-control-wrapper>
    <label>Last name</label>
    <input type="text" name="lastName" [ngModel]="formValue().generalInfo?.lastName"/>
  </div>
</div>

Complex Validations

Nitrokit simplifies handling complex validations, such as dependent field validations (e.g., password and confirm password) and cross-field validations.

For example:

test('passwords.password', 'Password is required', () => {
  enforce(model.passwords?.password).isNotBlank();
});
omitWhen(!model.passwords?.password, () => {
  test('passwords.confirmPassword', 'Confirm password is required', () => {
    enforce(model.passwords?.confirmPassword).isNotBlank();
  });
});
omitWhen(!model.passwords?.password || !model.passwords?.confirmPassword, () => {
  test('passwords', 'Passwords do not match', () => {
    enforce(model.passwords?.confirmPassword).equals(model.passwords?.password);
  });
});

Conclusion

Nitrokit streamlines the process of building and validating forms in Angular applications. By using ngx-vest-forms and Vest.js, Nitrokit removes much of the manual work involved in form handling and validation, allowing you to focus on building robust applications.

Examples

Check out the examples provided in the GitHub repository to see Nitrokit in action:

Explore these examples to see how Nitrokit handles complex forms, including conditional validations, form arrays, and reusable components.

Want to Learn More?

For a deeper dive into complex Angular forms, check out this course that will guide you through creating advanced forms in Angular.

Have questions?

Still have questions? Talk to support.