diff --git a/CHANGELOG.md b/CHANGELOG.md
index 483b26f85fb..088e9491748 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,21 @@ All notable changes for each version of this project will be documented in this
### Features
- `igx-circular-bar` and `igx-linear-bar` now feature an indeterminate input property. When this property is set to true the indicator will be continually growing and shrinking along the track.
- `IgxTimePickerComponent`: in addition to the current dialog interaction mode, now the user can select or edit a time value, using an editable masked input with a dropdown.
+- `IgxColumnComponent` now accepts its templates as input properties through the markup. This can reduce the amount of code one needs to write when applying a single template to multiple columns declaratively. The new exposed inputs are:
+ + `cellTemplate` - the template for the column cells
+ + `headerTemplate` - the template for the column header
+ + `cellEditorTemplate` - the template for the column cells when a cell is in edit mode
+ + ```html
+
+
+
+
+
+
+
+ {{ value }}
+
+ ```
## 7.1.1
### Bug Fixes
@@ -26,7 +41,7 @@ All notable changes for each version of this project will be documented in this
- Added a new `igxToolbarCustomContent` directive which can be used to mark an `ng-template` which provides a custom content for the IgxGrid's toolbar ([#2983](https://github.com/IgniteUI/igniteui-angular/issues/2983))
- Summary results are now calculated and displayed by default for each row group when 'Group By' feature is enabled.
- `clearSummaryCache()` and `recalculateSummaries()` methods are deprecated. The grid will clear the cache and recalculate the summaries automatically when needed.
- - `locale` property added. If not set, it returns browser's language. All child components will use it as locale.
+ - `locale` property added. Default value is `en`. All child components will use it as locale.
- **Breaking change** `IgxSummaryOperand.operate()` method is called with empty data in order to calculate the necessary height for the summary row. For custom summary operands, the method should always return an array of `IgxSummaryResult` with proper length.
- `IgxIconModule`:
- **Breaking change** `igxIconService` is now provided in root (providedIn: 'root') and `IgxIconModule.forRoot()` method is deprecated.
diff --git a/projects/igniteui-angular/src/lib/calendar/calendar.component.ts b/projects/igniteui-angular/src/lib/calendar/calendar.component.ts
index 7da94dfd4b1..8e9648a6297 100644
--- a/projects/igniteui-angular/src/lib/calendar/calendar.component.ts
+++ b/projects/igniteui-angular/src/lib/calendar/calendar.component.ts
@@ -142,7 +142,7 @@ export class IgxCalendarComponent implements OnInit, ControlValueAccessor {
/**
* Gets the `locale` of the calendar.
- * By default the browser's language is used.
+ * Default value is `"en"`.
* ```typescript
* let locale = this.calendar.locale;
* ```
@@ -156,7 +156,7 @@ export class IgxCalendarComponent implements OnInit, ControlValueAccessor {
/**
* Sets the `locale` of the calendar.
* Expects a valid BCP 47 language tag.
- * By default the browser's language is used.
+ * Default value is `"en"`.
* ```html
*
* ```
@@ -613,7 +613,7 @@ export class IgxCalendarComponent implements OnInit, ControlValueAccessor {
/**
*@hidden
*/
- private _locale = window.navigator.language;
+ private _locale = 'en';
/**
*@hidden
*/
diff --git a/projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts b/projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts
index 49140321c0f..72421c1da19 100644
--- a/projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts
+++ b/projects/igniteui-angular/src/lib/date-picker/date-picker.component.ts
@@ -105,12 +105,12 @@ export class IgxDatePickerComponent implements IgxDatePickerBase, ControlValueAc
public labelVisibility = true;
/**
- *An @Input property that sets locales. By default the browser's language is used.
+ *An @Input property that sets locales. Default locale is en.
*```html
*
*```
*/
- @Input() public locale: string = window.navigator.language;
+ @Input() public locale: 'en';
/**
*An @Input property that sets on which day the week starts.
diff --git a/projects/igniteui-angular/src/lib/directives/input/input.directive.spec.ts b/projects/igniteui-angular/src/lib/directives/input/input.directive.spec.ts
index fc3d62b26c4..399cede3c4a 100644
--- a/projects/igniteui-angular/src/lib/directives/input/input.directive.spec.ts
+++ b/projects/igniteui-angular/src/lib/directives/input/input.directive.spec.ts
@@ -193,6 +193,37 @@ describe('IgxInput', () => {
testRequiredValidation(inputElement, fixture);
});
+ it('Should update style when required input\'s value is set.', () => {
+ const fixture = TestBed.createComponent(RequiredInputComponent);
+ fixture.detectChanges();
+
+ const igxInput = fixture.componentInstance.igxInput;
+ const inputElement = fixture.debugElement.query(By.directive(IgxInputDirective)).nativeElement;
+
+ dispatchInputEvent('focus', inputElement, fixture);
+ dispatchInputEvent('blur', inputElement, fixture);
+
+ const inputGroupElement = fixture.debugElement.query(By.css('igx-input-group')).nativeElement;
+ expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(true);
+ expect(igxInput.valid).toBe(IgxInputState.INVALID);
+
+ igxInput.value = 'test';
+ fixture.detectChanges();
+
+ expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(false);
+ expect(igxInput.valid).toBe(IgxInputState.VALID);
+
+
+ igxInput.value = '';
+ fixture.detectChanges();
+
+ dispatchInputEvent('focus', inputElement, fixture);
+ dispatchInputEvent('blur', inputElement, fixture);
+
+ expect(inputGroupElement.classList.contains(INPUT_GROUP_INVALID_CSS_CLASS)).toBe(true);
+ expect(igxInput.valid).toBe(IgxInputState.INVALID);
+ });
+
it('Should style required input with two-way databinding correctly.', () => {
const fixture = TestBed.createComponent(RequiredTwoWayDataBoundInputComponent);
fixture.detectChanges();
diff --git a/projects/igniteui-angular/src/lib/directives/input/input.directive.ts b/projects/igniteui-angular/src/lib/directives/input/input.directive.ts
index e522ffe60cd..9a8dcb5b5e5 100644
--- a/projects/igniteui-angular/src/lib/directives/input/input.directive.ts
+++ b/projects/igniteui-angular/src/lib/directives/input/input.directive.ts
@@ -52,6 +52,7 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy {
@Input('value')
set value(value: any) {
this.nativeElement.value = value;
+ this.checkValidity();
}
/**
* Gets the `value` propery.
@@ -144,9 +145,7 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy {
*/
@HostListener('input')
public onInput() {
- if (!this.ngControl && this._hasValidators()) {
- this._valid = this.nativeElement.checkValidity() ? IgxInputState.VALID : IgxInputState.INVALID;
- }
+ this.checkValidity();
}
/**
*@hidden
@@ -294,4 +293,10 @@ export class IgxInputDirective implements AfterViewInit, OnDestroy {
public set valid(value: IgxInputState) {
this._valid = value;
}
+
+ private checkValidity() {
+ if (!this.ngControl && this._hasValidators) {
+ this._valid = this.nativeElement.checkValidity() ? IgxInputState.VALID : IgxInputState.INVALID;
+ }
+ }
}
diff --git a/projects/igniteui-angular/src/lib/grids/README.md b/projects/igniteui-angular/src/lib/grids/README.md
index 93f3e07a04a..f57425913c8 100644
--- a/projects/igniteui-angular/src/lib/grids/README.md
+++ b/projects/igniteui-angular/src/lib/grids/README.md
@@ -186,7 +186,7 @@ Below is the list of all inputs that the developers may set to configure the gri
|`transactions`| `TransactionService` | Transaction provider allowing access to all transactions and states of the modified rows. |
|`summaryPosition`| GridSummaryPosition | The summary row position for the child levels. The default is top. |
|`summaryCalculationMode`| GridSummaryCalculationMode | The summary calculation mode. The default is rootAndChildLevels, which means summaries are calculated for root and child levels. |
-|`locale`| string | Determines the locale of the grid. By default returns browser's language. |
+|`locale`| string | Determines the locale of the grid. Default value is `en`. |
### Outputs
diff --git a/projects/igniteui-angular/src/lib/grids/column.component.ts b/projects/igniteui-angular/src/lib/grids/column.component.ts
index 0097d33553c..b780f402926 100644
--- a/projects/igniteui-angular/src/lib/grids/column.component.ts
+++ b/projects/igniteui-angular/src/lib/grids/column.component.ts
@@ -579,6 +579,7 @@ export class IgxColumnComponent implements AfterContentInit {
* ```
* @memberof IgxColumnComponent
*/
+ @Input('cellTemplate')
get bodyTemplate(): TemplateRef {
return this._bodyTemplate;
}
@@ -600,7 +601,9 @@ export class IgxColumnComponent implements AfterContentInit {
*/
set bodyTemplate(template: TemplateRef) {
this._bodyTemplate = template;
- this.grid.markForCheck();
+ if (this.grid) {
+ this.grid.cdr.markForCheck();
+ }
}
/**
* Returns a reference to the header template.
@@ -609,6 +612,7 @@ export class IgxColumnComponent implements AfterContentInit {
* ```
* @memberof IgxColumnComponent
*/
+ @Input()
get headerTemplate(): TemplateRef {
return this._headerTemplate;
}
@@ -630,7 +634,9 @@ export class IgxColumnComponent implements AfterContentInit {
*/
set headerTemplate(template: TemplateRef) {
this._headerTemplate = template;
- this.grid.markForCheck();
+ if (this.grid) {
+ this.grid.cdr.markForCheck();
+ }
}
/**
* Returns a reference to the inline editor template.
@@ -639,6 +645,7 @@ export class IgxColumnComponent implements AfterContentInit {
* ```
* @memberof IgxColumnComponent
*/
+ @Input('cellEditorTemplate')
get inlineEditorTemplate(): TemplateRef {
return this._inlineEditorTemplate;
}
@@ -658,7 +665,9 @@ export class IgxColumnComponent implements AfterContentInit {
*/
set inlineEditorTemplate(template: TemplateRef) {
this._inlineEditorTemplate = template;
- this.grid.markForCheck();
+ if (this.grid) {
+ this.grid.cdr.markForCheck();
+ }
}
/**
* Gets the cells of the column.
diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.component.ts b/projects/igniteui-angular/src/lib/grids/grid-base.component.ts
index 9c2ba27092a..9a00914542d 100644
--- a/projects/igniteui-angular/src/lib/grids/grid-base.component.ts
+++ b/projects/igniteui-angular/src/lib/grids/grid-base.component.ts
@@ -302,7 +302,7 @@ export abstract class IgxGridBaseComponent extends DisplayDensityBase implements
if (this._locale) {
return this._locale;
} else {
- return window.navigator.language;
+ return 'en';
}
}
diff --git a/projects/igniteui-angular/src/lib/grids/grid/column.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/column.spec.ts
index 42c22b7d921..5a754219d23 100644
--- a/projects/igniteui-angular/src/lib/grids/grid/column.spec.ts
+++ b/projects/igniteui-angular/src/lib/grids/grid/column.spec.ts
@@ -20,6 +20,7 @@ describe('IgxGrid - Column properties', () => {
declarations: [
ColumnsFromIterableComponent,
TemplatedColumnsComponent,
+ TemplatedInputColumnsComponent,
ColumnCellFormatterComponent,
ColumnHaederClassesComponent,
ColumnHiddenFromMarkupComponent
@@ -247,6 +248,26 @@ describe('IgxGrid - Column properties', () => {
expect(grid.columns[0].width).toBe('300');
expect(grid.columns[1].width).toBe('300');
});
+
+ it('should support passing templates through the markup as an input property', () => {
+ const fixture = TestBed.createComponent(TemplatedInputColumnsComponent);
+ fixture.detectChanges();
+
+ const grid = fixture.componentInstance.instance;
+
+ grid.getColumnByName('Name').cells.forEach(c =>
+ expect(c.nativeElement.querySelector('.customCellTemplate')).toBeDefined());
+
+ grid.headerCellList.forEach(header =>
+ expect(header.elementRef.nativeElement.querySelector('.customHeaderTemplate')).toBeDefined());
+
+ const cell = grid.getCellByColumn(0, 'ID');
+ cell.inEditMode = true;
+ fixture.detectChanges();
+
+ expect(cell.nativeElement.querySelector('.customEditorTemplate')).toBeDefined();
+
+ });
});
@Component({
@@ -284,6 +305,37 @@ export class TemplatedColumnsComponent {
public newCellTemplate: TemplateRef;
}
+@Component({
+ template: `
+
+
+
+
+
+
+ {{ value }}
+
+
+
+ {{ column.field }}
+
+
+
+ {{ value }}
+
+ `
+})
+export class TemplatedInputColumnsComponent {
+
+ data = SampleTestData.personIDNameRegionData();
+ columns = Object.keys(this.data[0]);
+
+ @ViewChild(IgxGridComponent, { read: IgxGridComponent })
+ public instance: IgxGridComponent;
+}
+
@Component({
template: `
diff --git a/projects/igniteui-angular/src/lib/snackbar/README.md b/projects/igniteui-angular/src/lib/snackbar/README.md
index 5ae63fae2fe..39bad9b6d8a 100644
--- a/projects/igniteui-angular/src/lib/snackbar/README.md
+++ b/projects/igniteui-angular/src/lib/snackbar/README.md
@@ -40,3 +40,17 @@ You can hide the Snackbar by using `snackbar.hide()` method.
By default, the IgxSnackbar will be automatically hidden after 4000 milliseconds. The automatic hiding behavior can be controlled via the following attributes:
- `autoHide` - whether the snackbar should be hidden after a certain time interval.
- `displayTime` - the time interval in which the snackbar would hide.
+
+
+## Snackbar with custom content
+
+```html
+
+
+
+ Custom content
+
+```
+You can display custom content by adding elements inside the snackbar.
diff --git a/projects/igniteui-angular/src/lib/snackbar/snackbar.component.html b/projects/igniteui-angular/src/lib/snackbar/snackbar.component.html
index 3ae3e08ea1b..d9da0c044d2 100644
--- a/projects/igniteui-angular/src/lib/snackbar/snackbar.component.html
+++ b/projects/igniteui-angular/src/lib/snackbar/snackbar.component.html
@@ -1,6 +1,9 @@
- {{ message }}
+
+ {{ message }}
+
+
diff --git a/projects/igniteui-angular/src/lib/snackbar/snackbar.component.spec.ts b/projects/igniteui-angular/src/lib/snackbar/snackbar.component.spec.ts
index f62a31ae920..53781531af3 100644
--- a/projects/igniteui-angular/src/lib/snackbar/snackbar.component.spec.ts
+++ b/projects/igniteui-angular/src/lib/snackbar/snackbar.component.spec.ts
@@ -11,7 +11,8 @@ describe('IgxSnackbar', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
- SnackbarInitializeTestComponent
+ SnackbarInitializeTestComponent,
+ SnackbarCustomContentComponent
],
imports: [
BrowserAnimationsModule,
@@ -35,7 +36,6 @@ describe('IgxSnackbar', () => {
expect(snackbar.displayTime).toBe(4000);
expect(snackbar.autoHide).toBeTruthy();
expect(snackbar.isVisible).toBeFalsy();
- expect(snackbar.actionText).toBeUndefined();
expect(domSnackbar.id).toContain('igx-snackbar-');
snackbar.id = 'customId';
@@ -67,6 +67,7 @@ describe('IgxSnackbar', () => {
snackbar.show();
+ fixture.detectChanges();
expect(snackbar.isVisible).toBeTruthy();
expect(snackbar.autoHide).toBeFalsy();
@@ -90,6 +91,53 @@ describe('IgxSnackbar', () => {
});
});
+describe('IgxSnackbar with custom content', () => {
+ configureTestSuite();
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ SnackbarCustomContentComponent
+ ],
+ imports: [
+ BrowserAnimationsModule,
+ IgxSnackbarModule
+ ]
+ }).compileComponents();
+ }));
+
+ let fixture, domSnackbar, snackbar;
+ beforeEach(async(() => {
+ fixture = TestBed.createComponent(SnackbarCustomContentComponent);
+ fixture.detectChanges();
+ snackbar = fixture.componentInstance.snackbar;
+ domSnackbar = fixture.debugElement.query(By.css('igx-snackbar')).nativeElement;
+ }));
+
+ it('should display a message, a custom content element and a button', () => {
+ fixture.componentInstance.text = 'Undo';
+ snackbar.message = 'Item shown';
+ snackbar.isVisible = true;
+ fixture.detectChanges();
+
+ const messageEl = fixture.debugElement.query(By.css('.igx-snackbar__message'));
+ expect(messageEl.nativeElement.innerText).toContain('Item shown');
+
+ const customContent = fixture.debugElement.query(By.css('.igx-snackbar__content'));
+ expect(customContent).toBeTruthy('Custom content is not found');
+
+ // Verify the message is displayed on the left side of the custom content
+ const messageElRect = (messageEl.nativeElement).getBoundingClientRect();
+ const customContentRect = (customContent.nativeElement).getBoundingClientRect();
+ expect(messageElRect.left <= customContentRect.left).toBe(true, 'The message is not on the left of the custom content');
+
+ // Verify the custom content element is on the left side of the button
+ const button = fixture.debugElement.query(By.css('.igx-snackbar__button'));
+ const buttonRect = (button.nativeElement).getBoundingClientRect();
+ expect(customContentRect.right <= buttonRect.left).toBe(true, 'The custom element is not on the left of the button');
+ expect(messageElRect.right <= buttonRect.left).toBe(true, 'The button is not on the right side of the snackbar content');
+ });
+});
+
@Component({
template: ``
@@ -98,3 +146,13 @@ class SnackbarInitializeTestComponent {
public text: string;
@ViewChild(IgxSnackbarComponent) public snackbar: IgxSnackbarComponent;
}
+
+@Component({
+ template: `
+ Custom content
+ `
+})
+class SnackbarCustomContentComponent {
+ public text: string;
+ @ViewChild(IgxSnackbarComponent) public snackbar: IgxSnackbarComponent;
+}