ngx-translate / core

The internationalization (i18n) library for Angular
MIT License
4.53k stars 579 forks source link

Translate pipe used with a Date parameter is not triggered when the parameter changes #1156

Open lhude opened 4 years ago

lhude commented 4 years ago

Angular 8 ngx-translate/core: 11.0.1 ngx-translate-messageformat-compiler: 4.5.0

We are using ngx-translate-messageformat-compiler along with ngx-translate to support translation of Date with accurate date format according to the current lang (among other things). So we have some cases where we give just Date parameters to the translate pipe. The translate pipe works fine the first time it is triggered but when the value of one parameter changes, the pipe does not update the value displayed anymore.

Here's an exemple :

html

<div>{{ 'test' | translate:param }}</div>

json

{
  "test": "This is a date : {myDate, date, short}"
}

component

  public param = {myDate: new Date()};

  change(newDate: Date) {
    this.param = { myDate: newDate };
  }

As I said the date displayed in the html is not updated when the value of this.param changes as the method change is called with a different Date. I'm pretty sure it's because of the start of the transform method of TranslatePipe :

translate.pipe.ts

  transform(query: string, ...args: any[]): any {
    if (!query || query.length === 0) {
      return query;
    }

    // if we ask another time for the same key, return the last value
    if (equals(query, this.lastKey) && equals(args, this.lastParams)) {
      return this.value;
    }

The problem is the equal fonction (see here) which returns true for 2 different Date objects.

To force the pipe to see a difference between args and this.lastParams I can modify the code like this : component

  public param = {myDate: new Date(), myDateAsString: new Date().toISOString()};

  change(newDate: Date) {
    this.param = { myDate: newDate, myDateAsString: myDate.toISOString() };
  }

By doing this, the equals function see a difference between both param values (args and this.lastParams), because the comparison of the string values return false, and now the pipe works as expected. But this is an ugly workaround.

The equals function should return false for 2 different Date objects in the first place.

rettier commented 8 months ago

i can confirm this still exists in version 15.x.

The issue arises from the fact that the equals method compares Date objects as objects, and the keySet only exists of fp_incr. This is a function pointer which is the same for any two Date objects.

keySet = Object.create(null);
for (key in o1) { // -> ['fp_incr', undefined] 
  if (!equals(o1[key], o2[key])) {
    return false;
  }
  keySet[key] = true;
}