From 8df93594bf3fcf502697f1b1ae5790061335727f Mon Sep 17 00:00:00 2001 From: Wilson Zeng Date: Mon, 2 Jul 2018 22:14:52 +0800 Subject: [PATCH] fix(module:date-picker): support changing language at runtime (#1768) close #1717 --- .../date-picker/abstract-picker.component.ts | 45 +++++++++++++++---- .../date-picker/date-picker.component.spec.ts | 24 +++++++++- components/date-picker/demo/basic.ts | 11 +++++ 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/components/date-picker/abstract-picker.component.ts b/components/date-picker/abstract-picker.component.ts index d8e198a3f4f..f798798c56b 100644 --- a/components/date-picker/abstract-picker.component.ts +++ b/components/date-picker/abstract-picker.component.ts @@ -2,6 +2,7 @@ import { EventEmitter, Input, OnChanges, + OnDestroy, OnInit, Output, SimpleChanges, @@ -9,6 +10,8 @@ import { } from '@angular/core'; import { ControlValueAccessor } from '@angular/forms'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { InputBoolean } from '../core/util/convert'; import { NzDatePickerI18nInterface } from '../i18n/nz-i18n.interface'; import { NzI18nService } from '../i18n/nz-i18n.service'; @@ -20,7 +23,7 @@ const POPUP_STYLE_PATCH = { 'position': 'relative' }; // Aim to override antd's /** * The base picker for all common APIs */ -export abstract class AbstractPickerComponent implements OnInit, OnChanges, ControlValueAccessor { +export abstract class AbstractPickerComponent implements OnInit, OnChanges, OnDestroy, ControlValueAccessor { // --- Common API @Input() @InputBoolean() nzAllowClear: boolean = true; @Input() @InputBoolean() nzAutoFocus: boolean = false; @@ -52,28 +55,42 @@ export abstract class AbstractPickerComponent implements OnInit, OnChanges, Cont this.nzValue = this.isRange ? [] : null; } + protected destroyed$: Subject = new Subject(); + protected isCustomPlaceHolder: boolean = false; + constructor(protected i18n: NzI18nService) { } ngOnInit(): void { - // Default locale (NOTE: Place here to assign default value due to the i18n'locale may change before ngOnInit) + // Subscribe the every locale change if the nzLocale is not handled by user if (!this.nzLocale) { - this.nzLocale = this.i18n.getLocaleData('DatePicker', {}); + this.i18n.localeChange + .pipe(takeUntil(this.destroyed$)) + .subscribe(() => this.setLocale()); } // Default value this.initValue(); - - // Default placeholder - if (!this.nzPlaceHolder) { - this.nzPlaceHolder = this.isRange ? this.nzLocale.lang.rangePlaceholder : this.nzLocale.lang.placeholder; - } } ngOnChanges(changes: SimpleChanges): void { if (changes.nzPopupStyle) { // Always assign the popup style patch this.nzPopupStyle = this.nzPopupStyle ? { ...this.nzPopupStyle, ...POPUP_STYLE_PATCH } : POPUP_STYLE_PATCH; } + + // Mark as customized placeholder by user once nzPlaceHolder assigned at the first time + if (changes.nzPlaceHolder && changes.nzPlaceHolder.firstChange && typeof this.nzPlaceHolder !== 'undefined') { + this.isCustomPlaceHolder = true; + } + + if (changes.nzLocale) { // The nzLocale is currently handled by user + this.setDefaultPlaceHolder(); + } + } + + ngOnDestroy(): void { + this.destroyed$.next(); + this.destroyed$.complete(); } closeOverlay(): void { @@ -138,6 +155,18 @@ export abstract class AbstractPickerComponent implements OnInit, OnChanges, Cont // | Internal methods // ------------------------------------------------------------------------ + // Reload locale from i18n with side effects + private setLocale(): void { + this.nzLocale = this.i18n.getLocaleData('DatePicker', {}); + this.setDefaultPlaceHolder(); + } + + private setDefaultPlaceHolder(): void { + if (!this.isCustomPlaceHolder && this.nzLocale) { + this.nzPlaceHolder = this.isRange ? this.nzLocale.lang.rangePlaceholder : this.nzLocale.lang.placeholder; + } + } + private formatDate(date: CandyDate): string { return date ? this.i18n.formatDateCompatible(date.nativeDate, this.nzFormat) : ''; } diff --git a/components/date-picker/date-picker.component.spec.ts b/components/date-picker/date-picker.component.spec.ts index d9905f98ca2..024a091b840 100644 --- a/components/date-picker/date-picker.component.spec.ts +++ b/components/date-picker/date-picker.component.spec.ts @@ -10,6 +10,9 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import * as isSameDay from 'date-fns/is_same_day'; import { dispatchKeyboardEvent, dispatchMouseEvent } from '../core/testing'; +import en_US from '../i18n/languages/en_US'; +import { NzI18nModule } from '../i18n/nz-i18n.module'; +import { NzI18nService } from '../i18n/nz-i18n.service'; import { NzDatePickerModule } from './date-picker.module'; import { PickerResultSingle } from './standard-types'; @@ -21,10 +24,11 @@ describe('NzDatePickerComponent', () => { let debugElement: DebugElement; let overlayContainer: OverlayContainer; let overlayContainerElement: HTMLElement; + let i18nService: NzI18nService; beforeEach(fakeAsync(() => { TestBed.configureTestingModule({ - imports : [ FormsModule, NoopAnimationsModule, NzDatePickerModule ], + imports : [ FormsModule, NoopAnimationsModule, NzDatePickerModule, NzI18nModule ], providers : [], declarations: [ NzTestDatePickerComponent @@ -40,9 +44,10 @@ describe('NzDatePickerComponent', () => { debugElement = fixture.debugElement; }); - beforeEach(inject([ OverlayContainer ], (oc: OverlayContainer) => { + beforeEach(inject([ OverlayContainer, NzI18nService ], (oc: OverlayContainer, i18n: NzI18nService) => { overlayContainer = oc; overlayContainerElement = oc.getContainerElement(); + i18nService = i18n; })); afterEach(() => { @@ -67,6 +72,21 @@ describe('NzDatePickerComponent', () => { expect(getPickerContainer()).toBeNull(); })); + it('should support changing language at runtime', fakeAsync(() => { + fixture.detectChanges(); + expect(getPickerTrigger().placeholder).toBe('请选择日期'); + i18nService.setLocale(en_US); + fixture.detectChanges(); + expect(getPickerTrigger().placeholder).toBe('Select date'); + + dispatchMouseEvent(getPickerTrigger(), 'click'); + fixture.detectChanges(); + tick(500); + fixture.detectChanges(); + expect((queryFromOverlay('.ant-calendar-date-input-wrap input.ant-calendar-input') as HTMLInputElement).placeholder).toBe('Select date'); + expect(queryFromOverlay('.ant-calendar-table .ant-calendar-column-header-inner').textContent).toContain('Su'); + })); + /* Issue https://github.com/NG-ZORRO/ng-zorro-antd/issues/1539 */ it('should be openable after closed by "Escape" key', fakeAsync(() => { fixture.detectChanges(); diff --git a/components/date-picker/demo/basic.ts b/components/date-picker/demo/basic.ts index 7d546da0f5a..3e4625af7fe 100644 --- a/components/date-picker/demo/basic.ts +++ b/components/date-picker/demo/basic.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import * as getISOWeek from 'date-fns/get_iso_week'; +import { en_US, zh_CN, NzI18nService } from 'ng-zorro-antd'; @Component({ selector: 'nz-demo-date-picker-basic', @@ -11,6 +12,8 @@ import * as getISOWeek from 'date-fns/get_iso_week';
+
+ `, styles : [ ` nz-date-picker, nz-month-picker, nz-range-picker, nz-week-picker { @@ -22,6 +25,9 @@ import * as getISOWeek from 'date-fns/get_iso_week'; export class NzDemoDatePickerBasicComponent { date = null; // new Date(); dateRange = []; // [ new Date(), addDays(new Date(), 3) ]; + isEnglish = false; + + constructor(private i18n: NzI18nService) {} onChange(result: Date): void { console.log('onChange: ', result); @@ -30,4 +36,9 @@ export class NzDemoDatePickerBasicComponent { getWeek(result: Date): void { console.log('week: ', getISOWeek(result)); } + + changeLanguage(): void { + this.i18n.setLocale(this.isEnglish ? zh_CN : en_US); + this.isEnglish = !this.isEnglish; + } }