NG-ZORRO / ng-zorro-antd

Angular UI Component Library based on Ant Design
https://ng.ant.design
MIT License
8.86k stars 3.91k forks source link

Clicking on month/year next/previous buttons in DatePicker causes parent form to submit #7012

Closed slavafomin closed 2 years ago

slavafomin commented 2 years ago

Reproduction link

https://stackblitz.com/edit/ng-zorro-antd-ivy-zmsagn

Steps to reproduce

Put DatePicker inside of a <form> and click on one of the month/year next/previous buttons.

What is expected?

DatePicker shouldn't trigger form submission.

What is actually happening?

Form submission is triggered.

Environment Info
ng-zorro-antd 12.0.1
Browser Any

This is happening, because buttons miss type="button" attribute.

slavafomin commented 2 years ago

I have to use the following workaround to fix this issue in our application:


  /**
   * Makes sure that each button has a proper `type=button`
   * attribute, otherwise they could submit a parent form.
   */
  private fixCalendarButtons() {

    if (!('querySelector' in document)) {
      console.warn(`DOM query selector is not available`);
      return;
    }

    const element = this.getCalendarElement();

    element?.querySelectorAll('button:not([type])').forEach(
      element => element.setAttribute('type', 'button')
    );

  }

  private clearCalendarMutationObserver() {
    if (this.calendarMutationObserver) {
      this.calendarMutationObserver.disconnect();
      this.calendarMutationObserver = undefined;
    }
  }

  /**
   * Monitors calendar DOM for changes and
   * applies buttons fix automatically.
   */
  private setupCalendarMutationObserver() {

    // Clearing previous observer
    this.clearCalendarMutationObserver();

    const element = this.getCalendarElement();
    if (!element) {
      return;
    }

    this.calendarMutationObserver = (
      new MutationObserver(() => this.fixCalendarButtons())
    );

    this.calendarMutationObserver.observe(element, {
      subtree: true,
      childList: true,
    });

  }

  private getCalendarElement(): (HTMLElement | undefined) {

    if (!this.calendarElement?.nativeElement) {
      console.warn(
        `Can't obtain DatePicker DOM element, ` +
        `is it rendered?`
      );
      return;
    }

    return this.calendarElement.nativeElement;

  }

Mutation observer is needed, because some buttons are getting rendered dynamically and do change when navigating around the calendar.