Search code examples
angularangular7angular-reactive-forms

How to add a value selected in the dropdown of the reactive form as an optional field in the object that creates the form?


I've created an angular reactive form. I can enter the name as a normal input field and type in the second dropdown field and the value for that type. There is a type called credential, after selecting it you can enter the user name & password as input fields. Only the value can be input for the non-credential types (String, Int, Number). The problem is that I currently create a createtial object in the form value and send the value to the backend as follows.

{
    "name": "TestJson",
    "type": "credential",
    "value": "",
    "credential": {
        "username": UserName,
        "password": password
    },
    "secure": false,
    "id": "bjbjsdfjb3334weknn12340nvf9r5df"
}

When it is not a credential type, the credential object sends as null.

{
    "name": "testJson",
    "type": "json",
    "value": "{\"name\":\"John\", \"age\":30, \"car\":null}",
    "credential": {
        "username": null,
        "password": null
    },
    "secure": false,
    "environment_id": "nmvsdmfvndfnkdfnsfldf,fd"
}

But when the value is sent with a string or integer type that is not a credential, an error comes from the backend because the values of the credential object are null. How to improve the form builder object so that the credential object is created when the credential type is sent and the credential object is not created when it is not?

form.html

<form id="add-form"
   [formGroup]="createVariableForm">
   <div class="field">
      <label
         for="add-name">{{name}}</label>
      <input id="add-name"
         type="text"
         name="name"
         formControlName="name"
         [maxlength]="64">
   </div>
   <div class="field">
      <label
         for="add-type">{{argument_type}}</label>
      <select
         id="add-type"
         placeholder="select"
         formControlName="type">
         <option value="string">{{string}}</option>
         <option value="credential">{{credential}}</option>
         <option value="boolean">{{boolean}}</option>
         <option value="number">{{number}}</option>
         <option value="integer">{{integer}}</option>
         <option value="json">Json</option>
      </select>
   </div>
   <div class="form-group" formGroupName = "credential">
      <div *ngIf="createVariableForm.get('type').value === 'credential'" class="field">
         <label
            for="add-value">Username</label>
         <input id="add-username"
            type="text"
            name="username"
            formControlName="username"
            [maxlength]="64">
      </div>
      <div *ngIf="createVariableForm.get('type').value === 'credential'" class="field">
         <label
            for="add-value">Password</label>
         <input id="add-password"
            type="password"
            name="password"
            formControlName="password"
            [maxlength]="64">
      </div>
   </div>
   <div *ngIf="createVariableForm.get('type').value !== 'credential'" class="field">
      <label
         for="add-value">{{value}}</label>
      <input id="add-value"
         type="text"
         name="value"
         formControlName="value"
         [maxlength]="64">
   </div>
   <div class="field">
      <input
         id="add-secure"
         type="checkbox"
         name="secure"
         formControlName="secure">
      <label
         for="add-secure">{{secure}}</label>
   </div>
   <div class="modal-buttonset">
      <button id="add-can"
         type="button"
         class="btn-modal"
         (click)="cancel()">{{cancel}}</button>
      <button id="add-save"
      type="button"
      class="btn-modal-primary no-validation"
      [disabled]="isBusy"
      (click)="add()">{{add}}</button>
   </div>
</form>

form.ts

ngOnInit(): void {
        this.createForm();
    }

    private createForm(): void {
        this.createVariableForm = this.formBuilder.group({
            name: [''],
            type: [''],
            value: [''],
            credential: this.formBuilder.group({
                username: [],
                password: [],
            }),
            secure: [false],
            environment_id: [this.UtilService.getEnvironmentId()]
        });
    }

Solution

  • You could subscribe to type FormControl changes to decide whether you need to include credential control to the form or not.

    ts

    private createForm(): void {
      this.createVariableForm = this.formBuilder.group({
        name: [''],
        type: [''],
        value: [''],
        secure: [false],
        environment_id: [1]
      });
    
      this.createVariableForm.get('type')?.valueChanges
        .pipe(takeUntil(this.destroyed$)).subscribe(value => {
          if (value === 'credential') {
            this.createVariableForm.addControl('credential', this.formBuilder.group({
              username: [],
              password: [],
            }))
          } else if (this.createVariableForm.get('credential')) {
            this.createVariableForm.removeControl('credential');
          }
        })
    }
    

    html

    <div *ngIf="createVariableForm.get('credential')" class="form-group" 
         formGroupName = "credential">
      <div  class="field">
        <label for="add-value">Username</label>
        <input id="add-username"
                type="text"
                name="username"
                formControlName="username"
                [maxlength]="64">
      </div>
      <div  class="field">
        <label for="add-value">Password</label>
        <input id="add-password"
                type="password"
                name="password"
                formControlName="password"
                [maxlength]="64">
      </div>
    </div>
    <div *ngIf="!createVariableForm.get('credential')" class="field">
      <label for="add-value">Value</label>
      <input id="add-value"
              type="text"
              name="value"
              formControlName="value"
              [maxlength]="64">
    </div>
    

    Ng-run Example