简体   繁体   English

Primeng 自动完成组件的 Angular2 数据绑定

[英]Angular2 Data binding for Primeng autocomplete component

Am using Angular2: 2.1.0 and Primeng: 1.0.0 ,我正在使用Angular2: 2.1.0Primeng: 1.0.0
I want Autocomplete component to bind to my object's key and show object's value in UI.我希望Autocomplete组件绑定到我的object's key并在 UI 中显示object's value

Here the Object is ,这里的对象是

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

app.component.html应用程序组件.html

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

Using field attribute in autocomplete i can show my object's value in UI screen, but the entire object is binded to userId在自动完成中使用field属性我可以在 UI 屏幕中显示我的object's value ,但整个对象都绑定到userId
How can i make binding user_id of selected object to userId ?如何将所选对象的user_id绑定到userId

I had the same issue and actually ended using a separate method to capture the value我有同样的问题,实际上结束使用单独的方法来捕获值

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

And the actual use和实际使用

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

@NTN-JAVA I have done this my using field property. @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); }

To summarize my understanding of the question and discussion so far:总结我到目前为止对问题和讨论的理解:

  • the autocomplete gives us a User as model自动完成为我们提供了一个 User 作为模型
  • but what we want is user_id但我们想要的是 user_id
  • basically, we need a "model mapping" from User to user_id and also the other way around (if our model is initialized with a user_id, the according User should be pre-selected in the auto-complete )基本上,我们需要从 User 到 user_id 的“模型映射”,反之亦然(如果我们的模型是用 user_id 初始化的,则应在自动完成中预先选择相应的 User )

This can be achieved in a generic way by wrapping the ControlValueAccessor interface that autocomplete (and all other input components in angular) implements.这可以通过包装自动完成(以及 angular 中的所有其他输入组件)实现的 ControlValueAccessor 接口以通用方式实现。 This wrapper can do the transformation.这个包装器可以进行转换。 ngModel, formControl or formControlName directive is then used on the wrapper.然后在包装器上使用 ngModel、formControl 或 formControlName 指令。

I have created a plunkr to show this approach.我创建了一个plunkr来展示这种方法。 It uses "Country" instead of "User":它使用“国家”而不是“用户”:

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

The ControlValueMapper looks like this: 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" and "fromModel" are the functions that map from Country to its id and vice versa. "toModel" 和 "fromModel" 是从 Country 映射到其 id 的函数,反之亦然。

Note that this solution is probably 'longer' than others, but it can be re-used in all similar situations (with other input components than autocomplete).请注意,此解决方案可能比其他解决方案“更长”,但它可以在所有类似情况下重复使用(使用除自动完成以外的其他输入组件)。

I had found a solution an year ago and updating my answer for others.一年前我找到了一个解决方案,并为其他人更新了我的答案。 As stefan's answer we need model mapping, but his answer looks large process.作为stefan's回答,我们需要模型映射,但他的回答看起来是一个很大的过程。

I used primeng autocomplete component and created a own component called user-search with @Input() and @Output() events.我使用了primeng 自动完成组件并使用@Input()@Output()事件创建了一个名为user-search的自己的组件。

Template ( user.search.component.html )模板( user.search.component.html

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

Component ( UserSearchComponent ),组件( 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);
    }
}

And the usage of user-search component is,用户搜索组件的用法是,

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

Here the user_id given as input to user-search component, user-search component loads actual user object from cache/ from server as based on user_id .这里将 user_id 作为输入给user-search组件, user-search组件根据user_id从缓存/服务器加载实际用户对象。 once the user object gets loaded then p-autocomplete will bind with userObject and displayed the username in autocomplete box.一旦用户对象被加载, p-autocomplete将与userObject绑定并在自动完成框中显示用户名。

Once user selected from suggestion list, A default change event is triggered to update user_id value in parent component.一旦用户从建议列表中选择,就会触发默认更改事件以更新父组件中的user_id值。

Also you can avail the UserObject ie.你也可以利用 UserObject 即。 {user_id: 'xxx', user_name:'xxx'} in userSelected event. {user_id: 'xxx', user_name:'xxx'} 在userSelected事件中。

We can simply wrap primeNG's autocomplete inside a custom autocomplete component that implements ControlValueAccessor interface.我们可以简单地将 primeNG 的自动完成包装在一个实现ControlValueAccessor接口的自定义自动完成组件中。

The custom component will customize the data binding if a dataKey is defined as an @Input or keeps primeNG's default behavior if no dataKey is defined.自定义组件将自定义数据如果结合dataKey被定义为@Input或保持如果没有primeNG的默认行为dataKey定义。

In the following code I use only properties and events I need, but it can be applied to all properties and events provided by primeNG's API.在下面的代码中,我只使用了我需要的属性和事件,但它可以应用于 primeNG 的 API 提供的所有属性和事件。

Here is the HTML code :这是 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>

And here is the typescript code :这是打字稿代码:

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

You can then use your custom component, everywhere you use <p-autoComplete ..> you can replace it by <mb-autoComplete ..> (of course except in the html of AutoCompleteComponent where you must keep <p-autoComplete ..> ).然后你可以使用你的自定义组件,在你使用<p-autoComplete ..>任何地方你都可以用<mb-autoComplete ..>替换它(当然除了在AutoCompleteComponent的 html 中你必须保留<p-autoComplete ..> )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM