Create an Angular Formly form modal

Angular add a formly modal
Difficulty

Let’s create an Angular Formly form modal.
With Angular we can create forms in many different ways, depending on what we need to do and depending on the complexity of the request.
The library we are going to use today helps us in the generation of dynamic forms that allow the user to enter data, even very complex ones.

The ingredients for our recipe are 3 and they are the following:

  • input with button to open the modal
  • a wrapper that simulates a modal
  • a button that sends what is inserted into the modal

1) Modal wrapper for Formly form

One situation that we can have, and that can be among the most complicated, is a modal inside our form. A way to answer to this problem is to create a wrapper for the form fields that will end up in it.


modal-wrapper.component.ts
import { Component } from '@angular/core';
import { FieldWrapper } from '@ngx-formly/core';

@Component({
  selector: 'modal-wrapper',
  templateUrl: './modal-wrapper.component.html',
})
export class ModalWrapperComponent extends FieldWrapper {

  onClose($event) {
    if (this.to.onClose) {
      this.to.onClose($event);
    }
  }
}

It extends the FieldWrapper of Formly.

modal-wrapper.component.html
<div class="formly-modal">
  <div class="modal show"
       tabindex="-1" role="dialog" aria-labelledby="dialog-events-name">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <button type="button" class="close" (click)="onClose($event)" aria-label="Close">
            <span class="close-icon">×</span>
          </button>
          <h4 class="modal-title" >
            {{to.title}}
          </h4>
        </div>
        <div class="modal-body">
          <div [innerHTML]="to.description"></div>
          <ng-container #fieldComponent></ng-container>
        </div>
      </div>
    </div>
    <div class="modal-backdrop fade in" (click)="onClose($event)"></div>
  </div>
</div>

2) Submit modal button for a Formly form modal

We have also to create a custom button to do actions inside the modal. We’ll put all inside the component but you free to divide the component and the template.

field-button.component.ts
import { Component } from "@angular/core";
import { FieldType } from "@ngx-formly/core";

@Component({
  selector: "modal-button",
  template: `
    <button
      [type]="to.type"
      [ngClass]="'btn btn-' + to.btnType"
      (click)="onClick($event)"
    >
      {{ to.text }}
    </button>
  `
})
export class FieldButtonComponent extends FieldType {
  onClick($event) {
    if (this.to.onClick) {
      this.to.onClick($event);
    }
  }
}

3) Modal input opener

The third ingredient is the input with a button to open our modal. In our example we will create a new input template with a right-floated button to open.

modal-input.component.ts
import { Component } from "@angular/core";
import { FieldType } from "@ngx-formly/core";

@Component({
  selector: "modal-input",
  template: `
    <div class="form-group">
      <label [attr.for]="id" class="col-sm-2 col-form-label" *ngIf="to.label">
        {{ to.label }}
        <ng-container *ngIf="to.required && to.hideRequiredMarker !== true"
          >*</ng-container>
      </label>
      <input
        class="col-sm-7"
        [type]="type"
        [formControl]="formControl"
        [formlyAttributes]="field"
        [class.is-invalid]="showError"
        class="form-control"
      />
      <div
        *ngIf="to.modable"
        class="input-group-addon btn btn btn-primary submit-button"
        (click)="onClick($event)">
        Open
      </div>
      <div *ngIf="showError" class="col-sm-3 invalid-feedback d-block">
        <formly-validation-message [field]="field"></formly-validation-message>
      </div>
    </div>
  `,
  styles: [
    `
      .form-group {
        display: flex;
      }
      .input-group-addon {
        flex-grow: 1;
        margin-left: 10px;
        padding: 0 1em;
        line-height: 28px;
      }
    `
  ]
})
export class ModalFieldInputComponent extends FieldType {
  defaultOptions = {
    templateOptions: {
      modable: false
    }
  };

  get type() {
    return this.to.type || "text";
  }

  onClick($event) {
    if (this.to.onClick) {
      this.to.onClick($event);
    }
  }
}

Let’s cook them!

Now that we have our wrapper (with accessories) we can insert them into our module, with the Formly import.

my-forms.module.ts
import { NgModule } from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormlyModule} from '@ngx-formly/core';
import {FormlyBootstrapModule} from '@ngx-formly/bootstrap';
import {ReactiveFormsModule} from '@angular/forms';
import {ModalWrapperComponent} from './modal-wrapper.component';
import {FieldButtonComponent} from './field-button.component';
import { ModalFieldInputComponent } from "./modal-input.component";

@NgModule({
  declarations: [
    FieldButtonComponent,
    ModalWrapperComponent,
    ModalFieldInputComponent
  ],
  imports: [
    CommonModule,
    ReactiveFormsModule,
    FormlyModule.forRoot({
      types: [
        { name: 'button', component: FieldButtonComponent },
        { name: "modal-input", component: ModalFieldInputComponent },
      ],
      wrappers: [
        { name: 'modal', component: ModalWrapperComponent },
      ],
    }),
    FormlyBootstrapModule,
  ]
})
export class FormsModule { }

Then we can mix them together and use our custom new components inside a standard formly form.

my-form.component.html
<form [formGroup]="form" (ngSubmit)="submit()">
  <formly-form
    [model]="model"
    [fields]="fields"
    [options]="options"
    [form]="form"></formly-form>
  <div class="row">
    <div class="col-sm-12">
      <p class="btn-container">
        <button type="submit" class="btn btn-primary"
          [disabled]="!form.valid">
          Submit
        </button>
      </p>
    </div>
  </div>
</form>
{{ model|json }}
my-form.component.ts
import { Component } from "@angular/core";
import { FormGroup } from "@angular/forms";
import { FormlyFormOptions, FormlyFieldConfig } from "@ngx-formly/core";

@Component({
  selector: "my-form",
  templateUrl: "./app.component.html"
})
export class MyFormComponent {
  form = new FormGroup({});
  model: any = {};
  options: FormlyFormOptions = {};
  emailModal = false; // Flag to open/close our modal

  fields: FormlyFieldConfig[] = [
    // This field uses a custom template to add a little
    // button to click and open the modal with the onClick event.
    {
      key: "modalOpener",
      type: "modal-input",
      templateOptions: {
        modable: true,
        disabled: true,
        label: "E-mail",
        placeholder: "email@email.it",
        onClick: $event => {
          this.emailModal = true;
        }
      }
    },

    /*********** MODAL FIELDS ***********/
    {
      key: "modalField",
      wrappers: ["modal"],
      hideExpression: () => !this.emailModal,
      templateOptions: {
        title: "A modal",
        description: "Description of this modal",
        onClose: () => {
          this.emailModal = false;
        }
      },
      fieldGroup: [
        {
          key: "email",
          type: "input",
          templateOptions: {
            type: "email",
            label: "E-mail",
            placeholder: "Insert a valid e-mail",
            minLength: 3
          }
        },
        {
          key: "myButton",
          type: "button",
          templateOptions: {
            type: "button",
            btnType: "primary",
            text: "Send",
            onClick: $event => {
              if (this.form.valid && this.model.modalField.email) {
                this.model = {
                  ...this.model,
                  modalOpener: this.model.modalField.email
                };
                this.form
                  .get("modalOpener")
                  .setValue(this.model.modalField.email);
                this.emailModal = false;
                console.log("this.model: ", this.model);
              }
            }
          }
        }
      ]
    },

    {
      key: "normal-input",
      type: "modal-input",
      templateOptions: {
        modable: false,
        label: "Generic text",
        placeholder: "generic value"
      }
    }
  ];

  submit() {
    alert(JSON.stringify(this.model));
  }
}

Here is the working example:


That’s all, for creating an Angular Formly form modal.
Try it at home!

0
Be the first one to like this.
Please wait...

3 thoughts on...
  1. Here I am not able to see send button. I simply want on click of button open modal with some form in it.

Leave a Reply

Thanks for choosing to leave a comment.
Please keep in mind that all comments are moderated according to our comment policy, and your email address will NOT be published.
Please do NOT use keywords in the name field. Let's have a personal and meaningful conversation.