mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(datepicker): @Output for year and month selected in multiyear/year (#9678)
* feat(datepicker): @Output of year and month selected in multiyear/year views * address @mmalerba's comments * address comments * add types to some outputs * fix wrong parameter type * restore accidentally removed object * example and docs text * change emitted values to objects instead of just numbers * change datepicker.md and switch back tslint configs * fix nit * Remove changes in demo app
This commit is contained in:
Generated
+4
-4
@@ -11265,7 +11265,7 @@
|
||||
"q": "1.5.1",
|
||||
"sauce-connect-launcher": "1.2.3",
|
||||
"saucelabs": "1.4.0",
|
||||
"wd": "1.4.1"
|
||||
"wd": "1.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"q": {
|
||||
@@ -18067,9 +18067,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"wd": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/wd/-/wd-1.4.1.tgz",
|
||||
"integrity": "sha512-C0wWd2X4SWWcyx5qxaixiZE4Vb07sl0yDfWHPeml8lDHSbmI9erE9BmTHIqOGoDxGgJ3/hkFmODQ7ZLKiF8+8Q==",
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/wd/-/wd-1.5.0.tgz",
|
||||
"integrity": "sha512-e/KpzTlhtSG3Ek0AcRz4G6PhxGsc53Nro+GkI1er9p05tWQ7W9dpGZR5SqQzGUqvbaqJCThDSAGaY7LHgi6MiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"archiver": "1.3.0",
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<input matInput [matDatepicker]="datePicker1" [(ngModel)]="date" [min]="minDate" [max]="maxDate"
|
||||
[matDatepickerFilter]="filterOdd ? dateFilter : null" disabled>
|
||||
<mat-datepicker #datePicker1 [touchUi]="touch" [startAt]="startAt"
|
||||
[startView]="yearView ? 'year' : 'month'"></mat-datepicker>
|
||||
[startView]="yearView ? 'year' : 'month'"></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
<input matInput disabled [matDatepicker]="datePicker3" [(ngModel)]="date" [min]="minDate"
|
||||
[max]="maxDate" [matDatepickerFilter]="filterOdd ? dateFilter : null">
|
||||
<mat-datepicker #datePicker3 [touchUi]="touch" [disabled]="false" [startAt]="startAt"
|
||||
[startView]="yearView ? 'year' : 'month'"></mat-datepicker>
|
||||
[startView]="yearView ? 'year' : 'month'"></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
mat-calendar {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
import {ChangeDetectionStrategy, Component} from '@angular/core';
|
||||
import {FormControl} from '@angular/forms';
|
||||
import {MatDatepickerInputEvent} from '@angular/material/datepicker';
|
||||
|
||||
import {MatDatepickerInputEvent} from '@angular/material';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
@@ -31,11 +30,11 @@ export class DatepickerDemo {
|
||||
lastDateInput: Date | null;
|
||||
lastDateChange: Date | null;
|
||||
|
||||
dateCtrl = new FormControl();
|
||||
|
||||
dateFilter =
|
||||
(date: Date) => !(date.getFullYear() % 2) && (date.getMonth() % 2) && !(date.getDate() % 2)
|
||||
|
||||
onDateInput = (e: MatDatepickerInputEvent<Date>) => this.lastDateInput = e.value;
|
||||
onDateChange = (e: MatDatepickerInputEvent<Date>) => this.lastDateChange = e.value;
|
||||
|
||||
dateCtrl = new FormControl();
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import {AccessibilityDemoModule} from './a11y/a11y-module';
|
||||
],
|
||||
entryComponents: [
|
||||
EntryApp,
|
||||
],
|
||||
]
|
||||
})
|
||||
export class DemoAppModule {
|
||||
constructor(private _appRef: ApplicationRef) { }
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
[activeDate]="_activeDate"
|
||||
[selected]="selected"
|
||||
[dateFilter]="_dateFilterForViews"
|
||||
(monthSelected)="_monthSelectedInYearView($event)"
|
||||
(selectedChange)="_goToDateInView($event, 'month')">
|
||||
</mat-year-view>
|
||||
|
||||
@@ -44,6 +45,7 @@
|
||||
[activeDate]="_activeDate"
|
||||
[selected]="selected"
|
||||
[dateFilter]="_dateFilterForViews"
|
||||
(yearSelected)="_yearSelectedInMultiYearView($event)"
|
||||
(selectedChange)="_goToDateInView($event, 'year')">
|
||||
</mat-multi-year-view>
|
||||
</div>
|
||||
|
||||
@@ -196,6 +196,40 @@ describe('MatCalendar', () => {
|
||||
expect(testComponent.selected).toEqual(new Date(2017, JAN, 31));
|
||||
});
|
||||
|
||||
it('should emit the selected month on cell clicked in year view', () => {
|
||||
periodButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(calendarInstance._currentView).toBe('multi-year');
|
||||
expect(calendarInstance._activeDate).toEqual(new Date(2017, JAN, 31));
|
||||
|
||||
(calendarElement.querySelector('.mat-calendar-body-active') as HTMLElement).click();
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(calendarInstance._currentView).toBe('year');
|
||||
|
||||
(calendarElement.querySelector('.mat-calendar-body-active') as HTMLElement).click();
|
||||
|
||||
const normalizedMonth: Date = fixture.componentInstance.selectedMonth;
|
||||
expect(normalizedMonth.getMonth()).toEqual(0);
|
||||
});
|
||||
|
||||
it('should emit the selected year on cell clicked in multiyear view', () => {
|
||||
periodButton.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(calendarInstance._currentView).toBe('multi-year');
|
||||
expect(calendarInstance._activeDate).toEqual(new Date(2017, JAN, 31));
|
||||
|
||||
(calendarElement.querySelector('.mat-calendar-body-active') as HTMLElement).click();
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const normalizedYear: Date = fixture.componentInstance.selectedYear;
|
||||
expect(normalizedYear.getFullYear()).toEqual(2017);
|
||||
});
|
||||
|
||||
it('should re-render when the i18n labels have changed',
|
||||
inject([MatDatepickerIntl], (intl: MatDatepickerIntl) => {
|
||||
const button = fixture.debugElement.nativeElement
|
||||
@@ -916,10 +950,18 @@ describe('MatCalendar', () => {
|
||||
|
||||
|
||||
@Component({
|
||||
template: `<mat-calendar [startAt]="startDate" [(selected)]="selected"></mat-calendar>`
|
||||
template: `
|
||||
<mat-calendar
|
||||
[startAt]="startDate"
|
||||
[(selected)]="selected"
|
||||
(yearSelected)="selectedYear=$event"
|
||||
(monthSelected)="selectedMonth=$event">
|
||||
</mat-calendar>`
|
||||
})
|
||||
class StandardCalendar {
|
||||
selected: Date;
|
||||
selectedYear: Date;
|
||||
selectedMonth: Date;
|
||||
startDate = new Date(2017, JAN, 31);
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,18 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges {
|
||||
/** Emits when the currently selected date changes. */
|
||||
@Output() readonly selectedChange: EventEmitter<D> = new EventEmitter<D>();
|
||||
|
||||
/**
|
||||
* Emits the year chosen in multiyear view.
|
||||
* This doesn't imply a change on the selected date.
|
||||
*/
|
||||
@Output() readonly yearSelected: EventEmitter<D> = new EventEmitter<D>();
|
||||
|
||||
/**
|
||||
* Emits the month chosen in year view.
|
||||
* This doesn't imply a change on the selected date.
|
||||
*/
|
||||
@Output() readonly monthSelected: EventEmitter<D> = new EventEmitter<D>();
|
||||
|
||||
/** Emits when any date is selected. */
|
||||
@Output() readonly _userSelection: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
@@ -228,6 +240,16 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles year selection in the multiyear view. */
|
||||
_yearSelectedInMultiYearView(normalizedYear: D) {
|
||||
this.yearSelected.emit(normalizedYear);
|
||||
}
|
||||
|
||||
/** Handles month selection in the year view. */
|
||||
_monthSelectedInYearView(normalizedMonth: D) {
|
||||
this.monthSelected.emit(normalizedMonth);
|
||||
}
|
||||
|
||||
_userSelected(): void {
|
||||
this._userSelection.emit();
|
||||
}
|
||||
|
||||
@@ -8,5 +8,7 @@
|
||||
[dateFilter]="datepicker._dateFilter"
|
||||
[selected]="datepicker._selected"
|
||||
(selectedChange)="datepicker._select($event)"
|
||||
(yearSelected)="datepicker._selectYear($event)"
|
||||
(monthSelected)="datepicker._selectMonth($event)"
|
||||
(_userSelection)="datepicker.close()">
|
||||
</mat-calendar>
|
||||
|
||||
@@ -51,6 +51,31 @@ year containing the `startAt` date.
|
||||
|
||||
<!-- example(datepicker-start-view) -->
|
||||
|
||||
#### Watching the views for changes on selected years and months
|
||||
|
||||
When a year or a month is selected in `multi-year` and `year` views respecively, the `yearSelected`
|
||||
and `monthSelected` outputs emit a normalized date representing the chosen year or month. By
|
||||
"normalized" we mean that the dates representing a year will have their month set to January and
|
||||
their day set to the 1st. Dates representing months will have their day set to the 1st of the
|
||||
month. For example, if `<mat-datepicker>` is configured to work with javascript native Date
|
||||
objects, the `yearSelected` will emit `new Date(2017, 0, 1)` if the user selects 2017 in
|
||||
`multi-year` view. Similarly, `monthSelected` will emit `new Date(2017, 1, 0)` if the user
|
||||
selects **February** in `year` view and the current date value of the connected `<input>` was
|
||||
something like `new Date(2017, MM, dd)` (the month and day are irrelevant in this case).
|
||||
|
||||
Notice that the emitted value does not affect the current value in the connected `<input>`, which
|
||||
is only bound to the selection made in the `month` view. So if the end user closes the calendar
|
||||
after choosing a year in `multi-view` mode (by pressing the `ESC` key, for example), the selected
|
||||
year, emitted by `yearSelected` output, will not cause any change in the value of the date in the
|
||||
associated `<input>`.
|
||||
|
||||
The following example uses `yearSelected` and `monthSelected` outputs to emulate a month and year
|
||||
picker (if you're not familiar with the usage of `MomentDateAdapter` and `MAT_DATE_FORMATS`
|
||||
you can [read more about them](#choosing-a-date-implementation-and-date-format-settings) below in
|
||||
this document to fully understand the example).
|
||||
|
||||
<!-- example(datepicker-views-selection) -->
|
||||
|
||||
### Setting the selected date
|
||||
|
||||
The type of values that the datepicker expects depends on the type of `DateAdapter` provided in your
|
||||
|
||||
@@ -433,12 +433,12 @@ describe('MatDatepicker', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('datepicker with startView', () => {
|
||||
let fixture: ComponentFixture<DatepickerWithStartView>;
|
||||
let testComponent: DatepickerWithStartView;
|
||||
describe('datepicker with startView set to year', () => {
|
||||
let fixture: ComponentFixture<DatepickerWithStartViewYear>;
|
||||
let testComponent: DatepickerWithStartViewYear;
|
||||
|
||||
beforeEach(fakeAsync(() => {
|
||||
fixture = createComponent(DatepickerWithStartView, [MatNativeDateModule]);
|
||||
fixture = createComponent(DatepickerWithStartViewYear, [MatNativeDateModule]);
|
||||
fixture.detectChanges();
|
||||
|
||||
testComponent = fixture.componentInstance;
|
||||
@@ -461,6 +461,73 @@ describe('MatDatepicker', () => {
|
||||
expect(firstCalendarCell.textContent)
|
||||
.toBe('JAN', 'Expected the calendar to be in year-view');
|
||||
});
|
||||
|
||||
it('should fire yearSelected when user selects calendar year in year view',
|
||||
fakeAsync(() => {
|
||||
spyOn(testComponent, 'onYearSelection');
|
||||
expect(testComponent.onYearSelection).not.toHaveBeenCalled();
|
||||
|
||||
testComponent.datepicker.open();
|
||||
fixture.detectChanges();
|
||||
|
||||
const cells = document.querySelectorAll('.mat-calendar-body-cell');
|
||||
|
||||
dispatchMouseEvent(cells[0], 'click');
|
||||
fixture.detectChanges();
|
||||
flush();
|
||||
|
||||
expect(testComponent.onYearSelection).toHaveBeenCalled();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('datepicker with startView set to multiyear', () => {
|
||||
let fixture: ComponentFixture<DatepickerWithStartViewMultiYear>;
|
||||
let testComponent: DatepickerWithStartViewMultiYear;
|
||||
|
||||
beforeEach(fakeAsync(() => {
|
||||
fixture = createComponent(DatepickerWithStartViewMultiYear, [MatNativeDateModule]);
|
||||
fixture.detectChanges();
|
||||
|
||||
testComponent = fixture.componentInstance;
|
||||
|
||||
spyOn(testComponent, 'onMultiYearSelection');
|
||||
}));
|
||||
|
||||
afterEach(fakeAsync(() => {
|
||||
testComponent.datepicker.close();
|
||||
fixture.detectChanges();
|
||||
flush();
|
||||
}));
|
||||
|
||||
it('should start at the specified view', () => {
|
||||
testComponent.datepicker.open();
|
||||
fixture.detectChanges();
|
||||
|
||||
const firstCalendarCell = document.querySelector('.mat-calendar-body-cell')!;
|
||||
|
||||
// When the calendar is in year view, the first cell should be for a month rather than
|
||||
// for a date.
|
||||
expect(firstCalendarCell.textContent)
|
||||
.toBe('2016', 'Expected the calendar to be in multi-year-view');
|
||||
});
|
||||
|
||||
it('should fire yearSelected when user selects calendar year in multiyear view',
|
||||
fakeAsync(() => {
|
||||
expect(testComponent.onMultiYearSelection).not.toHaveBeenCalled();
|
||||
|
||||
testComponent.datepicker.open();
|
||||
fixture.detectChanges();
|
||||
|
||||
const cells = document.querySelectorAll('.mat-calendar-body-cell');
|
||||
|
||||
dispatchMouseEvent(cells[0], 'click');
|
||||
fixture.detectChanges();
|
||||
flush();
|
||||
|
||||
expect(testComponent.onMultiYearSelection).toHaveBeenCalled();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('datepicker with ngModel', () => {
|
||||
@@ -996,7 +1063,8 @@ describe('MatDatepicker', () => {
|
||||
expect(testComponent.onDateChange).toHaveBeenCalled();
|
||||
expect(testComponent.onInput).not.toHaveBeenCalled();
|
||||
expect(testComponent.onDateInput).toHaveBeenCalled();
|
||||
}));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('with ISO 8601 strings as input', () => {
|
||||
@@ -1264,12 +1332,29 @@ class DatepickerWithStartAt {
|
||||
@Component({
|
||||
template: `
|
||||
<input [matDatepicker]="d" [value]="date">
|
||||
<mat-datepicker #d startView="year"></mat-datepicker>
|
||||
<mat-datepicker #d startView="year" (monthSelected)="onYearSelection()"></mat-datepicker>
|
||||
`,
|
||||
})
|
||||
class DatepickerWithStartView {
|
||||
class DatepickerWithStartViewYear {
|
||||
date = new Date(2020, JAN, 1);
|
||||
@ViewChild('d') datepicker: MatDatepicker<Date>;
|
||||
|
||||
onYearSelection() {}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<input [matDatepicker]="d" [value]="date">
|
||||
<mat-datepicker #d startView="multi-year"
|
||||
(yearSelected)="onMultiYearSelection()"></mat-datepicker>
|
||||
`,
|
||||
})
|
||||
class DatepickerWithStartViewMultiYear {
|
||||
date = new Date(2020, JAN, 1);
|
||||
@ViewChild('d') datepicker: MatDatepicker<Date>;
|
||||
|
||||
onMultiYearSelection() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -1314,6 +1399,7 @@ class DatepickerWithToggle {
|
||||
touchUI = true;
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<input [matDatepicker]="d">
|
||||
@@ -1325,6 +1411,7 @@ class DatepickerWithToggle {
|
||||
})
|
||||
class DatepickerWithCustomIcon {}
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<mat-form-field>
|
||||
@@ -1387,6 +1474,7 @@ class DatepickerWithChangeAndInputEvents {
|
||||
onDateInput() {}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<input [matDatepicker]="d" [(ngModel)]="date">
|
||||
@@ -1399,6 +1487,7 @@ class DatepickerWithi18n {
|
||||
@ViewChild(MatDatepickerInput) datepickerInput: MatDatepickerInput<Date>;
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<input [matDatepicker]="d" [(ngModel)]="value" [min]="min" [max]="max">
|
||||
@@ -1414,6 +1503,7 @@ class DatepickerWithISOStrings {
|
||||
@ViewChild(MatDatepickerInput) datepickerInput: MatDatepickerInput<Date>;
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<input [(ngModel)]="selected" [matDatepicker]="d">
|
||||
|
||||
@@ -164,6 +164,18 @@ export class MatDatepicker<D> implements OnDestroy {
|
||||
*/
|
||||
@Output() readonly selectedChanged: EventEmitter<D> = new EventEmitter<D>();
|
||||
|
||||
/**
|
||||
* Emits selected year in multiyear view.
|
||||
* This doesn't imply a change on the selected date.
|
||||
*/
|
||||
@Output() readonly yearSelected: EventEmitter<D> = new EventEmitter<D>();
|
||||
|
||||
/**
|
||||
* Emits selected month in year view.
|
||||
* This doesn't imply a change on the selected date.
|
||||
*/
|
||||
@Output() readonly monthSelected: EventEmitter<D> = new EventEmitter<D>();
|
||||
|
||||
/** Classes to be passed to the date picker panel. Supports the same syntax as `ngClass`. */
|
||||
@Input() panelClass: string | string[];
|
||||
|
||||
@@ -173,6 +185,7 @@ export class MatDatepicker<D> implements OnDestroy {
|
||||
/** Emits when the datepicker has been closed. */
|
||||
@Output('closed') closedStream: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
|
||||
/** Whether the calendar is open. */
|
||||
@Input()
|
||||
get opened(): boolean { return this._opened; }
|
||||
@@ -253,6 +266,16 @@ export class MatDatepicker<D> implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
/** Emits the selected year in multiyear view */
|
||||
_selectYear(normalizedYear: D): void {
|
||||
this.yearSelected.emit(normalizedYear);
|
||||
}
|
||||
|
||||
/** Emits selected month in year view */
|
||||
_selectMonth(normalizedMonth: D): void {
|
||||
this.monthSelected.emit(normalizedMonth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an input with this datepicker.
|
||||
* @param input The datepicker input to register with this datepicker.
|
||||
|
||||
@@ -66,6 +66,16 @@ describe('MatMultiYearView', () => {
|
||||
expect(selectedEl.innerHTML.trim()).toBe('2039');
|
||||
});
|
||||
|
||||
it('should emit the selected year on cell clicked', () => {
|
||||
let cellEls = multiYearViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
|
||||
|
||||
(cellEls[1] as HTMLElement).click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const normalizedYear: Date = fixture.componentInstance.selectedYear;
|
||||
expect(normalizedYear.getFullYear()).toEqual(2017);
|
||||
});
|
||||
|
||||
it('should mark active date', () => {
|
||||
let cellEls = multiYearViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
|
||||
expect((cellEls[1] as HTMLElement).innerText.trim()).toBe('2017');
|
||||
@@ -98,11 +108,13 @@ describe('MatMultiYearView', () => {
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<mat-multi-year-view [activeDate]="date" [(selected)]="selected"></mat-multi-year-view>`,
|
||||
<mat-multi-year-view [activeDate]="date" [(selected)]="selected"
|
||||
(yearSelected)="selectedYear=$event"></mat-multi-year-view>`
|
||||
})
|
||||
class StandardMultiYearView {
|
||||
date = new Date(2017, JAN, 1);
|
||||
selected = new Date(2020, JAN, 1);
|
||||
selectedYear: Date;
|
||||
|
||||
@ViewChild(MatYearView) yearView: MatYearView<Date>;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,9 @@ export class MatMultiYearView<D> implements AfterContentInit {
|
||||
/** Emits when a new month is selected. */
|
||||
@Output() readonly selectedChange: EventEmitter<D> = new EventEmitter<D>();
|
||||
|
||||
/** Emits the selected year. This doesn't imply a change on the selected date */
|
||||
@Output() readonly yearSelected: EventEmitter<D> = new EventEmitter<D>();
|
||||
|
||||
/** Grid of calendar cells representing the currently displayed years. */
|
||||
_years: MatCalendarCell[][];
|
||||
|
||||
@@ -110,6 +113,7 @@ export class MatMultiYearView<D> implements AfterContentInit {
|
||||
|
||||
/** Handles when a new year is selected. */
|
||||
_yearSelected(year: number) {
|
||||
this.yearSelected.emit(this._dateAdapter.createDate(year, 0, 1));
|
||||
let month = this._dateAdapter.getMonth(this.activeDate);
|
||||
let daysInMonth =
|
||||
this._dateAdapter.getNumDaysInMonth(this._dateAdapter.createDate(year, month, 1));
|
||||
|
||||
@@ -71,6 +71,16 @@ describe('MatYearView', () => {
|
||||
expect(selectedEl.innerHTML.trim()).toBe('DEC');
|
||||
});
|
||||
|
||||
it('should emit the selected month on cell clicked', () => {
|
||||
let cellEls = yearViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
|
||||
|
||||
(cellEls[cellEls.length - 1] as HTMLElement).click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const normalizedMonth: Date = fixture.componentInstance.selectedMonth;
|
||||
expect(normalizedMonth.getMonth()).toEqual(11);
|
||||
});
|
||||
|
||||
it('should mark active date', () => {
|
||||
let cellEls = yearViewNativeElement.querySelectorAll('.mat-calendar-body-cell');
|
||||
expect((cellEls[0] as HTMLElement).innerText.trim()).toBe('JAN');
|
||||
@@ -113,11 +123,13 @@ describe('MatYearView', () => {
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<mat-year-view [activeDate]="date" [(selected)]="selected"></mat-year-view>`,
|
||||
<mat-year-view [activeDate]="date" [(selected)]="selected"
|
||||
(monthSelected)="selectedMonth=$event"></mat-year-view>`
|
||||
})
|
||||
class StandardYearView {
|
||||
date = new Date(2017, JAN, 5);
|
||||
selected = new Date(2017, MAR, 10);
|
||||
selectedMonth: Date;
|
||||
|
||||
@ViewChild(MatYearView) yearView: MatYearView<Date>;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,9 @@ export class MatYearView<D> implements AfterContentInit {
|
||||
/** Emits when a new month is selected. */
|
||||
@Output() readonly selectedChange: EventEmitter<D> = new EventEmitter<D>();
|
||||
|
||||
/** Emits the selected month. This doesn't imply a change on the selected date */
|
||||
@Output() readonly monthSelected: EventEmitter<D> = new EventEmitter<D>();
|
||||
|
||||
/** Grid of calendar cells representing the months of the year. */
|
||||
_months: MatCalendarCell[][];
|
||||
|
||||
@@ -99,8 +102,13 @@ export class MatYearView<D> implements AfterContentInit {
|
||||
|
||||
/** Handles when a new month is selected. */
|
||||
_monthSelected(month: number) {
|
||||
let daysInMonth = this._dateAdapter.getNumDaysInMonth(
|
||||
this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), month, 1));
|
||||
const normalizedDate =
|
||||
this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), month, 1);
|
||||
|
||||
this.monthSelected.emit(normalizedDate);
|
||||
|
||||
const daysInMonth = this._dateAdapter.getNumDaysInMonth(normalizedDate);
|
||||
|
||||
this.selectedChange.emit(this._dateAdapter.createDate(
|
||||
this._dateAdapter.getYear(this.activeDate), month,
|
||||
Math.min(this._dateAdapter.getDate(this.activeDate), daysInMonth)));
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
/** No CSS for this example */
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<mat-form-field>
|
||||
<input matInput [matDatepicker]="dp" placeholder="Month and Year" [formControl]="date">
|
||||
<mat-datepicker-toggle matSuffix [for]="dp"></mat-datepicker-toggle>
|
||||
<mat-datepicker #dp (yearSelected)="chosenYearHandler($event)"
|
||||
(monthSelected)="chosenMonthHandler($event, dp)"></mat-datepicker>
|
||||
</mat-form-field>
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {FormControl} from '@angular/forms';
|
||||
import {MatDatepicker} from '@angular/material/datepicker';
|
||||
import {MomentDateAdapter} from '@angular/material-moment-adapter';
|
||||
import {DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE} from '@angular/material/core';
|
||||
|
||||
// Depending on whether rollup is used, moment needs to be imported differently.
|
||||
// Since Moment.js doesn't have a default export, we normally need to import using the `* as`
|
||||
// syntax. However, rollup creates a synthetic default module and we thus need to import it using
|
||||
// the `default as` syntax.
|
||||
import * as _moment from 'moment';
|
||||
import {default as _rollupMoment, Moment} from 'moment';
|
||||
const moment = _rollupMoment || _moment;
|
||||
|
||||
// See the Moment.js docs for the meaning of these formats:
|
||||
// https://momentjs.com/docs/#/displaying/format/
|
||||
export const MY_FORMATS = {
|
||||
parse: {
|
||||
dateInput: 'MM/YYYY',
|
||||
},
|
||||
display: {
|
||||
dateInput: 'MM/YYYY',
|
||||
monthYearLabel: 'MMM YYYY',
|
||||
dateA11yLabel: 'LL',
|
||||
monthYearA11yLabel: 'MMMM YYYY',
|
||||
},
|
||||
};
|
||||
|
||||
/** @title Datepicker emulating a Year and month picker */
|
||||
@Component({
|
||||
selector: 'datepicker-views-selection-example',
|
||||
templateUrl: 'datepicker-views-selection-example.html',
|
||||
styleUrls: ['datepicker-views-selection-example.css'],
|
||||
providers: [
|
||||
// `MomentDateAdapter` can be automatically provided by importing `MomentDateModule` in your
|
||||
// application's root module. We provide it at the component level here, due to limitations of
|
||||
// our example generation script.
|
||||
{provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE]},
|
||||
|
||||
{provide: MAT_DATE_FORMATS, useValue: MY_FORMATS},
|
||||
],
|
||||
})
|
||||
export class DatepickerViewsSelectionExample {
|
||||
date = new FormControl(moment());
|
||||
|
||||
chosenYearHandler(normalizedYear: Moment) {
|
||||
const ctrlValue = this.date.value;
|
||||
ctrlValue.year(normalizedYear.year());
|
||||
this.date.setValue(ctrlValue);
|
||||
}
|
||||
|
||||
chosenMonthHandler(normlizedMonth: Moment, datepicker: MatDatepicker<Moment>) {
|
||||
const ctrlValue = this.date.value;
|
||||
ctrlValue.month(normlizedMonth.month());
|
||||
this.date.setValue(ctrlValue);
|
||||
datepicker.close();
|
||||
}
|
||||
}
|
||||
@@ -30,21 +30,21 @@ import {
|
||||
MatInputModule,
|
||||
MatListModule,
|
||||
MatMenuModule,
|
||||
MatPaginatorModule,
|
||||
MatProgressBarModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatRadioModule,
|
||||
MatSelectModule,
|
||||
MatSidenavModule,
|
||||
MatSlideToggleModule,
|
||||
MatSliderModule,
|
||||
MatSidenavModule,
|
||||
MatSnackBarModule,
|
||||
MatSortModule,
|
||||
MatStepperModule,
|
||||
MatTableModule,
|
||||
MatTabsModule,
|
||||
MatToolbarModule,
|
||||
MatTooltipModule,
|
||||
MatPaginatorModule,
|
||||
MatSortModule,
|
||||
MatTableModule
|
||||
]
|
||||
})
|
||||
export class ExampleMaterialModule {}
|
||||
|
||||
Reference in New Issue
Block a user