Skip to content

Commit 4328501

Browse files
committed
fix(autosize): not updating when window is resized
Fixes the autosize textarea not recalculating its height when the viewport size has changed. Fixes #8610.
1 parent 6e865b7 commit 4328501

File tree

2 files changed

+50
-20
lines changed

2 files changed

+50
-20
lines changed

src/lib/input/autosize.spec.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import {Component, ViewChild} from '@angular/core';
22
import {FormsModule} from '@angular/forms';
3-
import {ComponentFixture, TestBed, async, fakeAsync, flushMicrotasks} from '@angular/core/testing';
3+
import {ComponentFixture, TestBed, async, fakeAsync, flush, tick} from '@angular/core/testing';
44
import {By} from '@angular/platform-browser';
55
import {MatInputModule} from './index';
66
import {MatTextareaAutosize} from './autosize';
77
import {MatStepperModule} from '@angular/material/stepper';
88
import {MatTabsModule} from '@angular/material/tabs';
99
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
10+
import {dispatchFakeEvent} from '@angular/cdk/testing';
1011

1112

1213
describe('MatTextareaAutosize', () => {
@@ -185,14 +186,14 @@ describe('MatTextareaAutosize', () => {
185186
autosize = fixtureWithPlaceholder.debugElement.query(
186187
By.directive(MatTextareaAutosize)).injector.get<MatTextareaAutosize>(MatTextareaAutosize);
187188

188-
triggerTextareaResize();
189+
autosize.resizeToFitContent(true);
189190

190191
const heightWithLongPlaceholder = textarea.clientHeight;
191192

192193
fixtureWithPlaceholder.componentInstance.placeholder = 'Short';
193194
fixtureWithPlaceholder.detectChanges();
194195

195-
triggerTextareaResize();
196+
autosize.resizeToFitContent(true);
196197

197198
expect(textarea.clientHeight).toBe(heightWithLongPlaceholder,
198199
'Expected the textarea height to be the same with a long placeholder.');
@@ -213,7 +214,7 @@ describe('MatTextareaAutosize', () => {
213214
Some late visitor entreating entrance at my chamber door;—
214215
This it is and nothing more.” `;
215216
fixtureWithForms.detectChanges();
216-
flushMicrotasks();
217+
flush();
217218
fixtureWithForms.detectChanges();
218219

219220
expect(textarea.clientHeight)
@@ -229,7 +230,7 @@ describe('MatTextareaAutosize', () => {
229230
`;
230231

231232
fixture.detectChanges();
232-
flushMicrotasks();
233+
flush();
233234
fixture.detectChanges();
234235

235236
expect(textarea.clientHeight)
@@ -250,16 +251,14 @@ describe('MatTextareaAutosize', () => {
250251
expect(textarea.getBoundingClientRect().height).toBeGreaterThan(1);
251252
});
252253

253-
/** Triggers a textarea resize to fit the content. */
254-
function triggerTextareaResize() {
255-
// To be able to trigger a new calculation of the height with a short placeholder, the
256-
// textarea value needs to be changed.
257-
textarea.value = '1';
258-
autosize.resizeToFitContent();
254+
it('should trigger a resize when the window is resized', fakeAsync(() => {
255+
spyOn(autosize, 'resizeToFitContent');
259256

260-
textarea.value = '';
261-
autosize.resizeToFitContent();
262-
}
257+
dispatchFakeEvent(window, 'resize');
258+
tick(10);
259+
260+
expect(autosize.resizeToFitContent).toHaveBeenCalled();
261+
}));
263262
});
264263

265264
// Styles to reset padding and border to make measurement comparisons easier.

src/lib/input/autosize.ts

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,20 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Directive, ElementRef, Input, AfterViewInit, DoCheck} from '@angular/core';
9+
import {
10+
Directive,
11+
ElementRef,
12+
Input,
13+
AfterViewInit,
14+
DoCheck,
15+
OnDestroy,
16+
NgZone,
17+
} from '@angular/core';
1018
import {Platform} from '@angular/cdk/platform';
19+
import {fromEvent} from 'rxjs/observable/fromEvent';
20+
import {debounceTime} from 'rxjs/operators/debounceTime';
21+
import {takeUntil} from 'rxjs/operators/takeUntil';
22+
import {Subject} from 'rxjs/Subject';
1123

1224

1325
/**
@@ -22,9 +34,10 @@ import {Platform} from '@angular/cdk/platform';
2234
'rows': '1',
2335
},
2436
})
25-
export class MatTextareaAutosize implements AfterViewInit, DoCheck {
37+
export class MatTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy {
2638
/** Keep track of the previous textarea value to avoid resizing when the value hasn't changed. */
2739
private _previousValue: string;
40+
private _destroyed = new Subject<void>();
2841

2942
private _minRows: number;
3043
private _maxRows: number;
@@ -47,7 +60,10 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
4760
/** Cached height of a textarea with a single row. */
4861
private _cachedLineHeight: number;
4962

50-
constructor(private _elementRef: ElementRef, private _platform: Platform) {}
63+
constructor(
64+
private _elementRef: ElementRef,
65+
private _platform: Platform,
66+
private _ngZone: NgZone) {}
5167

5268
/** Sets the minimum height of the textarea as determined by minRows. */
5369
_setMinHeight(): void {
@@ -72,9 +88,20 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
7288
ngAfterViewInit() {
7389
if (this._platform.isBrowser) {
7490
this.resizeToFitContent();
91+
92+
this._ngZone.runOutsideAngular(() => {
93+
fromEvent(window, 'resize')
94+
.pipe(debounceTime(10), takeUntil(this._destroyed))
95+
.subscribe(() => this.resizeToFitContent(true));
96+
});
7597
}
7698
}
7799

100+
ngOnDestroy() {
101+
this._destroyed.next();
102+
this._destroyed.complete();
103+
}
104+
78105
/** Sets a style property on the textarea element. */
79106
private _setTextareaStyle(property: string, value: string): void {
80107
const textarea = this._elementRef.nativeElement as HTMLTextAreaElement;
@@ -132,8 +159,12 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
132159
}
133160
}
134161

135-
/** Resize the textarea to fit its content. */
136-
resizeToFitContent() {
162+
/**
163+
* Resize the textarea to fit its content.
164+
* @param force Whether to force a height recalculation. By default the height will be
165+
* recalculated only if the value changed since the last call.
166+
*/
167+
resizeToFitContent(force = false) {
137168
this._cacheTextareaLineHeight();
138169

139170
// If we haven't determined the line-height yet, we know we're still hidden and there's no point
@@ -146,7 +177,7 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
146177
const value = textarea.value;
147178

148179
// Only resize of the value changed since these calculations can be expensive.
149-
if (value === this._previousValue) {
180+
if (value === this._previousValue && !force) {
150181
return;
151182
}
152183

0 commit comments

Comments
 (0)