2

I'm trying to build a website which provides the functionality of uploading your own courses.

Course Structure

    Name of course
    |-Module1
      |-Lecture1
      |-Lecture2
    |-Module2
      |-Lecture1
      |-Lecture2

Using Angular I'm trying to create a dynamic form which will add/remove modules within the course and lecture within a module

So far, I have written the following -

course-upload.component.ts

export class CourseUploadComponent implements OnInit {

    courseUploadForm: FormGroup;

    constructor(private formBuilder: FormBuilder) { }

    ngOnInit() {
        this.courseUploadForm = this.formBuilder.group({
            coursename: ['', Validators.required],
            modules: this.formBuilder.array([
                this.initModules()
            ])
        })
    }

    initModules() {
        return this.formBuilder.group({
            modulename: ['', Validators.required],
            lectures: this.formBuilder.array([
                this.initLecture()
            ])
        });
    }

    initLecture() {
        return this.formBuilder.group({
            lecturename: ['', Validators.required],
            description: ['', Validators.required],
            lecture: ['', Validators.required]
        });
    }

    addModule() {
        const control = <FormArray>this.courseUploadForm.get('modules');
        control.push(this.initModules());
    }

    addLecture() {
        const control = <FormArray>this.courseUploadForm.get('lectures');
        control.push(this.initLecture());
    }

    removeModule(i: number) {
        const control = <FormArray>this.courseUploadForm.get('modules');
        control.removeAt(i);

    } 

    removeLecture(i: number) {
        const control = <FormArray>this.courseUploadForm.get('lectures');
        control.removeAt(i);
    }

    getModulesControls(i: number) {
        >>>> return [(this.courseUploadForm.controls.modules as FormArray).controls[i]['controls']];
    }

    getLecturesControls(i: number) {
        return [(this.courseUploadForm.controls.lectures as FormArray).controls[i]['controls']];
    }

}

course-upload.component.html

<form [formGroup]="courseUploadForm" novalidate>

    <div formArrayName="modules">

        <mat-card *ngFor="let module of courseUploadForm.get('modules').value; let i=index">

            <mat-card-subtitle>
                {{i+1}}
            </mat-card-subtitle>

            <div [formGroupName]="i">

                <mat-form-field>
                    <mat-label>Module Name</mat-label>
                   **>>>** <input matInput placeholder="Module Name" formControlName="modulename">
                    <ng-container *ngFor="let control of getModulesControls(j)">
                        <mat-error *ngIf="!control.name.valid">Name Required</mat-error>
                    </ng-container>
                </mat-form-field>

                <div formArrayName="lectures">

                    <mat-card *ngFor="let lecture of module.get('lectures').value; let j=index">

                        <mat-card-subtitle>
                            Lecture {{i+1}}: {{lecture.name}}
                        </mat-card-subtitle>

                        <div [formGroupName]="j">

                            <mat-form-field>
                                <mat-label>Name</mat-label>
                                <input matInput placeholder="Lecture Name" formControlName="lecturename">
                                <ng-container *ngFor="let control of getLecturesControls(j)">
                                    <mat-error *ngIf="!control.name.valid">Name Required</mat-error>
                                </ng-container>
                            </mat-form-field>

                            <mat-form-field>
                                <mat-label>Description</mat-label>
                                <input matInput placeholder="Lecture Description" formControlName="description">
                                <ng-container *ngFor="let control of getLecturesControls(j)">
                                    <mat-error *ngIf="!control.description.valid">Description Required</mat-error>
                                </ng-container>
                            </mat-form-field>

                            <mat-form-field>
                                <mat-label>Lecture</mat-label>
                                <input matInput placeholder="Lecture Video" formControlName="lecture">
                                <ng-container *ngFor="let control of getLecturesControls(j)">
                                    <mat-error *ngIf="!control.lecture.valid">Lecture Video Required</mat-error>
                                </ng-container>
                            </mat-form-field>
                            <mat-card-actions>
                                <button mat-raised-button color="accent" (click)="addLecture()">Add Another
                                    Lecture</button>
                                <button mat-raised-button color="warn"
                                    *ngIf="module.get('lectures')['controls'].length > 1"
                                    (click)="removeLecture(j)">Remove This Lecture</button>
                            </mat-card-actions>
                        </div>

                    </mat-card>

                </div>
                <mat-card-actions>
                    <button mat-raised-button color="accent" (click)="addModule()">Add Another Module</button>
                    <button mat-raised-button color="warn"
                        *ngIf="courseUploadForm.get('modules')['controls'].length > 1" (click)="removeModule(i)">Remove
                        This Module</button>
                </mat-card-actions>
            </div>

        </mat-card>

    </div>

</form>

I get the error:

Cannot read property 'controls' of undefined

at CourseUploadComponent.getModulesControls 

at CourseUploadComponent_mat_card_2_Template 

I've highlighted the lines that throw the error with ** > **

Some help?

1
  • So, I made those changes according to the responses so far but now I get module_r1.get is not a function: stackblitz.com/edit/angular-ivy-eqnvnd Commented Jun 16, 2020 at 12:21

2 Answers 2

2

j is not defined here it should have been smg different

<ng-container *ngFor="let control of getModulesControls(j)">
    <mat-error *ngIf="!control.name.valid">Name Required</mat-error>
</ng-container>

I suppose you meant to use the variable i there: getModulesControls(i)

Also, line 5 of the HTML file the variable module is defined as an Object. Line 23 module.get('lectures') looks like you expect a FormGroup in the module variable. Take a look at this example from Angular docs. Pay attention to both HTML markup and TS. You will need to create several getters like get cities(): FormArray (:

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

5 Comments

Thank you, didn't notice that. But that doesn't solve the problem.
happy to help (: don't forget to select the answer and\or vote (:
Would you be so kind to check my StackBlitz I made? stackblitz.com/edit/angular-ivy-eqnvnd
in line 5 of the HTML file module is defined as an Object. in line 23 module.get('lectures') looks like you expect a FormGroup in the module variable. Take a look on this example angular.io/api/forms/FormArrayName#example. on both HTML markup and TS. You will need to create several getters like get cities(): FormArray in that example (:
That fixes it. Update your answer and I'll select your answer :)
0

I think you have to check your given array empty or not. If empty you should return null,

    getModulesControls(i: number) {
         if(this.courseUploadForm.controls.modules.length <1){
              return null;
         }
         else{
             return [(this.courseUploadForm.controls.modules as FormArray).controls[i]['controls']];
         }
    }

1 Comment

I did this and it gave rise to another problem altogether - I'm making a StackBlitz and commenting on the question itself. stackblitz.com/edit/angular-ivy-eqnvnd

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.