Angular - 无法使用 Material Datepicker 取消订阅 valueChanges

问题描述 投票:0回答:1

我在 Angular Material Datepicker 中发出日期时遇到了一个持续的问题。我正在使用一个应用程序,其中 Contentstack 中有 JSON 文件。 JSON 文件包含 Angular Reactive 表单的控件以及每个控件的电子邮件模板。在一些模板中有一个日期控件。 JSON 文件的想法是,反应式表单填充 JSON 文件中的电子邮件模板,然后将其发送到视图。我在表单中使用 MatDatepicker 来选择日期。我一直在努力解决的问题是,即使表单显示的日期为 08/31/2019,当电子邮件预览在视图中显示时,日期显示为 2019 年 8 月 31 日星期四 00:00 等。我想要它在电子邮件中显示为 8/31/2019。我知道问题与格式化选择和发射之间的日期有关。

我找到了解决这个问题的方法。每次进行更改时,我们都会调用一个名为 onChange() 的方法。该方法只是检查表单是否有效,如果有效,则将 EventEmitter 发射设置为 true,否则设置为 false。我发现如果我循环 formGroup 键,取出 formControlName 中带有日期的键,订阅它们的 valueChanges,然后对每次更改进行日期格式化,电子邮件预览将正确显示日期。

问题就在这里。完成此操作后,当您单击日期选择器日历上的日期时,日历将不再消失。您必须在日历外部单击才能使其消失。与此同时,即使您让日历消失,调用也会继续进行,直到您最终达到调用堆栈限制。

根据此处的其他问题和答案,我尝试添加一个 Subscription 对象并将其与 valueChanges 一起使用,然后在 ngOnDestroy 中取消订阅它。这对解决问题没有任何帮助。谁能告诉我如何在检测到并格式化值更改后关闭呼叫?

这是组件 TypeScript 文件:

import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ContentStackService } from 'src/app/services/content-stack.service';
import { Customer } from 'src/app/models/customer.model';
import { AdditionalEmailTemplateDefintionResponse, Control, TemplateDefinition } from 'src/app/models/additional-email-template-definition.model';
import { formatDate } from '@angular/common';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-ssp-send-additional-email-template-detail',
  templateUrl: './ssp-additional-email-template-detail.component.html',
  styleUrls: ['./ssp-additional-email-template-detail.component.scss'],
})
export class SspSendAdditionalEmailTemplateDetailComponent implements OnInit, OnDestroy {
  @Input() customer : Customer;
  @Input() selectedTemplateId:string;
  @Input() formGroup: FormGroup;
  @Input() productType: string;
  @Input() stateCode: string;
  @Output() onTemplateDetailedFilled = new EventEmitter();
  @Output() templateDefinitionEmitter = new EventEmitter<TemplateDefinition>();
  customerInfoLookUp = new Map<string, string>();
  public templateDefinition!: TemplateDefinition;
  public subscr: Subscription;

  constructor(
    private contentStackService: ContentStackService,
    private fb: FormBuilder) {}

  ngOnInit(): void {
    console.log('*****SspSendAdditionalEmailTemplateDetailComponent****');
    console.log('input customer', this.customer);

    // Build customer lookup used for dynamic form population.
    this.buildCustomerLookup();

    this.contentStackService.getAdditionalEmailTemplateDefinition(this.selectedTemplateId)
      .subscribe((formData: AdditionalEmailTemplateDefintionResponse) => {
        console.log('getAdditionalEmailTemplateDefinition', formData);
        this.templateDefinition = formData.template_definition;
        this.createForm(formData.template_definition.controls);
        this.templateDefinitionEmitter.emit(this.templateDefinition);
      });
  }

  createForm(controls: Control[]) {
    for (const control of controls) {
      const validatorsToAdd = [];
      for (const [key, value] of Object.entries(control.validators)) {
        switch (key) {
          case 'min':
            validatorsToAdd.push(Validators.min(value));
            break;
          case 'max':
            validatorsToAdd.push(Validators.max(value));
            break;
          case 'required':
            if (value) {
              validatorsToAdd.push(Validators.required);
            }
            break;
          case 'requiredTrue':
            if (value) {
              validatorsToAdd.push(Validators.requiredTrue);
            }
            break;
          case 'email':
            if (value) {
              validatorsToAdd.push(Validators.email);
            }
            break;
          case 'minLength':
            validatorsToAdd.push(Validators.minLength(value));
            break;
          case 'maxLength':
            validatorsToAdd.push(Validators.maxLength(value));
            break;
          case 'pattern':
            validatorsToAdd.push(Validators.pattern(value));
            break;
          case 'nullValidator':
            if (value) {
              validatorsToAdd.push(Validators.nullValidator);
            }
            break;
          default:
            break;
        }
      }

      if (control.type === 'date') {
        this.formGroup.addControl(
          control.name,
          this.fb.control(new Date(), validatorsToAdd),
        );
      } else {
        console.log('control.value', control);
        if (control.path) {
          const pathSplit = control.path.split('.');
          if (pathSplit[0] === 'customer') {
            control.value = this.customerInfoLookUp.get(pathSplit[1]);
          }
        }
        this.formGroup.addControl(
          control.name,
          this.fb.control(control.value, validatorsToAdd),
        );
        this.onChange();
      }
    }
  }

  buildCustomerLookup() {
    this.customerInfoLookUp.set('firstName', 
        this.customer.person.personNameList[0].firstName);
        this.customerInfoLookUp.set('lastName', this.customer.person.personNameList[0].lastName);
  }

  onChange() {
    Object.keys(this.formGroup.controls).forEach(key => {
      if ((key).includes('Date')) {
        this.subscr = this.formGroup.get(key).valueChanges.subscribe(x => {
          console.log('Value of ' + key + ' changed to ' + x);
          this.formGroup.get(key).setValue(formatDate(x, 'MM/dd/YYYY', 'en-US'));
        });
      }
    });
    if (this.formGroup.valid) {
      this.onTemplateDetailedFilled.emit(true);
    } else {
      this.onTemplateDetailedFilled.emit(false);
    }
  }

  ngOnDestroy(): void {
    this.subscr.unsubscribe();
  }
}  

这是该组件的 HTML:

<ng-container [formGroup]="formGroup" *ngFor="let control of templateDefinition?.controls" class="ssp-template-detail-full-width">

    <!-- input type text-->
    <mat-form-field *ngIf="[
            'text',
            'password',
            'email',
            'number',
            'search',
            'tel',
            'url'
        ].includes(control.type)" class="ssp-template-detail-full-width">
        <mat-label>{{control.label}}</mat-label>
        <input matInput [id]="control.name" [formControlName]="control.name" [type]="control.type" (change)="onChange()">
    </mat-form-field>

    <!-- input type textarea -->
    <mat-form-field *ngIf="[
            'textarea'
            ].includes(control.type)" class="ssp-template-detail-full-width">
        <mat-label>{{control.label}}</mat-label>
        <textarea matInput [id]="control.name" [formControlName]="control.name" (change)="onChange()"></textarea>
    </mat-form-field>

    <!-- checkbox-->
    <p *ngIf="[
            'checkbox'
            ].includes(control.type)" class="ssp-template-detail-full-width">
        <mat-checkbox [id]="control.name" [formControlName]="control.name" (change)="onChange()">{{control.label}}</mat-checkbox>
    </p>


    <!-- dropdown-->
    <mat-form-field *ngIf="[
            'dropdown'
            ].includes(control.type)" class="ssp-template-detail-full-width">
        <mat-label>{{control.label}}</mat-label>
        <mat-select [id]="control.name" [formControlName]="control.name" (change)="onChange()">
            <mat-option *ngFor="let option of control.options" [value]="option.value">
                {{option.option}}
            </mat-option>
        </mat-select>
    </mat-form-field>


    <!-- date -->
    <mat-form-field *ngIf="[
        'date'
        ].includes(control.type)" class="ssp-template-detail-full-width">
        <mat-label>{{control.label}}</mat-label>
        <input matInput [matDatepicker]="picker" [id]="control.name" [formControlName]="control.name" (change)="onChange()">
        <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
        <mat-datepicker #picker></mat-datepicker>
        <mat-hint>MM/DD/YYYY</mat-hint>
    </mat-form-field>

    <!-- input type text-->
    <mat-form-field *ngIf="[
            'time'
        ].includes(control.type)" class="ssp-template-detail-full-width">
        <mat-label>{{control.label}}</mat-label>
        <input matInput [id]="control.name" [formControlName]="control.name" type="text" (change)="onChange()">
    </mat-form-field>

</ng-container>
angular observable angular-reactive-forms subscription mat-datepicker
1个回答
0
投票
export class SspSendAdditionalEmailTemplateDetailComponent ...
  private subscriptionList = new Subscription(); // Instantiate it immediately
  ...
  onChange() {
    Object.keys(this.formGroup.controls).forEach(key => {
      if ((key).includes('Date')) {
        // This is why I call it subscriptionList, you are adding subscriptions to the list
        this.subscriptionList.add(
          this.subscr = this.formGroup.get(key).valueChanges.subscribe(x => {
            console.log('Value of ' + key + ' changed to ' + x);
            this.formGroup.get(key).setValue(formatDate(x, 'MM/dd/YYYY', 'en-US'));
          })
        );
      }
    });
    if (this.formGroup.valid) {
      this.onTemplateDetailedFilled.emit(true);
    } else {
      this.onTemplateDetailedFilled.emit(false);
    }
  }

  ngOnDestroy(): void {
    // Then you tell the entire list to unsubscribe.
    // If a sub in the list is already unsubscribed, it silently passes.
    this.subscriptionList.unsubscribe();
  }
© www.soinside.com 2019 - 2024. All rights reserved.