0

I am making angular application with angular dynamic form where i need to split up the form into two parts.

In which i have input field firstname and lastname at first page and on click the next button the children which has email and dropdown needs to get loaded..

Html:

    <form (ngSubmit)="onSubmit()" [formGroup]="form">

        <div *ngFor="let question of questions" class="form-row">
            <ng-container *ngIf="question.children">
                <div [formArrayName]="question.key">
                    <div *ngFor="let item of form.get(question.key).controls; let i=index" [formGroupName]="i">
                        <div *ngFor="let item of question.children">
                            <app-question [question]="item" [form]="form.get(question.key).at(i)"></app-question>
                        </div>
                    </div>
                </div>
            </ng-container>
            <ng-container *ngIf="!question.children">
                <app-question [question]="question" [form]="form"></app-question>

            </ng-container>
        </div>

  <button (click)="goBack()"> Previous </button> &nbsp;
  <button (click)="addControls('myArray')"> Next </button> 

        <div class="form-row">
            <button type="submit" [disabled]="!form.valid">Save</button>
    </div>
  </form>

Ts:

  @Input() questions: QuestionBase<any>[] = [];
  form: FormGroup;
  payLoad = '';

  page: number = 0

  constructor(private qcs: QuestionControlService) { }

  ngOnInit() {
    this.form = this.qcs.toFormGroup(this.questions);
  }

  onSubmit() {
    this.payLoad = JSON.stringify(this.form.value);
  }
  addControls(control: string) {
    let question: any = this.questions.find(q => q.key == control);
    let children = question ? question.children : null;
    if (children)
      (this.form.get(control) as FormArray).push(this.qcs.toFormGroup(children))
  }
  removeControls(control: string){
    let array=this.form.get(control) as FormArray;
    array.removeAt(array.length-1);
  }

  goBack() {
    //Function to move to previous step
  }

Working demo:

https://stackblitz.com/edit/angular-x4a5b6-p4agaq

In this demo with code you can see on every click over add button the children (array) get appended to the below in the same page..

I am also having removeControl function which has,

  removeControls(control: string){
    let array=this.form.get(control) as FormArray;
    array.removeAt(array.length-1);
  }

To be clear i am not going to use this now and not going to remove anything anywhere.. Only thing is on click next the children via addControl function adds the children to next next page and on previous get back to previous step.

In order to append to the same page given in demo, it should move to next page and again on click the previous it should get to original state on every next click it should give a new email and dropdown and on previous a getback to previous step..

It should get moved like slider with sliding effect while moving forth and back..

Everything needs to be in pure angular and javascript/typescript based and there is no jquery.. As you could able to see in my demo i have not included any library or jquery..

Kindly help me to achieve the result.. Stucked for a long time..

Maniraj Murugan
  • 8,868
  • 20
  • 67
  • 116

4 Answers4

1

Pass array in the goBack method which you want to remove

   <button (click)="goBack('myArray')"> Previous </button> &nbsp;    

Put this method in the component ts file

  goBack(control: string) {
    let question: any = this.questions.find(q => q.key == control);
     let children = question ? question.children : null;
    if (children)

      (this.form.get(control) as FormArray).removeAt(children.length-1)

  }
Ushmi Dave
  • 276
  • 1
  • 7
1

If you want to create stepper form you can project the form and use the formcontrol to connect the parentformgroup.

ParentComponent.ts


Next
<div class="step container" *ngIf="form.valid && nextForm" >
 <form [formGroup]="step2">
  <app-step2 (control)="enableSave($event)"></app-step2>
  <button class="btn btn-primary"  (click)="moveToPrevious()" >Previous</button>
</form>
</div>
<hr>
<button
[disabled]="eSave"
 class="btn btn-primary" (click)="save()">Save</button>
</app-stepper-wrapper>

ParentComponent.ts

name = 'Angular';
  eSave = true;
  form = new FormGroup({});
  step2 = new FormGroup({});
  nextForm = false;

  ngOnInit() {
    this.form.statusChanges.subscribe(s => this.eSave = true);
  }   
  moveToNext() {
    if (this.form.valid) {
      this.nextForm = true;
    }
  }   
  moveToPrevious() {
    this.nextForm = false;
  }
  save() {
    console.log(this.form);
    console.log(this.step2);
  }
  enableSave($event) {
    if ($event == 'VALID' && this.form.valid) {

      this.eSave = false;
    }
  }

Example:https://stackblitz.com/edit/angular-nynvvr

Chellappan வ
  • 23,645
  • 3
  • 29
  • 60
  • Chellappan, I couldn't able to find anything on click next button in your solution.. – Maniraj Murugan Nov 15 '18 at 07:50
  • Chellappan, Can you help me here, stucked for past two days, https://stackoverflow.com/questions/53316581/remove-the-selected-option-from-select-box – Maniraj Murugan Nov 15 '18 at 09:48
  • I will try to help later – Chellappan வ Nov 15 '18 at 09:55
  • Bro oru small help.. How to place a label instead of input box inside reactive form?? Here https://stackblitz.com/edit/angular-zjoyaa?file=src/app/app.component.html you can see that each ```template name``` is generated as input boxes but i need to keep it as heading and under each heading there will be the child boxes.. How to change that template name input box to heading?? It also needs to be in dynamic as per the selected template.. – Maniraj Murugan Nov 30 '18 at 06:56
  • i will check bro!! – Chellappan வ Nov 30 '18 at 06:59
  • you want place holder? – Chellappan வ Nov 30 '18 at 07:04
  • No bro as a heading i need.. Say ```Template One``` will be inside h1 tag as, may be like this ```

    {{template_name}}

    ``` now it was given as `````` whereas i need to change it as h1 tag.. How to change that one to h1 tag.. Only choosen template names i need to have as heading.. And child properties will be input boxes asusual..
    – Maniraj Murugan Nov 30 '18 at 07:06
  • Okay bro i will try give me a hour since i have task bro – Chellappan வ Nov 30 '18 at 07:14
  • No probs bro but don't forgot bro.. I am unable to post as question because i have reached the maximum limit of questions for a day.. – Maniraj Murugan Nov 30 '18 at 07:16
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/184507/discussion-between-chellappan-and-undefined). – Chellappan வ Nov 30 '18 at 08:40
  • Bro help me https://stackoverflow.com/questions/53626407/how-to-change-element-to-input-or-dropdown-in-angular-form – Maniraj Murugan Dec 05 '18 at 06:35
  • Okay bro you can take a reference from one of my question which is also similar but there it has different keys here for same key i need to manage either input or select box.. Look at it u will get idea https://stackoverflow.com/questions/53607677/change-form-elements-using-angular-form – Maniraj Murugan Dec 05 '18 at 06:43
  • Bro can you give answer for that asynchronous with stackblitz https://stackoverflow.com/questions/53680000/angular-subscribe-push-object-to-array – Maniraj Murugan Dec 08 '18 at 06:23
  • Bro can you help me?? https://stackoverflow.com/questions/53705500/deactivation-of-angular-router-link – Maniraj Murugan Dec 10 '18 at 12:36
0

Have try to reach your requirements : Except UI part. I hope you can handle you UI as your requirement.

TS :

    import { Component, Input, OnInit } from '@angular/core';
    import { FormGroup, FormArray } from '@angular/forms';

    import { QuestionBase } from './question-base';
    import { QuestionControlService } from './question-control.service';

    @Component({
      selector: 'app-dynamic-form',
      templateUrl: './dynamic-form.component.html',
      providers: [QuestionControlService]
    })
    export class DynamicFormComponent implements OnInit {

      @Input() questions: QuestionBase<any>[] = [];
      form: FormGroup;
      payLoad = '';

      page: number = 0;

      constructor(private qcs: QuestionControlService) { }

      ngOnInit() {
        this.form = this.qcs.toFormGroup(this.questions);
      }

      onSubmit() {
        this.payLoad = JSON.stringify(this.form.value);
      }
      addControls(control: string, index: any) {
        let array = this.form.get('myArray') as FormArray;
        if (array.length > this.page) {
          this.page = this.page + 1;
        } else {
          let question: any = this.questions.find(q => q.key == control);
          let children = question ? question.children : null;
          if (children)
            (this.form.get(control) as FormArray).push(this.qcs.toFormGroup(children))
          this.page = this.page + 1;
        }
      }
      removeControls(control: string) {
        let array = this.form.get(control) as FormArray;
        array.removeAt(array.length - 1);
      }

      goBack() {
        if (this.page > 0) {
          this.page = this.page - 1;
        }
        //let array = this.form.get('myArray') as FormArray;
        //array.removeAt(array.length - 1);
        //Function to move to previous step
      }

    }

HTML : 

<div>
    <form (ngSubmit)="onSubmit()" [formGroup]="form">

        <div *ngFor="let question of questions" class="form-row">
            <ng-container *ngIf="question.children">
                <div [formArrayName]="question.key">
                    <div *ngFor="let item of form.get(question.key).controls; let i=index" [formGroupName]="i">
            <ng-template [ngIf]="i + 1 == page"> 
              <div *ngFor="let item of question.children">
                <app-question [question]="item" [form]="form.get(question.key).at(i)"></app-question>
              </div>
            </ng-template>
                    </div>
                </div>
            </ng-container>
            <ng-container *ngIf="!question.children">
                <app-question [question]="question" [form]="form"></app-question>

            </ng-container>
        </div>

  <button (click)="goBack()"> Previous </button> &nbsp;
  <button (click)="addControls('myArray',i)"> Next </button> 

        <div class="form-row">
            <button type="submit" [disabled]="!form.valid">Save</button>
    </div>
  </form> <br>

  <pre>
{{form?.value|json}}
</pre>
</div>

This will help you showing one page at a time. It will create new one if no next form exists. And on clicking previous it will navigate to old form.

Vishw Patel
  • 529
  • 2
  • 9
  • I am in the need of the children in the next view and not in same view. If it moves to next view then provide stackblitz moving to next – Maniraj Murugan Nov 15 '18 at 05:51
0

So, you want to remove lastly added control email and dropdown control from the form group array.

I have added the code into goBack() function to remove child form group controls.

Component:

  goBack() {
    //Function to move to previous step
    if(this.form.controls['myArray']){      
      const arr = <FormArray>this.form.controls.myArray;
      arr.removeAt(arr.length - 1);
    }
  }

Working demo: https://angular-x4a5b6-cc4kyr.stackblitz.io

Surjeet Bhadauriya
  • 6,755
  • 3
  • 34
  • 52
  • For this i already have removeControl function, you can see in example.. The thing is on click next button, the ```first name and last name``` needs to get hidden and the email and dropdown needs to come.. On click the previous button the ```firstname and lastname``` come.. This alone my requirement.. And no need of remove function now.. On each next click the ```dropdown and email``` needs to get show in next next screen.. The question is mainly to handle the thing in the different views and not on same page.. – Maniraj Murugan Nov 15 '18 at 05:23
  • Okay, I will suggest you make a new route in this case for email and dropdown. Because things become complex if you do manual hide and show. – Surjeet Bhadauriya Nov 15 '18 at 05:26