Expand input width dynamically to the length of st

2020-07-18 10:08发布

问题:

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 an input 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>

also on StackBlitz.

In case of the textarea the scrollbar 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.

回答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.



回答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)
}