telerik / kendo-angular

Issue tracker - Kendo UI for Angular
http://www.telerik.com/kendo-angular-ui/
Other
467 stars 216 forks source link

Change Detection Grid Messages #362

Closed simon11196 closed 6 years ago

simon11196 commented 7 years ago

Plunkr:

http://plnkr.co/edit/OYQzlEYW0fqUYUdhF1DQ?p=preview

Message in grid doesn't update on input change (button click).

tsvetomir commented 7 years ago

So far we assumed that the translated messages don't change dynamically. They're not rendered as templates and don't get their own change detectors.

The specific use case might be served better from an "no records" template.

Do you have other use cases where you'd like to change the translated messages on the fly? You might still be able to do that by using message service, but changes won't show up until the component is recreated.

pfoedermayr commented 7 years ago

@tsvetomir Actually we do have this use case, as we allow the user to switch the displayed language at 'run-time' (w/o having to reload the whole page).

We have implemented this with the help of ngx-translate.

The reason why we are using the MessageService instead of e.g. the no records template, is to reduce the redundancy in the markup. This way we do not have to add the same template to every grid.

Right now we are facing two problems with the interaction between kendo-angular-l10n and ngx-translate.

Problem 1 'asynchronous loading of translations'

Right now we do have to use the instant function to retrieve the translations (string) synchronous in the MessageService implementation. But this does have the drawback, that the translations are sometime not yet loaded (especially if the connection to the server is somewhat bad). That's why ngx-translate suggest to use the get function instead, which returns an Observable<string> that resolves when the translations are loaded.

code sample:

import { MessageService } from "@progress/kendo-angular-l10n";
import { TranslateService } from "@ngx-translate/core";

export class TranslateKendoService extends MessageService {

    constructor(private service: TranslateService)
    {
        super();
    }

    get(key: string) {
        //return this.service.get(key); // Returns Observable<string> so it does not work
        return this.service.instant(key);
    }
}

Problem 2 'redraw of kendoUI-components'

When the language is changed by the user, ngx-translate will request the needed translations if they are not available yet. But we do not have an option to trigger a refresh/redraw of the translations provided by the MessageService implementation. Right now we are experimenting with removing the kendoUI-component from the DOM via *ngIf for the blink of a second, but this does feel far from optimal.

Ideas / Possible solutions

Idea 1

Implement a EventEmitter/Subject which triggers the kendoUI-component to reload the translations via MessageService.get.

Sample
export class TranslateKendoService extends MessageService {

    constructor(private service: TranslateService)
    {
        super();
        this.service.onLangChange.subscribe(() => this.onLanguageChange());
    }

    get(key: string) {
        return this.service.instant(key);
    }

    private onLanguageChange() {
        // Call the 'Redraw/Refresh EventEmitter' here
    }
}

Idea 2

Allow MessageService.get to (optional) return Observable<string> instead of string and watch the changes to reflect it in the components.

export class TranslateKendoService extends MessageService {

    constructor(private service: TranslateService)
    {
        super();
    }

    get(key: string) {
        this.service.get(key); //returns Observable<string>
    }
}

Looking forward to hear your opinion on this. Also please let me know if there are any questions and/or something is unclear.

tsvetomir commented 7 years ago

Thank you for the clarification.

We experimented with something similar to the first idea, but we couldn't find an acceptable way to re-render a component based on an external trigger. The "Angular way" to address this would be to render the strings in the component template, thereby creating change detectors for them. This is not how it currently works mostly due to the interaction with the message service.

The second approach is very risky in terms of performance. The number of observables will scale linearly with the number of messages and component instances on the page. The design document for the Angular i18n framework touches on this concern:

Performance considerations: There are extra bindings introduced. There's typically an extra network requests before content can be shown or all the translations for all the languages are served on initial load. There's support to mitigate this but it requires a good amount of extra code.

We tend to agree with the Angular team here. Translating each and every text in the application over the network, with dynamic bindings is very likely to slow it down.

And finally, a question. Wouldn't you use the router to assign different URLs for the different languages? If so, you can inject a different message service for each route.

pfoedermayr commented 7 years ago

Thank you for the feedback.

I absolutely agree with you and the Angular team here. If, the way we are currently going, is not done in a proper way it will definitely slow down the application. In our case we are making heavy use of TypeScript's inheritance and Angular Provider's to adapt parts of 3rd party libraries based on our (kind of exotic) use-case.

And finally, a question. Wouldn't you use the router to assign different URLs for the different languages? If so, you can inject a different message service for each route.

This was initially discussed when we started, but we were not able to find a acceptable way to do this, if there is more than one language to be displayed on a page.

For components (like the grid for example) we are always showing the 'primary' language. And due to that there was the idea to load translations needed for kendoUI components via the MessageService and Resolve, but we did not have the time to look into that yet.

tsvetomir commented 7 years ago

You can still have different translations on the page by wrapping the Grid in a custom component. This wrapper can inject a message service configured for the required language.

Something in the line of this plunkr. Here LocalizedComponent will recreate its child component every time the language is changed.

@Component({
    selector: 'my-app',
    template: `
      <button (click)="changeLanguage()">Change Language</button>

      <localized-component [language]="lang" [contentType]="childType">
      </localized-component>
    `
})
export class AppComponent {
  public lang: string = 'en';
  public childType = MyGridComponent;

  public changeLanguage() {
    this.lang = this.lang === 'en' ? 'es' : 'en';
  }
}

Not ideal, but I'm not aware of any other means to force Angular to re-instantiate a component.

Edit (13/03/2018): Updated plunkr to current version

pfoedermayr commented 7 years ago

Thank you for the sample. That is actually really close to what we are doing right now.

balazs-zsoldos commented 6 years ago

What is the official solution of this closed issue?

tsvetomir commented 6 years ago

We're considering our options to make dynamic localization possible. One option is to have a change notification emitted from the message service, as @balazs-zsoldos suggested in the support system.

There are other considerations, such as RTL and Locale information that must be kept in sync with the language change. We'll post updates as our research progresses.

tsvetomir commented 6 years ago

Good news, everyone! The latest develop build adds a notify method for changing translations dynamically. This is currently supported in the Grid package and the rest of the components will follow soon.

Please note that this is a breaking change for the components due to updated peer dependencies. The major version has been incremented and they now require @progress/kendo-angular-l10n v1.1.0 or later. The Grid also requires @progress/kendo-angular-buttons v4.0.0 or later.

Beers go to @rkonstantinov, @rusev and @danielkaradachki :beers:

balazs-zsoldos commented 6 years ago

👍

tsvetomir commented 6 years ago

Released officially in @progress/kendo-angular-grid v3.0.0

MLefebvreICO commented 3 years ago

@tsvetomir Is there a list somewhere of what is supported or not for changing translations dynamically ?

I searched on your website and didn't found anything about it. We noticed on our end that the EditorComponent wouldn't refresh the languages when we call the notify method.. 😐

Will we have some more surprises like this ?

Thanks

tsvetomir commented 3 years ago

Hi @MLefebvreICO, that sounds like a bug with the Editor component. Would you mind filing a separate issue so we don't spam the participants in that fairly old thread? Thanks!