Techumber
Home Blog Work

From Builder using Angular 2 and Bootstrap

Published on November 15, 2016

Hola, We use forms everywhere. 90% of web applications uses forms. In today’s post, we will build an Angular 2 component which takes input as a JSON object and generate Twitter Bootstrap form based on it.

Demo

source

Our goal is to generate a bootstrap form based on JSON we pass.

Let’s suppose if we have a JSON as below.

[
  {
    name: 'email',
    label: 'Email',
  },

  {
    name: 'first_name',
    label: 'First Name',
    multi: true,
  },
  {
    type: 'radio',
    name: 'radio',
    label: 'Radio',
    opts: [
      { label: 'Option 1', value: 'opt_1' },
      { label: 'Option 2', value: 'opt_2' },
    ],
  },
  {
    type: 'check',
    name: 'opt',
    opts: [
      { name: 'opt_1', label: 'Option 1' },
      { name: 'opt_2', label: 'Option 2' },
    ],
  },
  {
    type: 'select',
    name: 'select_1',
    label: 'Select',
    opts: [
      { label: 'Option 1', value: 'opt_1' },
      { label: 'Option 2', value: 'opt_2' },
    ],
  },
];

It should generate a bootstrap form for us.

Ok, Step 1 is we need to create bootstrap form controls. We will build only basic form controls like input,textarea,radio button, checkbox and select in this example.

input

import { Component, Input } from '@angular/core';

// text,email,tel,textarea,password,
@Component({
  selector: 'text-box',
  template: `
    <div class="form-group">
      <label>{{ prop.label }}</label>
      <input
        *ngIf="!prop.multi"
        type="{{ prop.type }}"
        class="form-control"
        [id]="prop.name"
        [name]="prop.name"
        [(ngModel)]="model[prop.name]"
      />
      <textarea
        *ngIf="prop.multi"
        class="form-control"
        rows="5"
        [id]="prop.name"
        [name]="prop.name"
        [(ngModel)]="model[prop.name]"
      ></textarea>
    </div>
  `,
})
export class TextBoxComponent {
  @Input() prop: any = {};
  @Input() model: any;
  constructor() {}
}

Here, we have both input,textarea controls in it. If we pass multi:true in our JSON object it will show text area otherwise it will show a text input.

For text input, we also passing type. So we can generate all text input type like email,tel,color,so on..

In the TextBoxComponent class it take prop and model as inputs. prop is for JSON object. modelis for input model. The user entered value will be stored on this.

All remaining components have pretty much same structure.

Radio button

import { Component, Input } from '@angular/core';
// Radio buttons
@Component({
  selector: 'radio-button',
  template: `
    <div class="form-group">
      <label>{{ prop.label }}</label>
      <div class="radio" *ngFor="let opt of prop.opts">
        <label
          ><input
            type="radio"
            [(ngModel)]="model[prop.name]"
            [name]="prop.name"
            [value]="opt.value"
          />{{ opt.label }}</label
        >
      </div>
    </div>
  `,
})
export class RadioButtonComponent {
  @Input() prop: any = {};
  @Input() model: any;
  constructor() {}
}

Select

import { Component, Input } from '@angular/core';

// Select control
@Component({
  selector: 'select-box',
  template: `
    <div class="form-group">
      <label for="sel1">{{ prop.label }}</label>
      <select class="form-control" [(ngModel)]="model[prop.name]" [name]="prop.name">
        <option *ngFor="let opt of prop.opts" [value]="opt.value">{{ opt.label }}</option>
      </select>
    </div>
  `,
})
export class SelectBoxComponent {
  @Input() prop: any = {};
  @Input() model: any;
  constructor() {}
}

Checkbox

import { Component, Input } from '@angular/core';

// checkbox control
@Component({
  selector: 'check-box',
  template: `
    <div class="form-group">
      <label>{{ prop.label }}</label>
      <div class="checkbox" *ngFor="let opt of prop.opts">
        <label><input type="checkbox" [(ngModel)]="model[opt.name]" />{{ opt.label }}</label>
      </div>
    </div>
  `,
})
export class CheckBoxComponent {
  @Input() prop: any = {};
  @Input() model: any;
  constructor() {}
}

Ok, We are done with all low-level component creation. Now, let’s create ControlBuilderComponent and FormBuilderComponent.

FormBuilderComponent

import { Component, Input } from '@angular/core';

@Component({
  selector: 'form-builder',
  template: `
    <form>
      <control-builder
        *ngFor="let prop of formJson"
        [prop]="prop"
        [model]="model"
      ></control-builder>
      <button type="submit" class="btn btn-default">Submit</button>
    </form>
  `,
})
export class FormBuilderComponent {
  @Input() formJson: any = {};
  @Input() model: any;
  constructor() {}
}

Form Builder is TopLevel component take inputs formJson and model. It’s basically a wrapper with <form> tag.

formJson is the JSON format of the form and model is the data model that we have been using in all our controllers.

In this FormBuilderComponent we are calling our ControlBuilderComponent which will generate components for form.

ControlBuilderComponent

import { Component, Input } from '@angular/core';

@Component({
  selector: 'control-builder',
  template: `
    <div [ngSwitch]="prop.type">
      <check-box *ngSwitchCase="'check'" [prop]="prop" [model]="model"></check-box>
      <radio-button *ngSwitchCase="'radio'" [prop]="prop" [model]="model"></radio-button>
      <select-box *ngSwitchCase="'select'" [prop]="prop" [model]="model"></select-box>
      <text-box *ngSwitchDefault [prop]="prop" [model]="model"></text-box>
    </div>
  `,
})
export class ControlBuilderComponent {
  @Input() prop: any = {};
  @Input() model: any;
  constructor() {}
}

Based on type it will choose our low-level components that we created earlier.

Now, let’s call our form builder from AppComponent.

import { Component, Input } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
    {{ model | json }}
    <div class="container">
      <h1>Form Builder</h1>
      <form>
        <form-builder [formJson]="ctrls" [model]="model"></form-builder>
      </form>
    </div>
  `,
})
export class AppComponent {
  public model: any = {};
  public fname: string;

  public ctrls: any = [
    {
      name: 'email',
      label: 'Email',
    },

    {
      name: 'first_name',
      label: 'First Name',
      multi: true,
    },
    {
      type: 'radio',
      name: 'radio',
      label: 'Radio',
      opts: [
        { label: 'Option 1', value: 'opt_1' },
        { label: 'Option 2', value: 'opt_2' },
      ],
    },
    {
      type: 'check',
      name: 'opt',
      opts: [
        { name: 'opt_1', label: 'Option 1' },
        { name: 'opt_2', label: 'Option 2' },
      ],
    },
    {
      type: 'select',
      name: 'select_1',
      label: 'Select',
      opts: [
        { label: 'Option 1', value: 'opt_1' },
        { label: 'Option 2', value: 'opt_2' },
      ],
    },
  ];

  consturctor() {}
}

Try the demo or view the source code here