angular8 报错Cannot create property 'validator' on string 'age'

发布于 2022-09-11 22:32:14 字数 8157 浏览 18 评论 0

如何调试angular程序,一有错误就指向源码?
程序是一个自定义表单组件,

typescript如下

import { Component, OnInit, Input, forwardRef, OnDestroy } from "@angular/core"
import {
  ControlValueAccessor,
  NG_VALUE_ACCESSOR,
  NG_VALIDATORS,
  FormControl,
  FormBuilder,
  FormGroup
} from "@angular/forms"
import {
  map,
  filter,
  startWith,
  debounceTime,
  distinctUntilChanged
} from "rxjs/operators"
import { Observable, Subscription } from "rxjs"
import { combineLatest, merge } from "rxjs"
import {
  subDays,
  subMonths,
  subYears,
  differenceInDays,
  differenceInMonths,
  differenceInYears,
  isBefore,
  parse,
  isDate,
  isFuture,
  format,
  isValid,
  toDate
} from "date-fns"
export enum AgeUnit {
  Year = 0,
  Month,
  Day
}
import { isValidDate } from "../../utils/date.util"
export interface Age {
  age: number
  unit: AgeUnit
}
@Component({
  selector: "app-age-input",
  templateUrl: "./age-input.component.html",
  styleUrls: ["./age-input.component.scss"],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => AgeInputComponent),
      multi: true
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => AgeInputComponent),
      multi: true
    }
  ]
})
export class AgeInputComponent
  implements ControlValueAccessor, OnDestroy, OnInit {
  selectedUnit = AgeUnit.Year
  @Input() daysTop = 90
  @Input() daysBottom = 0
  @Input() monthsTop = 24
  @Input() monthsBottom = 1
  @Input() yearsTop = 150
  @Input() yearsBottom = 0
  @Input() format = "yyyy-MM-DD"
  @Input() debounceTime = 300
  ageUnits = [
    { value: AgeUnit.Year, Label: "岁" },
    { value: AgeUnit.Month, Label: "月" },
    { value: AgeUnit.Day, Label: "天" }
  ]
  dateOfBirth
  private propageteChange = (_: any) => {}
  form: FormGroup
  sub: Subscription
  // dateFormat = "YYYY-MM-DD"
  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    const initDate = this.dateOfBirth
      ? this.dateOfBirth
      : toDate(subYears(Date.now(), 30))
    const initAge = this.toAge(initDate)

    // this.propageteChange("ss")
    this.form = this.fb.group({
      birthday: [initDate, this.validateDate],
      age: this.fb.group(
        {
          ageNum: [initAge.age],
          ageUnit: [AgeUnit.Year]
        },
        {
          validator: this.validateAge("ageNum", "ageUnit")
        }
      )
    })
    const birthday = this.form.get("birthday")
    const ageNum = this.form.get("age").get("ageNum")
    const ageUnit = this.form.get("age").get("ageUnit")
    const birthday$ = birthday.valueChanges.pipe(
      map(d => {
        return {
          date: d,
          from: "birthday"
        }
      }),
      debounceTime(this.debounceTime),
      distinctUntilChanged(),
      filter(_ => birthday.valid)
    )
    const ageNum$ = ageNum.valueChanges.pipe(
      startWith(ageNum.value),
      debounceTime(this.debounceTime),
      distinctUntilChanged()
    )
    const ageUnit$ = ageUnit.valueChanges.pipe(
      startWith(ageUnit.value),
      debounceTime(this.debounceTime),
      distinctUntilChanged()
    )
    const age$ = combineLatest(ageNum$, ageUnit$, (_n, _u) => {
      return this.toDate({
        age: _n,
        unit: _u
      })
    }).pipe(
      map(d => {
        return {
          date: d,
          from: "age"
        }
      }),
      filter(_ => this.form.get("age").valid)
    )
    const merged$ = merge(birthday$, age$).pipe(filter(_ => this.form.valid))
    this.sub = merged$.subscribe(d => {
      const age = this.toAge(d.date)
      if (d.from === "birthday") {
        if (age.age !== ageNum.value) {
          ageNum.patchValue(age.age, { emitEvent: false })
        }
        if (age.unit !== ageUnit.value) {
          this.selectedUnit = age.unit
          ageUnit.patchValue(age.age, { emitEvent: false })
        }
        this.propageteChange(d.date)
      } else {
        const ageToCompare = this.toAge(birthday.value)
        if (age.age !== ageToCompare.age || age.unit !== ageToCompare.unit) {
          birthday.patchValue(d.date, { emitEvent: false })
          this.propageteChange(d.date)
        }
      }
    })
  }

  writeValue(obj: any): void {
    // 用来写值的
    // this.selected = obj
    // this.propageteChange(this.selected)
    if (obj) {
      this.form.get("birthday").patchValue(format(obj, this.format))
    }
  }
  registerOnChange(fn: any): void {
    this.propageteChange = fn
  }
  registerOnTouched(fn: any): void {}
  toAge(dateStr: string): Age {
    const date = parse(dateStr, this.format, Date.now())
    const now = new Date()
    return isBefore(subDays(now, this.daysTop), date)
      ? { age: differenceInDays(now, date), unit: AgeUnit.Day }
      : isBefore(subMonths(now, this.monthsTop), date)
      ? { age: differenceInMonths(now, date), unit: AgeUnit.Month }
      : {
          age: differenceInYears(now, date),
          unit: AgeUnit.Year
        }
  }
  toDate(age: Age): string {
    const now = Date.now()

    switch (age.unit) {
      case AgeUnit.Year: {
        return format(subYears(now, age.age), this.format)
      }
      case AgeUnit.Month: {
        return format(subMonths(now, age.age), this.format)
      }
      case AgeUnit.Day: {
        return format(subDays(now, age.age), this.format)
      }
      default: {
        return null
      }
    }
  }
  ngOnDestroy() {
    if (this.sub) {
      this.sub.unsubscribe()
    }
  }
  validate(c: FormControl): { [key: string]: any } {
    const val = c.value
    if (!val) {
      return null
    }
    if (isValidDate(val)) {
      return null
    }
    return {
      dateOfBirthInvalid: true
    }
  }
  validateDate(c: FormControl): { [key: string]: any } {
    const val = c.value
    return isValidDate(val)
      ? null
      : {
          birthdayInvalid: true
        }
  }
  validateAge(ageNumKey: string, ageUnitKey: string) {
    return (group: FormGroup): { [key: string]: any } => {
      const ageNum = group.controls[ageNumKey]
      const ageUnit = group.controls[ageUnitKey]
      let result = false
      const ageNumVal = ageNum.value
      switch (ageUnit.value) {
        case AgeUnit.Year: {
          result = ageNumVal >= this.yearsBottom && ageNumVal < this.yearsTop
          break
        }
        case AgeUnit.Month: {
          result = ageNumVal >= this.monthsBottom && ageNumVal < this.monthsTop
          break
        }
        case AgeUnit.Day: {
          result = ageNumVal >= this.daysBottom && ageNumVal < this.daysTop
          break
        }
        default: {
          result = false
          return null
        }
      }
    }
  }
}

Html如下

<form [formGroup]="form" class="age-input">

  <mat-form-field>
    <input matInput [matDatepicker]="birthday" placeholder="出生日期" formControlName="birthday" type="text">
    <mat-error></mat-error>
    <mat-datepicker-toggle matSuffix [for]="birthday"></mat-datepicker-toggle>
    <!-- <button matSuffix [matDatepickerToggle]="birthday"></button> -->
    <mat-datepicker touchUi='true' #birthday></mat-datepicker>
  </mat-form-field>
  <ng-container formGroup="age">
    <div class="age-num">
      <mat-form-field>
        <input matInput placeholder="年龄" type="number" formControlName="birthday">
      </mat-form-field>
    </div>
    <div>
      <mat-button-toggle-group formControlName="ageUnit" [(ngModel)]="selectUnit">
        <mat-button-toggle *ngFor="let unit of ageUnits" [value]='unit.value'>
          {{unit.Label}}
        </mat-button-toggle>
      </mat-button-toggle-group>
    </div>
    <mat-error *ngIf="form.get('age').hasError('ageInvalid')">年龄或单位不正确</mat-error>
  </ng-container>
</form>

报错如下

clipboard.png

问题描述

问题出现的环境背景及自己尝试过哪些方法

相关代码

// 请把代码文本粘贴到下方(请勿用图片代替代码)

你期待的结果是什么?实际看到的错误信息又是什么?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

被翻牌 2022-09-18 22:32:14

应该是formControlName 有问题,少了一个formControlName=age的表单组件。

  <mat-form-field>
    <input matInput placeholder="年龄" type="number" formControlName="birthday">
  </mat-form-field>

你这个的formControlName=age应该写错了吧。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文