为什么使用日期获取器函数时,AngularJS的Digest进入无限循环?

6

我有一个DTO对象,其中包含一个日期参数。我将这个DTO封装在一个视图模型对象中,然后将其属性绑定到标签上,在我的视图中显示。

<label class="form-control">{{controller.ViewModel.Date}}</label>

在视图模型中,我有一个getter如下(我正在使用TypeScript)。

public get Date(): Date {
    return new Date(Date.parse(this.dto.Date));
    //return moment(this.dto.Date).toDate();
}

生成的JavaScript代码:

Object.defineProperty(ViewModel.prototype, "Date", {
    get: function () {
        return new Date(Date.parse(this.dto.Date));
    },
    enumerable: true,
    configurable: true
});

我认为问题出在getter中创建了新的日期对象,导致每次获取的日期都不同,直到模型稳定为止,这样会引起无限循环。

为什么Angular会这样做?
为什么它会一遍又一遍地调用getter,只调用一次有何问题?
我能否告诉Angular只调用一次getter并接受给定的值?


你使用的是哪个版本的Angular? - colin-higgins
Angular会运行连续的脏检查,直到所有监视器的结果稳定。在这里,每次您的监视器返回不同的对象,从而生成无限循环。有时,您可以通过在模板中调用toString()来解决这个问题。例如:{{ controller.ViewModel.Date.toString() }} - BiAiB
2
为什么不将函数的结果存储在 $scope 变量中,然后在 HTML 中使用该变量,而不是直接链接到函数呢? - David Meza
2个回答

3
如果您使用的是足够高的版本,可以尝试一次性绑定。请按照此处的说明操作:https://docs.angularjs.org/guide/expression#one-time-binding 您的假设基本上是正确的,即angular认为日期始终是新的。通过在getter中进行评估更改值时,会触发另一个脏检查和watch,这样会导致另一个digest。
您还能尝试提前解析日期吗?

我尝试像这样隐藏我的操作,但是我得到了相同的结果。 public get TxDate(): Date { let dtoDate = moment(this.dto.TxDate).toDate(); if (this._txDate === undefined || this._txDate === null) this._txDate = dtoDate; if (dtoDate.getFullYear() != this._txDate.getFullYear() || dtoDate.getMonth() != this._txDate.getMonth() || dtoDate.getDate() != this._txDate.getDate()) { this._txDate = dtoDate; } return this._txDate; } - John

2
我发现一种解决方法如下:
只发布 TypeScript,因为它更易读:
 public DisplayDate: string = new Date(Date.parse(this.dto.TxDate)).toLocaleDateString();
    public get TxDate(): Date {

        let txDate = new Date(Date.parse(this.dto.TxDate));
        if (this._txDate === null && this._txDate != txDate)
            this._txDate = txDate;
        return this._txDate;
    }
    public set TxDate(value: Date) {
        this.dto.TxDate = value.toISOString();
        this._txDate = value;
        this.DisplayDate = this._txDate.toLocaleDateString();
    }
    private _txDate: Date = null;

这似乎能够满足我的需求。显示日期绑定到可见标签,使我可以得到所需的显示格式,而TxDate则绑定到隐藏的表单日期选择器控件中。这样一来,所有东西都能正常工作,同时我还可以将Dto日期保持在ISO8601格式中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接