【问题标题】:Angular2 Data binding for Primeng autocomplete componentPrimeng自动完成组件的Angular2数据绑定
【发布时间】:2017-05-20 04:35:16
【问题描述】:

我正在使用 Angular2: 2.1.0Primeng: 1.0.0
我希望 Autocomplete 组件绑定到我的 object's key 并在 UI 中显示 object's value

对象在这里

[{
    "user_id": 101,
    "user_name": "John"
},
{
    "user_id": 101,
    "user_name": "Ganesh"
},
{
    "user_id": 101,
    "user_name": "Irfan"
}]

app.component.html

<p-autoComplete  [(ngModel)]="userId" placeholder="User Search..." field="user_name" [suggestions]="suggestionList"  (completeMethod)="userSearch($event)"></p-autoComplete>

在自动完成中使用field 属性我可以在UI 屏幕中显示我的object's value,但整个对象绑定到userId
如何将所选对象的user_id 绑定到userId

【问题讨论】:

    标签: angular primeng


    【解决方案1】:

    我遇到了同样的问题,实际上是使用单独的方法来捕获值

    captureId(event: any) {
        this.userId = event.user_id;
    }
    

    以及实际使用

    <p-autoComplete (onSelect)="captureId($event)" ...
    

    【讨论】:

    • 嗨 Blazej,t 不适合我。当我开始输入 Jho 时,userId 的值也为“Jho”。如果我在建议列表中选择 Jhon,则 userId 仍然是“Jho”,并且当我从用户建议列表中选择其中之一时,它未更改为 101(输入)事件不会触发。
    • 我已经更正了我的 anwser 不使用 ngModel,你现在可以试试吗?
    • 出现错误,Can't bind to 'value' since it isn't a known property of 'p-autoComplete'.
    • 您好 NTN-JAVA,很抱歉造成混乱,我再次测试了它,回滚到 ngModel 并插入丢失的 onSelect 事件。如果您不想捕获用户的输入(例如当他输入任何文本并提交表单时)只是为了覆盖选择,这可以简化。
    • 嗨 Blazej,很高兴再次见到你,在建议列表中选择用户后,这将在 UI 中显示 user_id 而不是 user_name。如果您从您的答案中删除了[ngModel]="userId",那么这正是我们想要的:-)。但问题是编辑屏幕可能没有 user_name 在自动完成框中 bcz 我删除了 [ngModel]="userId"
    【解决方案2】:

    @NTN-JAVA 我已经完成了我的使用字段属性。

    <p-autoComplete [(ngModel)]="userName" [suggestions]="filteredBrands" name="guestType"
    (completeMethod)="filterBrands($event)" [size]="12" [minLength]="1" field="user_name" inputStyleClass="txt-box" placeholder="Hint: type 'v' or 'f'" [dropdown]="true" (onDropdownClick)="handleDropdownClick($event)">
     </p-autoComplete>

      guestDetails =
        
        [{
            "user_id": 101,
            "user_name": "John"
        },
        {
            "user_id": 102,
            "user_name": "Ganesh"
        },
        {
            "user_id": 103,
            "user_name": "Irfan"
        }]
    
        **Javascript**
        
            handleDropdownClick() {
                this.filteredBrands = [];
                setTimeout(() => {
                  this.filteredBrands = guestDetails;
                  
                }, 100);
              }

    【讨论】:

    • reservationDetails.guestType 对你来说有什么价值?
    • 它绑定到{ "user_id": 101, "user_name": "John" }而不是101。
    • @NTN-JAVA 这是我从组件进行 2 路绑定的模型名称。我已经用正确的数据更新了我的帖子。
    • ,我想你误解了我的问题。我想与 user_id(即 101 不是整个用户对象)绑定,其中建议列表应显示 user_name。就像我们使用labelname 作为下拉组件一样。在你的情况下,整个用户对象将在userName
    • @NTN-JAVA 你现在有问题了。我会想出可行的答案。
    【解决方案3】:

    总结一下我到目前为止对问题和讨论的理解:

    • 自动完成功能为我们提供了一个用户作为模型
    • 但我们想要的是 user_id
    • 基本上,我们需要从 User 到 user_id 的“模型映射”,反之亦然(如果我们的模型使用 user_id 初始化,则应在自动完成中预先选择相应的 User)

    这可以通过包装自动完成(以及所有其他角度输入组件)实现的 ControlValueAccessor 接口以通用方式实现。这个包装器可以进行转换。 然后在包装器上使用 ngModel、formControl 或 formControlName 指令。

    我创建了一个plunkr 来展示这种方法。它使用“国家”而不是“用户”:

    <control-value-mapper [formControl]="control" [toModel]="idOfCountry" [fromModel]="countryForId" >
          <p-autoComplete #cvDelegate
    
            [suggestions]="results" 
            (completeMethod)="search($event)" 
            field="name"
            dataKey="id">
    
          </p-autoComplete>
    </control-value-mapper>
    

    ControlValueMapper 如下所示:

    @Component({
      selector: 'control-value-mapper',
      template: '<ng-content></ng-content>',
      providers: [{
        provide: NG_VALUE_ACCESSOR,
        useExisting: forwardRef(() => ControlValueMapper),
        multi: true
      }]
    })
    export class ControlValueMapper implements ControlValueAccessor {
      @ContentChild('cvDelegate')
      delegate: ControlValueAccessor
    
      @Input()
      fromModel: (any) => any;
    
      @Input()
      toModel: (any) => any;
    
      setDisabledState(isDisabled: boolean) {
        this.delegate.setDisabledState(isDisabled);
      }
    
      writeValue(obj: any) {
        this.delegate.writeValue(this.fromModel(obj));  
      }
    
      registerOnChange(fn: any)  {
        this.delegate.registerOnChange(value => fn(this.toModel(value)));
      }
    
      registerOnTouched(fn: any)  {
        this.delegate.registerOnTouched(value => fn(this.toModel(value)));
      }
    } 
    

    “toModel”和“fromModel”是从 Country 映射到其 id 的函数,反之亦然。

    请注意,此解决方案可能比其他解决方案“更长”,但它可以在所有类似情况下重复使用(使用除自动完成之外的其他输入组件)。

    【讨论】:

      【解决方案4】:

      我在一年前找到了一个解决方案,并为其他人更新了我的答案。 作为stefan's 的回答,我们需要模型映射,但他的回答看起来很庞大。

      我使用primeng自动完成组件并创建了一个名为user-search的自己的组件,其中包含@Input()@Output()事件。

      模板(user.search.component.html

      <p-autoComplete [(ngModel)]="userObject" placeholder="User Search..." field="user_name" [suggestions]="userSuggesstionList"
       (onSelect)="onUserSelect($event)" (completeMethod)="search($event)">
      </p-autoComplete>
      

      组件(UserSearchComponent),

      @Component({
          selector: 'user-search',
          templateUrl: 'user.search.component.html'
      })
      export class UserSearchComponent implements OnInit {
         userSuggesstionList: any[] = [];
          userObject: any;
          constructor(
          ) { }
      
          ngOnInit() {
      
          }
      
          // note that this must be named as the input model name + "Change"
          @Output() userSelected: any = new EventEmitter();
          @Output() userIdChange: any = new EventEmitter();
          @Input()
          set userId(userId: string) {
              if (userId != null && userId != '') {
                  this.userObject = // Load user object from local cache / from service.
              } else {
                  this.userObject = null;
              }
          }
      
          get userId(): string {
              if (this.userObject != null) {
                  return this.userObject.userId;
              } else {
                  return null;
              }
          }
      
          search(event) {
              // your search logic.
          }
      
          onUserSelect(event) {
              this.userIdChange.emit(event.userId);
              this.userSelected.emit(event);
          }
      }
      

      而用户搜索组件的用法是,

      <user-search [(userId)]="user_id"></user-search>
      

      这里的 user_id 作为user-search 组件的输入,user-search 组件根据user_id 从缓存/服务器加载实际用户对象。一旦用户对象被加载,p-autocomplete 将与userObject 绑定并在自动完成框中显示用户名。

      一旦用户从建议列表中选择,就会触发默认更改事件以更新父组件中的user_id值。

      您也可以利用 UserObject 即。 {user_id: 'xxx', user_name:'xxx'} 在userSelected 事件中。

      【讨论】:

        【解决方案5】:

        我们可以简单地将 primeNG 的自动完成功能封装在一个实现 ControlValueAccessor 接口的自定义自动完成组件中。

        如果dataKey 被定义为@Input,自定义组件将自定义数据绑定,如果没有定义dataKey,则保持primeNG 的默认行为。

        在下面的代码中,我只使用了我需要的属性和事件,但它可以应用于所有由 primeNG 的 API 提供的属性和事件。

        这是 HTML 代码:

        <p-autoComplete (completeMethod)="completeMethod.emit($event)"
                        (onClear)="onClear.emit($event)"
                        (onDropdownClick)="onDropdownClick.emit($event)"
                        (onSelect)="select($event)"
                        [dataKey]="dataKey"
                        [delay]="delay"
                        [disabled]="disabled"
                        [dropdown]="dropdown"
                        [emptyMessage]="emptyMessage"
                        [field]="field"
                        [forceSelection]="forceSelection"
                        [maxlength]="maxLength"
                        [minLength]="minLength"
                        [multiple]="multiple"
                        [placeholder]="placeholder"
                        [readonly]="readonly"
                        [required]="required"
                        [styleClass]="styleClass"
                        [suggestions]="suggestions"
                        [unique]="unique"
                        [(ngModel)]="autoCompleteValue">
        </p-autoComplete>
        

        这是打字稿代码:

        import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core';
        import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
        
        @Component({
            selector: 'mb-auto-complete',
            templateUrl: './auto-complete.component.html',
            styleUrls: ['./auto-complete.component.scss'],
            providers: [
                {
                    provide: NG_VALUE_ACCESSOR,
                    useExisting: forwardRef(() => AutoCompleteComponent),
                    multi: true
                }
            ]
        })
        export class AutoCompleteComponent implements ControlValueAccessor {
        
            @Input() dataKey: string = null;
            @Input() delay: number = 300;
            @Input() disabled: boolean;
            @Input() dropdown: boolean = false;
            @Input() emptyMessage: string = null;
            @Input() field: any = null;
            @Input() forceSelection: boolean = null;
            @Input() maxLength: number = null;
            @Input() minLength: number = 1;
            @Input() multiple: boolean = false;
            @Input() placeholder: string;
            @Input() readonly: boolean = false;
            @Input() required: boolean = false;
            @Input() styleClass: string = null;
            @Input() suggestions: any[] = [];
            @Input() unique: boolean = true;
            @Output() completeMethod: EventEmitter<any> = new EventEmitter<any>();
            @Output() onClear: EventEmitter<any> = new EventEmitter<any>();
            @Output() onDropdownClick: EventEmitter<any> = new EventEmitter<any>();
            @Output() onSelect: EventEmitter<any> = new EventEmitter<any>();
            private onChange = (value: any): void => { /**/ };
            private onTouched = (): void => { /**/};
            public autoCompleteValue: any;
        
            public registerOnChange(fn: any): void {
                this.onChange = fn;
            }
        
            public registerOnTouched(fn: any): void {
                this.onTouched = fn;
            }
        
            public setDisabledState(isDisabled: boolean): void {
                this.disabled = isDisabled;
            }
        
            public writeValue(value: any): void {
                if (this.dataKey?.length > 0) {
                    this.autoCompleteValue = this.suggestions.filter((item: any) => item[this.dataKey] === value)[0];
                } else {
                    this.autoCompleteValue = value;
                }
            }
        
            public select(selectedValue: any): void {
                const newValue: any = this.dataKey?.length > 0 ? selectedValue[this.dataKey] : selectedValue;
                this.onSelect.emit(newValue);
                this.onChange(newValue);
            }
        }
        

        然后您可以使用您的自定义组件,无论您在哪里使用&lt;p-autoComplete ..&gt;,您都可以将其替换为&lt;mb-autoComplete ..&gt;(当然除了在AutoCompleteComponent 的html 中您必须保留&lt;p-autoComplete ..&gt;)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-12-28
          • 2019-03-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-08-26
          • 2019-11-30
          • 1970-01-01
          相关资源
          最近更新 更多