Search code examples
typescriptangular-formlyangular-cli-v8

Angular Formly Forms - removing add/remove buttons from array type


I've got an issue trying to remove/disable buttons from a customized array component I use with Formly Forms when generating dynamic forms using a JSON schema (draft 7). I've tried various things to solve this but nothing seems to work.

What I want to achieve is, use an ngIf* to show/hide buttons or something similar to disable them inside the component template. Sometimes the array component should display these buttons, sometimes it should not. This must depend on a specific property defined somewhere it could easily be checked and button rendering can be prevented.

I've tried to set a disable: true property inside the JSON schema but I don't know how to access this property from an array component (I've checked the field object inside the component - no impact). I've also tried creating a new component where I physically removed these buttons and named it arrayDisabled (also updated the schema accordingly etc.) but the field.fieldGroup property gets messed up (it grabs an invalid object that doesn't represent an array at all) so nothing gets displayed inside the form.

There isn't much documentation on this.

Here is my custom component:

import { Component } from '@angular/core';
import { FieldArrayType } from '@ngx-formly/core';

@Component({
  selector: 'formly-array-type',
  template: `
  <div class="mb-3">
    <legend *ngIf="to.label">{{ to.label | translate }}</legend>
    <p *ngIf="to.description">{{ to.description | translate }}</p>
    <div class="alert alert-danger" role="alert" *ngIf="showError && formControl.errors">
      <formly-validation-message [field]="field"></formly-validation-message>
    </div>
    <div *ngFor="let field of field.fieldGroup;let i = index;" class="row">
      <formly-field class="col-10" [field]="field"></formly-field>
      <div *ngIf="isDisabled(field)" class="col-2 text-right">
        <button mat-flat-button color="warn" (click)="remove(i)">-</button>
      </div>
    </div>
    <div class="d-flex flex-row-reverse">
      <button mat-flat-button color="primary" (click)="add()">+</button>
    </div>
  </div>
  `,
  styles: [
    `button {min-width: 40px;}`,
    `div.ng-star-inserted {margin-left: 0px; margin-right: 0px;}`,
    `div.text-right {padding-left: 0px; padding-right: 0px;}`
  ]
})
export class ArrayTypeComponent extends FieldArrayType { }

Here is a sample of my json schema (draft 7):

"arrayOfItems": {
    "type": "array",
    "title": "My Array",
    "items": {
        "type": "object",
        "properties": {
            "itemA": {
                "type": "string",
                "title": "Item A"
            },
            "itemB": {
                "type": "string",
                "title": "Item B"
            },
            "itemC": {
                "type": "integer",
                "title": "Item C"
            },
        },
        "additionalProperties": false,
        "required": []
    }
}

Here is a sample model for this piece of json schema (it's a simple array of objects):

"arrayOfItems": [{ itemA: "Item A" }, { itemB: "Item B" }, { itemC: 0 }]

And last but not least, here is my imports segment from a shared module I use:

  imports: [
    FormlyModule.forRoot({
        types: [
            {
              name: 'array',
              component: ArrayTypeComponent,
              defaultOptions: {
                templateOptions: {
                  type: 'array',
                },
              },
            }, ...

Solution

  • I was able to solve my issue by setting a FormlyFormOptions formState property inside my component, that generates the form.

    public options: FormlyFormOptions = {
      formState: {
        disabled: true
      }
    }
    

    I used options property on my formly-form tag like this:

    <formly-form [model]="..." [fields]="..." [options]="options" [form]="...">
    

    After that I was able to use this property inside a custom Formly component and access it like this this.formState.disabled from class perspective and/or *ngIf="formState.disabled" from template perspective.

    *ngIf handles whether to show or hide buttons inside my customized components template.