vendredi 17 août 2018

Angular Typescript - Suggestion for a design pattern for mapping json data to Textbox, dropdown or another component object

I have developed an angular app which generates dynamic components. Those dynamic components are textbox and drowdown for now. I am getting what to use in a form from a service.

getPortalSettingsControls(): Observable<WizardItem[]> {
    return this.http.get<WizardItem[]>('../../assets/mock_service/wizardData.json');
}

My wizardData.json is:

[
  {
    "id": 1,
    "sortnumber": 1,
    "headerText": "General Settings",
    "wizardSettingItems": 
    [
      {
          "id": 1,
          "sortNumber": 1,
          "menuText": "AWS Settings",
        "wizardComponentItems": [
          {
            "id": 1,
            "sortNumber": 1,
            "controlIdName": "agencyName",
            "controlType": "Textbox",
            "inputType": "text",
            "labelText": "Agency Name"
          },
          {
            "id": 2,
            "sortNumber": 2,
            "controlIdName": "agencyCode",
            "controlType": "Textbox",
            "inputType": "text",
            "labelText": "Agency Code"
          },
          {
            "id": 3,
            "sortNumber": 3,
            "controlIdName": "wsUserName",
            "controlType": "Textbox",
            "inputType": "text",
            "labelText": "WS UserName"
          },
          {
            "id": 4,
            "sortNumber": 4,
            "controlIdName": "wsPassword",
            "controlType": "Textbox",
            "inputType": "password",
            "labelText": "WS Password"
          },
          {
            "id": 5,
            "sortNumber": 5,
            "controlIdName": "customDomainUrl",
            "controlType": "Textbox",
            "inputType": "text",
            "labelText": "Custom Domain URL"
          },
          {
            "id": 6,
            "sortNumber": 6,
            "controlIdName": "wsap",
            "controlType": "Textbox",
            "inputType": "text",
            "labelText": "WSAP"
          },
          {
            "id": 7,
            "sortNumber": 7,
            "controlIdName": "wsOrganizationID",
            "controlType": "Textbox",
            "inputType": "text",
            "labelText": "WS Organization ID"
          },
          {
            "id": 8,
            "sortNumber": 8,
            "controlIdName": "wsOfficeID",
            "controlType": "Textbox",
            "inputType": "text",
            "labelText": "WS Office ID"
          },
          {
            "id": 9,
            "sortNumber": 9,
            "controlIdName": "wsUserName",
            "controlType": "Textbox",
            "inputType": "text",
            "labelText": "WS Username"
          },
          {
            "id": 10,
            "sortNumber": 10,
            "controlIdName": "wsPassword",
            "controlType": "Textbox",
            "inputType": "password",
            "labelText": "WS Password"
          },
          {
            "id": 11,
            "sortNumber": 0,
            "controlIdName": "market",
            "controlType": "Dropdown",
            "labelText": "Market",
            "dropdownOption": [
              {
                "value": "Turkey",
                "text": "Turkey"
              },
              {
                "value": "France",
                "text": "France"
              },
              {
                "value": "Spain",
                "text": "Spain"
              }
            ]
          }
        ]
      }
    ]
  }
]

As you can see there is only two types of components right now (Textbox and dropdown).

I have created one base model and two child models for these two components:

export class WizardComponentItem {
id: number;
sortNumber: number;
controlIdName: string;
controlType: string;
labelText: string;
value: string;

}

import { WizardComponentItem } from '../wizard-component-item.model';
export class WizardTextboxComponent extends WizardComponentItem {
    inputType: string;
}


import { WizardComponentItem } from '../wizard-component-item.model';
export class WizardDropdownComponent extends WizardComponentItem {
    dropdownOption: { value: any, text: string };
}

As you can see I am inheriting both of textbox and dropdown from base object model. In the service the return type is base object model (WizardComponentItem ).

Here is my dynamic component for textbox:

<div [formGroup]="form" class="form-group row">
    <label [for]="controlIdName" class="col-sm-2 col-form-label"></label>
    <div class="col-sm-10">
        <input [type]="type" class="form-control" [id]="controlIdName" [formControlName]="controlIdName">
    </div>
</div>

For dropdown:

<div [formGroup]="form" class="form-group row">
    <label [for]="controlIdName" class="col-sm-2 col-form-label"></label>
    <div class="col-sm-10">
        <select class="form-control" [id]="controlIdName" [formControlName]="controlIdName">
            <option [value]="dropdownOption.value" *ngFor="let dropdownOption of dropdownOptions"></option>
        </select>
    </div>
</div>

And this is how I understand which dynamic component to attach:

attachFormComponents(wizardForm: FormGroup, wizardComponentItems: WizardComponentItem[], viewContainerRef: ViewContainerRef) {

        for (let item of wizardComponentItems) {
            let componentParams = Object.entries(item).map(([name, value]) => ({ name, value }));
            componentParams.push({ name: "form", value: wizardForm });
            switch (item.controlType) {
                case "Textbox":
                    this.componentAttacher.attachComponent(WizardTextboxComponent, viewContainerRef, componentParams);
                    break;
                case "Dropdown":
                    this.componentAttacher.attachComponent(WizardDropdownComponent, viewContainerRef, componentParams);
                    break;
                default:
                    break;
            }
        }
    }

Can you please advise me a design pattern or a way to map the json to a generic model and somehow understand it to extend to a textbox model or dropdown model? If you want further information please do not hesitate to ask for more.

Aucun commentaire:

Enregistrer un commentaire