mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(autosize): not updating when window is resized (#8619)
Fixes the autosize textarea not recalculating its height when the viewport size has changed. Fixes #8610.
This commit is contained in:
committed by
mmalerba
parent
fb9ea53dbe
commit
b8664b8e85
@@ -1,12 +1,13 @@
|
||||
import {Component, ViewChild} from '@angular/core';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {ComponentFixture, TestBed, async, fakeAsync, flushMicrotasks} from '@angular/core/testing';
|
||||
import {ComponentFixture, TestBed, async, fakeAsync, flush, tick} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {MatInputModule} from './index';
|
||||
import {MatTextareaAutosize} from './autosize';
|
||||
import {MatStepperModule} from '@angular/material/stepper';
|
||||
import {MatTabsModule} from '@angular/material/tabs';
|
||||
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {dispatchFakeEvent} from '@angular/cdk/testing';
|
||||
|
||||
|
||||
describe('MatTextareaAutosize', () => {
|
||||
@@ -185,14 +186,14 @@ describe('MatTextareaAutosize', () => {
|
||||
autosize = fixtureWithPlaceholder.debugElement.query(
|
||||
By.directive(MatTextareaAutosize)).injector.get<MatTextareaAutosize>(MatTextareaAutosize);
|
||||
|
||||
triggerTextareaResize();
|
||||
autosize.resizeToFitContent(true);
|
||||
|
||||
const heightWithLongPlaceholder = textarea.clientHeight;
|
||||
|
||||
fixtureWithPlaceholder.componentInstance.placeholder = 'Short';
|
||||
fixtureWithPlaceholder.detectChanges();
|
||||
|
||||
triggerTextareaResize();
|
||||
autosize.resizeToFitContent(true);
|
||||
|
||||
expect(textarea.clientHeight).toBe(heightWithLongPlaceholder,
|
||||
'Expected the textarea height to be the same with a long placeholder.');
|
||||
@@ -213,7 +214,7 @@ describe('MatTextareaAutosize', () => {
|
||||
Some late visitor entreating entrance at my chamber door;—
|
||||
This it is and nothing more.” `;
|
||||
fixtureWithForms.detectChanges();
|
||||
flushMicrotasks();
|
||||
flush();
|
||||
fixtureWithForms.detectChanges();
|
||||
|
||||
expect(textarea.clientHeight)
|
||||
@@ -229,7 +230,7 @@ describe('MatTextareaAutosize', () => {
|
||||
`;
|
||||
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
flush();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(textarea.clientHeight)
|
||||
@@ -250,16 +251,14 @@ describe('MatTextareaAutosize', () => {
|
||||
expect(textarea.getBoundingClientRect().height).toBeGreaterThan(1);
|
||||
});
|
||||
|
||||
/** Triggers a textarea resize to fit the content. */
|
||||
function triggerTextareaResize() {
|
||||
// To be able to trigger a new calculation of the height with a short placeholder, the
|
||||
// textarea value needs to be changed.
|
||||
textarea.value = '1';
|
||||
autosize.resizeToFitContent();
|
||||
it('should trigger a resize when the window is resized', fakeAsync(() => {
|
||||
spyOn(autosize, 'resizeToFitContent');
|
||||
|
||||
textarea.value = '';
|
||||
autosize.resizeToFitContent();
|
||||
}
|
||||
dispatchFakeEvent(window, 'resize');
|
||||
tick(16);
|
||||
|
||||
expect(autosize.resizeToFitContent).toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
|
||||
// Styles to reset padding and border to make measurement comparisons easier.
|
||||
|
||||
@@ -6,8 +6,20 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Directive, ElementRef, Input, AfterViewInit, DoCheck} from '@angular/core';
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
Input,
|
||||
AfterViewInit,
|
||||
DoCheck,
|
||||
OnDestroy,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import {Platform} from '@angular/cdk/platform';
|
||||
import {fromEvent} from 'rxjs/observable/fromEvent';
|
||||
import {auditTime} from 'rxjs/operators/auditTime';
|
||||
import {takeUntil} from 'rxjs/operators/takeUntil';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
|
||||
|
||||
/**
|
||||
@@ -23,9 +35,10 @@ import {Platform} from '@angular/cdk/platform';
|
||||
'rows': '1',
|
||||
},
|
||||
})
|
||||
export class MatTextareaAutosize implements AfterViewInit, DoCheck {
|
||||
export class MatTextareaAutosize implements AfterViewInit, DoCheck, OnDestroy {
|
||||
/** Keep track of the previous textarea value to avoid resizing when the value hasn't changed. */
|
||||
private _previousValue: string;
|
||||
private _destroyed = new Subject<void>();
|
||||
|
||||
private _minRows: number;
|
||||
private _maxRows: number;
|
||||
@@ -49,7 +62,12 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
|
||||
/** Cached height of a textarea with a single row. */
|
||||
private _cachedLineHeight: number;
|
||||
|
||||
constructor(private _elementRef: ElementRef, private _platform: Platform) {}
|
||||
constructor(
|
||||
private _elementRef: ElementRef,
|
||||
private _platform: Platform,
|
||||
private _ngZone?: NgZone) {}
|
||||
|
||||
// TODO(crisbeto): make the `_ngZone` a required param in the next major version.
|
||||
|
||||
/** Sets the minimum height of the textarea as determined by minRows. */
|
||||
_setMinHeight(): void {
|
||||
@@ -74,9 +92,22 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
|
||||
ngAfterViewInit() {
|
||||
if (this._platform.isBrowser) {
|
||||
this.resizeToFitContent();
|
||||
|
||||
if (this._ngZone) {
|
||||
this._ngZone.runOutsideAngular(() => {
|
||||
fromEvent(window, 'resize')
|
||||
.pipe(auditTime(16), takeUntil(this._destroyed))
|
||||
.subscribe(() => this.resizeToFitContent(true));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._destroyed.next();
|
||||
this._destroyed.complete();
|
||||
}
|
||||
|
||||
/** Sets a style property on the textarea element. */
|
||||
private _setTextareaStyle(property: string, value: string): void {
|
||||
const textarea = this._elementRef.nativeElement as HTMLTextAreaElement;
|
||||
@@ -134,8 +165,12 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
|
||||
}
|
||||
}
|
||||
|
||||
/** Resize the textarea to fit its content. */
|
||||
resizeToFitContent() {
|
||||
/**
|
||||
* Resize the textarea to fit its content.
|
||||
* @param force Whether to force a height recalculation. By default the height will be
|
||||
* recalculated only if the value changed since the last call.
|
||||
*/
|
||||
resizeToFitContent(force = false) {
|
||||
this._cacheTextareaLineHeight();
|
||||
|
||||
// If we haven't determined the line-height yet, we know we're still hidden and there's no point
|
||||
@@ -148,7 +183,7 @@ export class MatTextareaAutosize implements AfterViewInit, DoCheck {
|
||||
const value = textarea.value;
|
||||
|
||||
// Only resize of the value changed since these calculations can be expensive.
|
||||
if (value === this._previousValue) {
|
||||
if (value === this._previousValue && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user