Expand input width dynamically to the length of string
Solution 1
You can use ngStyle to bind width of the mat-form-field to a calculated value, and use the input event on the input to set that value. For example, here's an input who's width follows the text width over 64px:
<mat-form-field [ngStyle]="{'width.px': width}">
<input #elasticInput matInput (input)="resize()">
</mat-form-field>
<span #hiddenText style="visibility: hidden; white-space: pre;">{{elasticInput.value}}</span>
export class InputTextWidthExample {
@ViewChild('hiddenText') textEl: ElementRef;
minWidth: number = 64;
width: number = this.minWidth;
resize() {
setTimeout(() => this.width = Math.max(this.minWidth, this.textEl.nativeElement.offsetWidth));
}
}
Obviously, this example uses a hidden span element for getting the text width, which is a little hacky. There is surely more than one way to calculate a string's width, including this.
Here is the example on Stackblitz.
Solution 2
I now created a more suitable solution for this problem. After I found a perfect solution in jQuery and @Obsidian added the corresponding JS code. I tried to adapt it for Angular input
and came up with the following.
I also added some scenarios that support cutting and pasting strings.
Here is a demo on StackBlitz and the corresponding code:
Template:
<style>
#invisibleTextID {
white-space: pre;
}
// prevents flickering:
::ng-deep .mat-form-field-flex {
width: 102% !important;
}
</style>
<mat-form-field
#formFieldInput
[ngStyle]="{'width.px': width}">
<input
#inputText
id="inputTextID"
matInput
[(ngModel)]="inString"
(paste)="resizeInput()"
(cut)="resizeInput()"
(input)="resizeInput()">
</mat-form-field>
<span #invisibleText id="invisibleTextID">{{ inString }}</span>
Resize method:
@ViewChild('invisibleText') invTextER: ElementRef;
inString: string = '';
width: number = 64;
resizeInput() {
// without setTimeout the width gets updated to the previous length
setTimeout ( () =>{
const minWidth = 64;
if (this.invTextER.nativeElement.offsetWidth > minWidth) {
this.width = this.invTextER.nativeElement.offsetWidth + 2;
} else {
this.width = minWidth;
}
}, 0)
}
Solution 3
simple and only template solution :
<mat-form-field
[ngStyle]="{'width.ch': inputText.value.length, 'min-width.ch': 10}">
<input #inputText matInput>
</mat-form-field>
Night Train
Updated on June 09, 2022Comments
-
Night Train almost 2 years
I am trying to create an
input
field that expands at least in width dynamically with the length of the string the user entered, probably even multiline. Is that possible with aninput
element in Angular Material 2?With the
textarea
field from Angular Material 2 I only managed to expand the textarea in height, not in width with the following code:<mat-form-field (keydown.enter)="onEnter()" floatPlaceholder="never"> <textarea matInput placeholder="Autosize textarea" matTextareaAutosize matAutosizeMinRows="1" matAutosizeMaxRows="8"> </textarea> </mat-form-field>
In case of the
textarea
thescrollbar
should be invisible or replaced by a smaller one. And most important pressing Enter should not create a new line but trigger an action only. -
Night Train about 6 yearsI changed
keyup
tokeydown
. That works a little smoother. The unitch
represents the width of the char0
. developer.mozilla.org/en-US/docs/Web/CSS/length. Maybe there is no way around JS to measure the width... -
G. Tranter about 6 yearsThere are ways to measure text width, here's one. Since the keydown event hasn't updated the input's value string yet, you'll need to append the event character to the value string for measurement, and to do that properly you will need to differentiate between control characters like backspace and actual text as well as whitespace. So there are a number of little things to worry about that make this somewhat tedious, but it can work.
-
Night Train about 6 yearsthat works awesome! in my case it might have been enough to set:
min-width: 30vw; width: 30vw; max-width: 98vw; text-align: center;
But that is much better!!