【问题标题】:how to display different values on focus and on blur in autocomplete component如何在自动完成组件中显示不同的焦点和模糊值
【发布时间】:2019-07-30 13:56:33
【问题描述】:

我正在尝试根据焦点/模糊自动完成输入显示不同的值。

假设我们有项目建议,其中每个项目都有iddesc。我想按iddesc 过滤项目。当我选择其中一个并且输入模糊时,应该显示 iddesc(例如 1 - item01)。

当焦点回到输入时,它应该只显示id(没有desc),如果有任何关于id的建议(比如如果有10,它应该建议1010 | desc)。现在我必须强制删除desc 才能回到id。不应该是这样的。

感谢您的任何建议。

您可以尝试编辑this example

查看图片上的预期行为:

【问题讨论】:

  • 所以您希望在重新聚焦时自动删除描述,并且只有 id 应该在那里?
  • 尝试使用管道。我制作了一个样本,可以让你开始stackblitz.com/edit/angular-mssmik

标签: javascript angular typescript angular-material


【解决方案1】:

控制值访问器 (CVA)

ControlValueAccessor 接口正是您所寻找的。​​p>

为什么?该接口将 DOM 与 Angular 表单分离,允许显示下拉菜单和输入与表单实际使用的值不同。

您可以将自定义输入实现为单独的组件*并传入 FormControl。

以下是未经测试的Working Stackblitz

*Edit 3 - 我相信它也可以作为指令来实现。见-Angular 2 Directive implementing ControlValueAccessor doesn't update 'touched' property on change


在黑匣子之外

您的 app.component.html 最终会看起来像。

<form class="example-form">
  <app-auto-special [users]="options" [formControl]="myControl"></app-auto-special>
</form>

app-auto-special 就像一个只关心用户 ID 的黑匣子。

我们可以 patchValue 或 setValue 并且它会做的事情(在内部调用 writeValue)。如果我们与这个组件交互,我们会得到 FormControl 值的用户 ID。

编辑 - 没有什么能阻止你传递整个 User 对象。根据问题,我假设 OP 想要 id。

编辑 2 - 传递用户对象而不是 Working Stackbliz

的示例

黑匣子内部

我们需要使用 NG_VALUE_ACCESSOR 将 app-auto-special 组件注册为 controlValueAccessor 的提供者。这是通过:

  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => AutoSpecialComponent),
    multi: true
  }]

所以在黑盒中我们实现了由 4 个方法组成的接口:

  1. writeValue(obj: any): void
  2. registerOnChange(fn: any): void
  3. registerOnTouched(fn: any): void
  4. setDisabledState(isDisabled: boolean)?: void

这通常意味着以下样板:

export class AutoSpecialComponent implements ControlValueAccessor {
  public _value: number;
  public disabled: boolean;
  onChanged: any = () => {};
  onTouched: any = () => {};

  /*
  * ControlValueAccessor boilerplate
  *
  */
  writeValue(value): void {
    this._value = value
  }
  registerOnChange(fn: any): void {
    this.onChanged = fn;
  }
  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled
  }

}

我们复制registerOnChangeregisterOnTouched 提供的函数,并在我们想要更新formControl 值或者它在touched 属性上时调用这些副本(this.onChangedthis.onTouched)。 setDisabledState 是可选的,writeValue 在初始化时调用,或者当我们从父级调用 patchValuesetValue 时调用。

要设置 FormControl,我们调用 this.onChanged(some_value);,我们可以挂钩各种事件 inputfocusinbluroptionSelected,并分别决定发生什么:

  • FormControl 值
  • 显示哪些选项
  • 输入中应包含什么显示字符串

这个答案附带一个警告,这是我完成的第一个 CVA 实现之一,所以我在基础上摇摇欲坠。


额外好处

  • 单元测试 - 隔离 DOM 显示与表单
  • 逻辑分离 - 父级不再饱和

资源

通过以下 youtube 视频了解更多信息 The Control Value Accessor | Jennifer Wadella

下面附上slides

【讨论】:

  • 如果需要重用,这有潜力,但必须进行相当多的修改以允许使用不同的数据集和格式。否则,这可能是严厉的。它还假设表单控件值可以只是“id”——而不是 OP 使用的整个“用户”对象。它需要工作,因为它不能正常工作 - 聚焦、选择、模糊、聚焦导致显示文本只是 id。
  • @G.Tranter per OP...当焦点回到输入时,它应该只显示 id。绝对没有什么能阻止你将用户传递给表单控件
  • 我的错 - 我现在明白你在说什么,onBlur 应该显示 id 和 descr。现在已修复。
  • @Sergey - 仍然需要我在您删除答案之前提到的修复(这是一个很好的答案) - 您无法在该字段中输入。固定:stackblitz.com/edit/angular-kcb71e?file=src/app/….
【解决方案2】:

更新:

基于用户@Sergey 删除的答案,您需要做的就是向输入元素添加管道和模板引用。无需进行其他更改:

Stackblitz

HTML:

<input type="text" placeholder="Assignee" aria-label="Assignee" 
  matInput [formControl]="myControl" [matAutocomplete]="auto"
  #input [value]="myControl.value | inputFocus:input.ownerDocument.activeElement === input">

管道:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'inputFocus'
})
export class InputFocusPipe implements PipeTransform {

  transform(value: any, focused: boolean): any {
    if (!value || typeof value === 'string') { 
      return value; 
    }

    return focused ? value.id : value.id + ' | ' + value.description;
  }

}

【讨论】:

    猜你喜欢
    • 2019-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-07
    • 1970-01-01
    相关资源
    最近更新 更多