1

In my angular (2) application I have contacts which have a list of properties (fields). Each property has its own type like Date, Gender, String, Int etc.

In the editor for contacts I render specific form elements according to the type of the field. Date picker for date, radio groups for gender etc.

To do this I now have a (rather large) switch statement on the type of the field where each case is a specific form element/component.

<ng-container [formGroup]="formGroup" [ngSwitch]="field.fieldType">
  <sp-contact-field-checkbox-list *ngSwitchCase="ContactFieldType.SET" [formControlName]="formControlName" [options]="field.options"></sp-contact-field-checkbox-list>
  <div *ngSwitchCase="ContactFieldType.GENDER">
    <sp-gender-input [id]="formControlName" [formControlName]="formControlName"></sp-gender-input>
  </div>
...

In another component I show a details view of the contact. There I have a similar switch case so each of the property values is rendered correctly.

I feel like having these large switch statements in the HTML might be a anti pattern or bad design. Adding/changing property types needs to be done in multiple files.

Does anyone have a nicer (angular) solution to this problem? It feels like there should be a polymorphic solution but I can't do it in angular.

Bob Brinks
  • 1,372
  • 1
  • 10
  • 19
  • you would probably benefit from something along these lines: https://stackoverflow.com/questions/40115072/how-to-load-component-dynamically-using-component-name-in-angular2 the idea is to use componentFactory with component name (string) – dee zg Apr 23 '18 at 07:28
  • the type of your fields, can be transform to string,number,radio,select and date (only 5 types), if is a select or a radio, can have a property "values", e.g. values:[[1,'one'],[2,'two'],[3,'three']] – Eliseo Apr 23 '18 at 08:01

1 Answers1

2

I will give you an solution with examples from my current project. Where we moved the switch in the service and dinamicaly create the content. If you data change dinamicaly and the html templates have very different structure is a good solution. Otherwise think about to create a new component with your switch cases.

So, First you will need a container. In order to user ViewContainerRef, you should create a directive.

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

    @Directive({
        selector: '[assignment-container]',
    })
    export class AssignmentContainer {
        constructor(public viewContainerRef: ViewContainerRef) { }
    }

Craete a service to manage the dinamicaly created components.

@Injectable()
export class AssignmentTemplateService {

    constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

    public initTemplate(assignmentTemplateContainer: AssignmentContainer, model: any, permissions: any) {
        this.loadAssignmentTemplateComponent(assignmentTemplateContainer, model, permissions);
    }

    private loadAssignmentTemplateComponent(assignmentTemplateContainer:AssignmentContainer, model: any, permissions: any) {
        let componentRef;
       // put you switch cases
        switch(model.linkedEntityType) {               
            default: {
                componentRef = this.retrieveTemplateComponent(GeneralAssignmentTemplateComponent, assignmentTemplateContainer);
                //Set the inputs
                componentRef.requestTypeObject = model;
                componentRef.permissions = permissions;
            }
        }

    }

    private retrieveTemplateComponent(componentType: Type<IAssignmentTemplateComponent>, assignmentTemplateContainer: AssignmentContainer) : IAssignmentTemplateComponent {

        let componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentType);
        //Clear the container content
        let viewContainerRef = assignmentTemplateContainer.viewContainerRef;
        viewContainerRef.clear();
        //Get the component instance to set required fields like the model
        return viewContainerRef.createComponent(componentFactory).instance;
    }
}

For every single switch case you should create an different component like GeneralAssignmentTemplateComponent

Inside the component where need to use it you should add the container

<div assignment-container></div>

In the .ts file you should add a reference to container. //Select by type @ViewChild(AssignmentContainer) assignmentTemplateContainer;

Add service instance . Pass the container reference.

 ngAfterViewInit() {
        this.assignmentTemplateService.initTemplate(this.assignmentTemplateContainer, this.model, this.permissions);
    }
Toshkata Tonev
  • 1,431
  • 11
  • 12
  • 1
    While i think this is beter (and it could be improved more with some polymorphism instead of the switch case).,it doesn't quite feel like the angular way. I'll wait a bit more before accepting your answer. – Bob Brinks Apr 23 '18 at 08:31