将输入宽度动态扩展到字符串的长度

Expand input width dynamically to the length of string

我正在尝试创建一个 input 字段,该字段至少在宽度上随用户输入的字符串长度动态扩展,甚至可能是多行。 Angular Material 2 中的 input 元素可能吗?

使用 Angular Material 2 中的 textarea 字段,我只能使用以下代码扩展高度的文本区域,而不是宽度:

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

textarea 的情况下,scrollbar 应该是不可见的或被较小的替换。最重要的是,按 Enter 不应创建新行,而只会触发一个操作。

您可以使用 ngStyle 将 mat-form-field 的宽度绑定到计算值,并使用输入上的输入事件来设置该值。例如,这里有一个宽度跟随超过 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));
    }
}

显然,此示例使用隐藏的 span 元素来获取文本宽度,这有点 hacky。计算字符串宽度的方法肯定不止一种,包括 this.

Here is the example on Stackblitz.

我现在为这个问题创建了一个更合适的解决方案。在我在 jQuery 中找到完美的解决方案后,@Obsidian 添加了 corresponding JS code。我尝试将其改编为 Angular input 并提出以下内容。 我还添加了一些支持剪切和粘贴字符串的场景。

Here is a demo on StackBlitz及对应代码:

模板:

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

调整大小方法:

@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)
}

我需要 matInputmat-table 内的动态宽度(动态大小),我尝试了几种基于 的方法。但是,根据呈现的 table 单元查看动态数量的 <span> 元素是不切实际的。因此,我想出了一个比较实用的解决方案:

每当数据更新时,我创建一个 <span> 元素,用更新的数据填充它,将它添加到 HTML body(或 div 中我的情况),提取 <span>offsetWidth 并立即将其从 HTML 文档中删除。在测试时,我没有观察到任何闪烁或渲染问题(尽管我认为这是可能的)。

模板

...

<!-- col1 Column -->
    <ng-container matColumnDef="col1">
      <th *matHeaderCellDef mat-header-cell mat-sort-header scope="col"> ifPattern</th>
      <td *matCellDef="let dataRecord;" mat-cell>

        <mat-form-field [ngStyle]="{'width.px': columnWidth[0]}" floatLabel="never">

          <input (input)="resize(0, dataRecord.col1)" [(ngModel)]="dataRecord.col1"
                 [value]="dataRecord.col1" matInput placeholder="col1 value">

        </mat-form-field>

      </td>
    </ng-container>
...

调整大小方法

displayedColumns: string[] = ["col1", "col2", "col3"];
minWidth: number = 64; // pixel
columnWidth: number[] = [this.minWidth, this.minWidth, this.minWidth];

  resize(column_index: number, value: string) {
    setTimeout(() => {
      let span = document.createElement('span');
      span.innerText = value;
      let div = document.getElementById('case-cleaning-overview')
      div?.append(span);
      this.columnWidth[column_index] = Math.max(this.minWidth, span.offsetWidth);
      div?.removeChild(span);
    });
  }

简单且唯一的模板解决方案:

<mat-form-field
  [ngStyle]="{'width.ch': inputText.value.length, 'min-width.ch': 10}"> 
  <input  #inputText  matInput>
</mat-form-field>