1

i am trying to create nested forms but i am stuck in the second level and not sure how the addAttribute and removeAttribute will look like ?

export class ExportFormComponent implements OnInit {

    public exportForm: FormGroup;

    constructor( private formBuilder: FormBuilder ) { }

    ngOnInit() {
        this.exportForm = this.formBuilder.group( {
            dataType: [''],
            items: this.formBuilder.array( [
                this.initItem(),
            ] )
        });
    }

    initItem() {
        return this.formBuilder.group( {
            exportExpression: [''],
            description: [''],
            updatable: [true],
            attributes: this.formBuilder.array( [
                this.initAttribute(),
            ] )
        });
    }

    initAttribute() {
        return this.formBuilder.group( {
            exportExpression: [''],
            localizedRawField: [''],
            localizable: [true],
        });
    }

    addItem() {
        const control = <FormArray>this.exportForm.controls['items'];
        control.push( this.initItem() );
    }

    removeItem( i: number ) {
        const control = <FormArray>this.exportForm.controls['items'];
        control.removeAt( i );
    }

    addAttribute() {

    }

    removeAttribute(  ) {

    }

    save( exportConfiguration: ExportConfiguration ) {
        console.log( exportConfiguration );
    }
}

My interface tree

export interface ExportConfiguration {
    dataType?: string,
    items?: Item[],
}

export interface Item {
    exportExpression?: string,
    description?: string,
    updatable?: boolean,
    attributes?: Attribute[],
}

export interface Attribute {
    exportExpression?: string,
    localizable?: boolean,
    localizedRawField?: string,
    rules?: TransformationRule[]
}

export interface TransformationRule {
    dataPathKey?: string,
    expressionCheck?: boolean,
    expression?: string,
}

EDIT

Okay so i used the demo posted as one of the answers but i get null in the following ( this.itemsCtrl.get( '${index}.attributes' ) as FormArray )

addAttribute( index: number ) {
        ( this.itemsCtrl.get( '${index}.attributes' ) as FormArray ).push( this.initAttribute() );
    }
2
  • Can you describe the final shape of the tree of controls for your form, indicating which parts are dynamic? Commented Feb 23, 2017 at 14:56
  • @AngularFrance : I updated. Does it help Commented Feb 23, 2017 at 15:43

1 Answer 1

1

First of all I prefer to create references in component to handle the controls, instead of calling .controls or .get all the time.

So, your add's and remove's functions will be cleaner as your template.

It could be like this:

exportForm: FormGroup;
dataTypeCtrl: FormControl;
itemsCtrl: FormArray;

constructor(private formBuilder: FormBuilder) { }

ngOnInit() {
  this.dataTypeCtrl = this.formBuilder.control('');
  this.itemsCtrl = this.formBuilder.array([this.initItem()]);

  this.exportForm = this.formBuilder.group( {
      dataType: this.dataTypeCtrl,
      items: this.itemsCtrl
  });
}

// ...

addItem() {
  this.itemsCtrl.push(this.initItem());
}

removeItem(i: number) {
  this.itemsCtrl.removeAt(i);
}

addAttribute(index: number) {
  this.itemsCtrl.get(`${index}.attributes`).push(this.initAttribute())
}

removeAttribute(itemIndex: number, attrIndex: number) {
  this.itemsCtrl.get(`${itemIndex}.attributes`).removeAt(attrIndex)
}

Then, in template you can access directly the controls, as below:

...
<div formArrayName="items" *ngFor="let itemCtrl of itemsCtrl.controls; let i = index">
  <div [formGroupName]="i">
    <button (click)="removeItem(i)">Remove item</button>
    <input type="text" formControlName="description">
    <!-- other inputs from items -->

    <button (click)="addAttribute(i)">Add attribute</button>

    <div formArrayName="attributes" *ngFor="let attributeCtrl of itemCtrl.get('attributes').controls; let j = index">
      <div [formGroupName]="j">
        <button (click)="removeAttribute(i, j)">Remove attribute</button>
        <input type="text" formControlName="exportExpression"
        <!-- other inputs from attributes -->
      </div>
    </div>
  </div>
</div>

See DEMO

Sign up to request clarification or add additional context in comments.

11 Comments

Thank you. What does it means this.formBuilder.control('')
@SaurabhKumar You're welcome :) this.formBuilder.control('') is a sugar(or not) syntax to new FormControl(''). I've edited the answer to include a demo, you can check all the source there.
Oh this is super cool . Awesome. Thanks a lot I am learning angular :)
Oh .. my editor says Property 'push' does not exist on type 'AbstractControl'. Could you please tell me what should i do. Same for removeAt. itemsCtrl.get( '${index}.attributes' ).push...
@SaurabhKumar (itemsCtrl.get(...) as FormArray).push.. the same for removeAt.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.