your programing

자동 완성 구현

lovepro 2021. 1. 5. 19:47
반응형

자동 완성 구현


Angular2에 대한 좋은 자동 완성 구성 요소를 찾는 데 문제가 있습니다. 키 레이블 객체 목록을 전달할 수 있고 input필드 에서 멋진 자동 완성 기능을 사용할 수있는 모든 것 입니다.

Kendo는 아직 Angular 2를 지원하지 않으며 우리가 내부적으로 주로 사용하는 것입니다. Angular Material이 Angular 2를 아직 지원하지 않는 것 같습니다.

누구든지 저를 올바른 방향으로 안내하거나 그들이 사용하는 것을 알려줄 수 있습니까?

이것이 제가 지금까지 만든 것입니다. 꽤 나쁘고 멋져 보이는 것을 찾고 싶습니다.

import {Component, EventEmitter, Input, Output} from 'angular2/core';
import {Control} from 'angular2/common';
import {Observable} from 'rxjs/Observable';
import {SimpleKeyValue} from '../models/simple-key-value'
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';

@Component({
selector: 'general-typeahead',
template: ` <div>
            <div class="input-group">
            <input type="text" [ngFormControl] = "term" class="form-control" placeholder={{placeHolder}} >
            </div>
            <ul>
                <li class="item" *ngFor="#item of matchingItems" (click)="selectItem(item)">
                    {{item.value}}
                </li>
            </ul>              
</div>`
})

export class GeneralTypeahead {

  matchingItems: Array<SimpleKeyValue>;
  term = new Control();

  @Input() allItems: Array<SimpleKeyValue>;
  @Input() placeHolder: string;
  @Output() onSelectItem = new EventEmitter<SimpleKeyValue>();

  constructor() {
    this.term.valueChanges
        .distinctUntilChanged()
        .debounceTime(200)
        .subscribe((term : string) => this.matchingItems = this.allItems.filter(sl => sl.value.toLowerCase().indexOf(term.toLowerCase()) > -1));
  }

  selectItem(sl: SimpleKeyValue) {
    this.onSelectItem.emit(sl);
  }
}

업데이트 : 이 답변은 ng2-completerAngular2 자동 완성 구성 요소 의 개발로 이어졌습니다 . 다음은 Angular2의 기존 자동 완성 구성 요소 목록입니다.

  1. ng2-completer
  2. ng2- 자동 완성
  3. ng2-typeahead

아이디어를 내놓은 @ dan-cancro에 대한 크레딧

자신의 지침을 만들고자하는 사람들을 위해 원래의 대답을 유지하십시오.

자동 완성 목록을 표시하려면 먼저 입력 텍스트를 기반으로 제안 목록을 반환 한 다음 드롭 다운에 표시 하는 속성 지시문필요합니다 . 지시문에는 목록을 표시하는 두 가지 옵션이 있습니다.

  1. nativeElement에 대한 참조를 얻고 DOM을 직접 조작
  2. DynamicComponentLoader를 사용하여 목록 구성 요소를 동적으로로드

두 번째 방법이 DOM과 직접 작업하여 우회하는 대신 각도 2 코어 메커니즘을 사용하므로 더 나은 선택이라고 생각하므로이 방법을 사용할 것입니다.

다음은 지시어 코드입니다.

"use strict";
import {Directive, DynamicComponentLoader, Input, ComponentRef, Output, EventEmitter, OnInit, ViewContainerRef} from "@angular/core";
import {Promise} from "es6-promise";
import {AutocompleteList} from "./autocomplete-list";

@Directive({
    selector: "[ng2-autocomplete]", // The attribute for the template that uses this directive
    host: {
        "(keyup)": "onKey($event)" // Liten to keyup events on the host component
    }
})
export class AutocompleteDirective implements OnInit {
    // The search function should be passed as an input
    @Input("ng2-autocomplete") public search: (term: string) => Promise<Array<{ text: string, data: any }>>;
    // The directive emits ng2AutocompleteOnSelect event when an item from the list is selected
    @Output("ng2AutocompleteOnSelect") public selected = new EventEmitter();

    private term = "";
    private listCmp: ComponentRef<AutocompleteList> = undefined;
    private refreshTimer: any = undefined;
    private searchInProgress = false;
    private searchRequired = false;

    constructor( private viewRef: ViewContainerRef, private dcl: DynamicComponentLoader) { }
    /**
     * On key event is triggered when a key is released on the host component
     * the event starts a timer to prevent concurrent requests
     */
    public onKey(event: any) {
        if (!this.refreshTimer) {
            this.refreshTimer = setTimeout(
            () => {
                if (!this.searchInProgress) {
                    this.doSearch();
                } else {
                    // If a request is in progress mark that a new search is required
                    this.searchRequired = true;
                }
            },
            200);
        }
        this.term = event.target.value;
        if (this.term === "" && this.listCmp) {
            // clean the list if the search term is empty
            this.removeList();
        }
    }

    public ngOnInit() {
        // When an item is selected remove the list
        this.selected.subscribe(() => {
            this.removeList();
        });
    }

    /**
     * Call the search function and handle the results
     */
    private doSearch() {
        this.refreshTimer = undefined;
        // if we have a search function and a valid search term call the search
        if (this.search && this.term !== "") {
            this.searchInProgress = true;
            this.search(this.term)
            .then((res) => {
                this.searchInProgress = false;
                // if the term has changed during our search do another search
                if (this.searchRequired) {
                    this.searchRequired = false;
                    this.doSearch();
                } else {
                    // display the list of results
                    this.displayList(res);
                }
            })
            .catch(err => {
                console.log("search error:", err);
                this.removeList();
            });
        }
    }

    /**
     * Display the list of results
     * Dynamically load the list component if it doesn't exist yet and update the suggestions list
     */
    private displayList(list: Array<{ text: string, data: any }>) {
        if (!this.listCmp) {
            this.dcl.loadNextToLocation(AutocompleteList, this.viewRef)
            .then(cmp => {
                // The component is loaded
                this.listCmp = cmp;
                this.updateList(list);
                // Emit the selectd event when the component fires its selected event
                (<AutocompleteList>(this.listCmp.instance)).selected
                    .subscribe(selectedItem => {

                    this.selected.emit(selectedItem);
                });
            });
        } else {
            this.updateList(list);
        }
    }

    /**
     * Update the suggestions list in the list component
     */
    private updateList(list: Array<{ text: string, data: any }>) {
        if (this.listCmp) {
            (<AutocompleteList>(this.listCmp.instance)).list = list;
        }
    }

    /**
     * remove the list component
     */
    private removeList() {
        this.searchInProgress = false;
        this.searchRequired = false;
        if (this.listCmp) {
            this.listCmp.destroy();
            this.listCmp = undefined;
        }
    }
}

이 지시문은 드롭 다운 구성 요소를 동적으로로드합니다. 이것은 부트 스트랩 4를 사용하는 이러한 구성 요소의 샘플입니다.

"use strict";
import {Component, Output, EventEmitter} from "@angular/core";

@Component({
    selector: "autocomplete-list",
    template: `<div class="dropdown-menu  search-results">
                    <a *ngFor="let item of list" class="dropdown-item" (click)="onClick(item)">{{item.text}}</a>
               </div>`, // Use a bootstrap 4 dropdown-menu to display the list
    styles: [".search-results { position: relative; right: 0; display: block; padding: 0; overflow: hidden; font-size: .9rem;}"]
})
export class AutocompleteList  {
    // Emit a selected event when an item in the list is selected
    @Output() public selected = new EventEmitter();

    public list;

    /**
     * Listen for a click event on the list
     */
    public onClick(item: {text: string, data: any}) {
        this.selected.emit(item);
    }
}

다른 구성 요소에서 지시문을 사용하려면 지시문을 가져 와서 구성 요소 지시문에 포함하고 선택 항목에 대한 검색 기능 및 이벤트 핸들러를 제공해야합니다.

 "use strict";
import {Component} from "@angular/core";

import {AutocompleteDirective} from "../component/ng2-autocomplete/autocomplete";

@Component({
    selector: "my-cmp",
    directives: [AutocompleteDirective],
    template: `<input class="form-control" type="text" [ng2-autocomplete]="search()" (ng2AutocompleteOnSelect)="onItemSelected($event)" autocomplete="off">`
})
export class MyComponent  {

    /**
     * generate a search function that returns a Promise that resolves to array of text and optionally additional data
     */  
    public search() {
        return (filter: string): Promise<Array<{ text: string, data: any }>> => {
            // do the search
            resolve({text: "one item", data: null});
        };
    }

    /**
     * handle item selection
     */  
    public onItemSelected(selected: { text: string, data: any }) {
        console.log("selected: ", selected.text);
    }
}

업데이트 : angular2 rc.1과 호환되는 코드


PrimeNG에는 템플릿 및 다중 선택과 같은 고급 기능이 포함 된 기본 자동 완성 구성 요소가 있습니다.

http://www.primefaces.org/primeng/#/autocomplete


나는 당신이 사용할 수 있다고 생각합니다 typeahead.js. 그것에 대한 typescript 정의가 있습니다. 그래서 개발을 위해 typescript를 사용하는 경우 사용하기 쉽습니다.


이미 몇 가지 답변이 있다는 것을 알고 있지만 우리 팀은 재료를 사용하기 때문에 무거운 라이브러리 또는 부트 스트랩과 관련된 것에 의존하고 싶지 않은 비슷한 상황에 처해 있었기 때문에 재료와 유사한 기능을 사용하여 자체 자동 완성 제어를 만들었습니다. 스타일, 당신은 내 자동 완성을 사용할 수 있거나 적어도 당신에게 약간의 안내를 줄 수 있습니다 .NPM에서 공유 할 구성 요소를 업로드하는 방법에 대한 간단한 예제에 대한 문서가 많지 않았습니다.


anuglar2 autocomplete를위한 모듈을 만들었습니다.이 모듈에서는 배열 또는 url npm 링크를 사용할 수 있습니다. ang2-autocomplete


앵귤러 머티리얼 에서 자동 완성에 사용할 수있는 빌드 인 구성 요소

확인 하려면 여기를 클릭 하십시오 !

참고 : 요구 사항에 따라이 자동 완성의 필터 논리를 사용자 지정할 수도 있습니다.


나는이 주제와 다른 주제에 대한이 답변 / 다른 튜토리얼의 아이디어 중 일부를 기반으로 상당히 간단하고 재사용 가능하며 기능적인 Angular2 자동 완성 구성 요소를 만들었습니다. 결코 포괄적 인 것은 아니지만 직접 구축하기로 결정한 경우 도움이 될 수 있습니다.

구성 요소 :

import { Component, Input, Output, OnInit, ContentChild, EventEmitter, HostListener } from '@angular/core';
import { Observable } from "rxjs/Observable";
import { AutoCompleteRefDirective } from "./autocomplete.directive";

@Component({
    selector: 'autocomplete',
    template: `
<ng-content></ng-content>
<div class="autocomplete-wrapper" (click)="clickedInside($event)">
    <div class="list-group autocomplete" *ngIf="results">
        <a [routerLink]="" class="list-group-item" (click)="selectResult(result)" *ngFor="let result of results; let i = index" [innerHTML]="dataMapping(result) | highlight: query" [ngClass]="{'active': i == selectedIndex}"></a>
    </div>
</div>
    `,
    styleUrls: ['./autocomplete.component.css']
})
export class AutoCompleteComponent implements OnInit {

    @ContentChild(AutoCompleteRefDirective)
    public input: AutoCompleteRefDirective;

    @Input() data: (searchTerm: string) => Observable<any[]>;
    @Input() dataMapping: (obj: any) => string;
    @Output() onChange = new EventEmitter<any>();

    @HostListener('document:click', ['$event'])
    clickedOutside($event: any): void {
        this.clearResults();
    }

    public results: any[];
    public query: string;
    public selectedIndex: number = 0;
    private searchCounter: number = 0;

    ngOnInit(): void {
        this.input.change
            .subscribe((query: string) => {
                this.query = query;
                this.onChange.emit();
                this.searchCounter++;
                let counter = this.searchCounter;

                if (query) {
                    this.data(query)
                        .subscribe(data => {
                            if (counter == this.searchCounter) {
                                this.results = data;
                                this.input.hasResults = data.length > 0;
                                this.selectedIndex = 0;
                            }
                        });
                }
                else this.clearResults();
            });

        this.input.cancel
            .subscribe(() => {
                this.clearResults();
            });

        this.input.select
            .subscribe(() => {
                if (this.results && this.results.length > 0)
                {
                    this.selectResult(this.results[this.selectedIndex]);
                }
            });

        this.input.up
            .subscribe(() => {
                if (this.results && this.selectedIndex > 0) this.selectedIndex--;
            });

        this.input.down
            .subscribe(() => {
                if (this.results && this.selectedIndex + 1 < this.results.length) this.selectedIndex++;
            });
    }

    selectResult(result: any): void {
        this.onChange.emit(result);
        this.clearResults();
    }

    clickedInside($event: any): void {
        $event.preventDefault();
        $event.stopPropagation();
    }

    private clearResults(): void {
        this.results = [];
        this.selectedIndex = 0;
        this.searchCounter = 0;
        this.input.hasResults = false;
    }
}

구성 요소 CSS :

.autocomplete-wrapper {
    position: relative;
}

.autocomplete {
    position: absolute;
    z-index: 100;
    width: 100%;
}

지침 :

import { Directive, Input, Output, HostListener, EventEmitter } from '@angular/core';

@Directive({
    selector: '[autocompleteRef]'
})
export class AutoCompleteRefDirective {
    @Input() hasResults: boolean = false;
    @Output() change = new EventEmitter<string>();
    @Output() cancel = new EventEmitter();
    @Output() select = new EventEmitter();
    @Output() up = new EventEmitter();
    @Output() down = new EventEmitter();

    @HostListener('input', ['$event'])
    oninput(event: any) {
        this.change.emit(event.target.value);
    }

    @HostListener('keydown', ['$event'])
    onkeydown(event: any)
    {
        switch (event.keyCode) {
            case 27:
                this.cancel.emit();
                return false;
            case 13:
                var hasResults = this.hasResults;
                this.select.emit();
                return !hasResults;
            case 38:
                this.up.emit();
                return false;
            case 40:
                this.down.emit();
                return false;
            default:
        }
    }
}

하이라이트 파이프 :

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

@Pipe({
    name: 'highlight'
})

export class HighlightPipe implements PipeTransform {
    transform(value: string, args: any): any {
        var re = new RegExp(args, 'gi');

        return value.replace(re, function (match) {
            return "<strong>" + match + "</strong>";
        })

    }
}

구현 :

import { Component } from '@angular/core';
import { Observable } from "rxjs/Observable";
import { Subscriber } from "rxjs/Subscriber";

@Component({
    selector: 'home',
    template: `
<autocomplete [data]="getData" [dataMapping]="dataMapping" (onChange)="change($event)">
    <input type="text" class="form-control" name="AutoComplete" placeholder="Search..." autocomplete="off" autocompleteRef />
</autocomplete>
    `
})
export class HomeComponent {

    getData = (query: string) => this.search(query);

    // The dataMapping property controls the mapping of an object returned via getData.
    // to a string that can be displayed to the use as an option to select.
    dataMapping = (obj: any) => obj;

    // This function is called any time a change is made in the autocomplete.
    // When the text is changed manually, no object is passed.
    // When a selection is made the object is passed.
    change(obj: any): void {
        if (obj) {
            // You can do pretty much anything here as the entire object is passed if it's been selected.
            // Navigate to another page, update a model etc.
            alert(obj);
        }
    }

    private searchData = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];

    // This function mimics an Observable http service call.
    // In reality it's probably calling your API, but today it's looking at mock static data.
    private search(query: string): Observable<any>
    {
        return new Observable<any>((subscriber: Subscriber<any>) => subscriber
            .next())
            .map(o => this.searchData.filter(d => d.indexOf(query) > -1));
    }
}

아무도 아직 언급하지 않은 것을 추가하고 싶습니다 : ng2-input-autocomplete

NPM : https://www.npmjs.com/package/ng2-input-autocomplete

GitHub : https://github.com/liuy97/ng2-input-autocomplete#readme

참조 URL : https://stackoverflow.com/questions/35881815/implementing-autocomplete

반응형