javascript - Angular2 : View updates when data changes, 但不适用于 ngif

标签 javascript angular typescript angular2-template

我正在使用 Angular2 构建一个简单的日历。使用此代码一切正常(并且按预期):

app.component.ts

import { Component, OnInit } from '@angular/core';
import { Calendar }  from '@npm/calendar';

export class CalendarWeek {
  days: Array<any>;
  data?: string; // reserved for further use
}

@Component({
 selector: 'my-app',
 templateUrl: 'app/app.component.html',
 styleUrls: ['app/app.component.css']
})

export class AppComponent implements OnInit { 

  title = 'Calendar';
  weeks:CalendarWeek[];

  months:string[] = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December'
 ];

 weekDays:string[] = [
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday',
    'Sunday'
  ];

 monthString:string;
 month:number;
 year:number;

 prevMonth:Boolean;
 nextMonth:Boolean;
 thisMonth:Boolean;
 showMonth:Boolean;

 getWeeks(): void {

   let cal = new Calendar(1); // start with Monday
   let mdc = cal.monthDates(this.year, this.month);

   this.weeks = [];
   this.monthString = this.months[this.month];
   this.prevMonth = this.nextMonth = this.thisMonth = false;

   for (let i=0; i<mdc.length; i++) {
     this.weeks.push({ days: mdc[i] });
   }
 }

 getThisMonth(): void {
   this.month = new Date().getMonth();
   this.year = new Date().getFullYear();
   this.getWeeks();
 }

 getPreviousMonth(): void {
   this.getMonth(-1);
 }

 getNextMonth(): void {
  this.getMonth(+1);
 }

 getMonth(direction): void {
   if ( this.month == 11 && direction > 0 ) {
     this.year++;
     this.month = 0;

   } else if (this.month == 0 && direction < 0) {
    this.year--;
    this.month = 11;

   } else {
    this.month = this.month + direction;
   }

   this.getWeeks();
 }

 ngOnInit(): void {
   this.getThisMonth();
 }
}

app.component.html

<h1>{{title}}</h1>

<div class="period">
  <a (click)="getPreviousMonth()" class="monthPager">&lt;</a>
  <a (click)="getThisMonth()" class="monthPager">now</a>
  <a (click)="getNextMonth()" class="monthPager">&gt;</a>
  <span class="now">{{monthString}}, {{year}}</span>
</div>

<div class="week">
  <div *ngFor="let day of weekDays" class="week-day">
    <span>{{day}}</span>
  </div>
</div>

<div *ngFor="let week of weeks" class="week">
  <div *ngFor="let day of week.days" class="day"> 
    <span class="day-date">{{day | date:'d'}}</span>
  </div>
</div>

正如我所说,一切正常,我可以来回翻阅几个月的内容。 npm/calendar 模块提供了每月每周(从周一到周日)日期对象数组的简单数组。问题是,第一周和最后一周还可以包括上个月或下个月的日期,因此我想检查这些情况并显示一次月份名称。所以我会10月31日、11月1日、2、3、[...]、30、12月1、2、3、4

为了执行此检查,我在 app.component.html 中添加了一行,因此它看起来像这样:

<div *ngFor="let week of weeks" class="week">
    <div *ngFor="let day of week.days" class="day">
        <span *ngIf="checkMonth(day)" class="day-month">{{day | date:'MMM'}}</span>
        <span class="day-date">{{day | date:'d'}}</span>
    </div>
</div>

将此方法添加到app.component.ts:

  checkMonth(day): Boolean {

    let mo = day.getMonth();
    this.showMonth = false;

    if  ( mo < this.month && !this.prevMonth) {
      this.prevMonth = true;
      this.showMonth = true;
    }

    if ( mo > this.month && !this.nextMonth) {
      this.nextMonth = true;
      this.showMonth = true;
    }

    if ( mo == this.month && !this.thisMonth) {
      this.thisMonth = true;
      this.showMonth = true;
    }

    return this.showMonth;

  }

现在我遇到了一个错误

Subscriber.ts:238 EXCEPTION: Error in app/app.component.html:17:14 caused by: Expression has changed after it was checked. Previous value: 'true'. Current value: 'false'.

我无法再翻阅几个月了。仍在评估点击并生成新数组,但 View 不会更新。哦,奇怪的是, checkMonth 似乎也按预期评估日期,它仍然不显示月份的名称 - 我只得到纯日期,没有月份名称。

什么可能导致这里出现问题? 谢谢。

UPD:这是骗子:https://plnkr.co/edit/HrIlOG9fsGhIS3w12bV0?p=preview

最佳答案

我已经更新了您的 plunkr:https://plnkr.co/edit/TCVkfJUDvLH68ybFJXqA?p=preview

问题

您收到该消息的原因是

  • 您检查属性的值(例如 this.thisMonth)
  • 您更新了该属性的值,从而更改了它。
  • Angular 在开发模式下将运行每个检查两次,以帮助您过滤掉可能的错误。 (这在您的情况下失败了。由于您更新了类上的属性,如果您运行 checkMonth() 函数两次,它将产生两个不同的结果,其中一个将为 true,第二个将为 false,因为您已经更新了运行检查的值。

为了避免后者,您应该更多地分离逻辑(尽管我认为您编写的代码非常有意义,并且应该可以用于生产,但这不是 Angular2 的看法)。

因此,您可以编写一个仅基于输入参数返回相同值的函数,而不是“来回”检查和设置属性。

<小时/>

可能的解决方案

getShowableDays(weeks: Array): Array {
    let showableDays: Array<any> = [];
    let shownMonths: Array<number> = [];

    weeks.forEach((week) => {
      for(let day of week.days) {
        if(shownMonths.indexOf(day.getMonth()) === -1) {
          showableDays.push(day);
          shownMonths.push(day.getMonth());
        }
      }
    });

    return showableDays;
}

checkMonth(day): Boolean {
    let showableDays: Array<any> = this.getShowableDays(this.weeks);

    for(let showableDay of showableDays) {
      if(showableDay === day)
        return true;
    }

    return false;
  }

这将占用您当前的几周时间,并检查每个月的第一次出现是什么。基于此,无论您运行此代码多少次,结果都将始终相同,因为它将独立于应用程序的其余部分或应用程序的状态运行。

关于javascript - Angular2 : View updates when data changes, 但不适用于 ngif,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40716431/

相关文章:

javascript - 在 Angular 4 生产版本中出现错误 ReferenceError : userAgent is not defined, (Angular 4)

javascript - typescript 错误: type this is not assignable to type this

javascript - Angular4自定义单元格样式中的ag-grid

javascript - 如何获取 CKEditor 5 的值?

javascript - ionic 单选按钮 - Controller 范围无法识别更改

javascript - 如何在悬停另一个 div 时保持 div 可见

javascript - 在异步类型函数中省略返回

javascript - 使用 Nock 模拟 NodeJS 请求和响应

javascript - 基于 OR 表达式的变量赋值简写

css - Angular2 Material Dialog css,对话框大小