angulartypescriptangular-materialfrontend

How to restrict user from opening the succeeding accordions until the form in current accordion is valid in Angular Material?


I have a requirement where I have forms in expansion panels, and I need to allow the user to navigate to succeeding accordions only after the form in the current accordion is filled.

For example, if there are five accordions and a form in each, the user can access the fourth accordion only after the forms in the first three are filled out. At any given point, the user can access the previous accordions. So, in this example, when the user is in the third accordion, they can access the first two accordions without any problem

The challenge is that if the user is in the third accordion and trying to access the fourth accordion without filling out the form, I should restrict that and trigger validation for the third accordion. The user shouldn't be able to open the fourth accordion at all. It should trigger form validation for the form in the third accordion and the third accordion should remain opened.

I followed How to prevent toggling mat-expansion-panel by clicking mat-expansion-panel-header?, How can I prevent a mat-expansion-panel from opening when clicked?, How to toggle Angular material expansion panel programmatically, How to open mat-expansion-panel when the form group of that panel is invalid? but no luck.

Here is what I have tried so far: Stackblitz Exmple (I have shared stackblitz example for a quick preview and better execution)


Solution

  • I fixed using Angular's ViewChildren and QueryList to access and manipulate the accordion panels dynamically.

    accordions = [0, 1, 2, 3, 4];
      forms: FormGroup[] = [];
      activeIndex: number | null = 0; // Open the first accordion by default
    
      @ViewChildren(MatExpansionPanel) accordionPanels!: QueryList<MatExpansionPanel>;
    
      constructor(private fb: FormBuilder) {
        this.accordions.forEach(() => {
          this.forms.push(this.fb.group({ field: ['', Validators.required] }));
        });
      }
    
    toggleAccordion(index: number, event: Event): void {
      event.stopPropagation(); // Prevent default event propagation
      if (index === 0 || (index > 0 && this.forms[index - 1].valid)) {
        this.activeIndex = this.activeIndex === index ? null : index;
      } else {
        this.forms[index - 1].markAllAsTouched();
        this.activeIndex = null; // Force close the current panel
      }
    }
    
    closePanel(index: number): void {
        const panel = this.accordionPanels.toArray()[index];
        if (panel) {
          panel.close();
        }
      }
    

    I have updated the Stackblitz Exmple also