简体   繁体   English

如何停止或禁用 ngbTypeahead 输入字段在焦点上打开,直到它完成映射所有数据

[英]How to stop or disable ngbTypeahead input field from opening on focus until it finishes mapping all the data

I need the typeahead to show the first 5 results when clicked on the input field.单击输入字段时,我需要预先输入以显示前 5 个结果。 I have already made a solution using the ngbTypeahead documentation .我已经使用 ngbTypeahead 文档制定了解决方案。

app.component.html app.component.html

<div class="form-group g-0 mb-3">
  <input id="typeahead-prevent-manual-entry" type="text" class="form-control"
  placeholder="Big dataset"
  formControlName="bigDataset"
  [ngbTypeahead]="search"
  [inputFormatter]="valueFormatter"
  [resultFormatter]="valueFormatter"
  [editable]="false"
  [focusFirst]="false"
  (focus)="stateFocus$.next($any($event).target.value)"
  (click)="stateClick$.next($any($event).target.value)"
  #instance="ngbTypeahead" />
</div>

app.component.ts app.component.ts

type BigDataset: {
  id: string,
  name: string
}

export class AppComponent implements OnInit {
  dataset: BigDataset[];

  @ViewChild('instance', {static: true}) instance: NgbTypeahead;
  focus$ = new Subject<string>();
  click$ = new Subject<string>();

  constructor(
    private formBuilder: FormBuilder,
  ) { }

  ngOnInit(): void {
    this.dataForm = this.formBuilder.group({
      bigDataset: ["", [Validators.required]],
    });
  }

  getBigDataset() {
    //Excluded for simplicity. This returns a set of objects (~3000)
    //of type BigDataset and assigns it to this.dataset.
  }

  valueFormatter = (value: any) => value.name;

  search: OperatorFunction<string, readonly BigDataset[]> = (text$: Observable<string>) => {
    const debouncedText$ = text$.pipe(debounceTime(100), distinctUntilChanged());
    const clicksWithClosedPopup$ = this.click$.pipe(filter(() => !this.instance.isPopupOpen()));
    const inputFocus$ = this.focus$;

    return merge(debouncedText$, inputFocus$, clicksWithClosedPopup$).pipe(map(term => (term === '' ? this.dataset
        : this.dataset.filter(data => data.name.toLowerCase().indexOf(term.toLowerCase()) > -1)).slice(0, 5))
    );
  };
}

Now this works .现在这有效 However, the problem is if I click on the input field immediately after the page initialisation, I get this error:但是,问题是如果我在页面初始化后立即单击输入字段,我会收到此错误:

ERROR TypeError: Cannot read properties of undefined (reading 'slice')
    at org-address.component.ts:93:109
    at map.js:7:1
    at OperatorSubscriber._next (OperatorSubscriber.js:9:1)
    at OperatorSubscriber.next (Subscriber.js:31:1)
    at subscribe._OperatorSubscriber__WEBPACK_IMPORTED_MODULE_1__.OperatorSubscriber.innerComplete (mergeInternals.js:25:1)
    at OperatorSubscriber._next (OperatorSubscriber.js:9:1)
    at OperatorSubscriber.next (Subscriber.js:31:1)
    at Subject.js:31:1
    at errorContext (errorContext.js:19:1)
    at Subject.next (Subject.js:26:21)

From what I can understand this is because the typeahead tries to show data before it finishes the map function.据我所知,这是因为预输入试图在完成 map function 之前显示数据。 The problem is the time it takes to map can increase if I decided to increase the dataset, so waiting an unknown amount of time until it finishes the process is not an option.问题是如果我决定增加数据集,map 所需的时间可能会增加,因此等待未知的时间直到完成该过程不是一个选项。 I would like to disable the field or use another solution until it finished the mapping process.我想禁用该字段或使用其他解决方案,直到它完成映射过程。

I have tried disabling the form field and enabling it after the mapping process using finalize(), but I seem to have made a mistake and the field stays disabled.我曾尝试禁用表单字段并在映射过程之后使用 finalize() 启用它,但我似乎犯了一个错误并且该字段保持禁用状态。

search: OperatorFunction<string, readonly BigDataset[]> = (text$: Observable<string>) => {
    this.dataForm.get('bigDataset')?.disable();
    const debouncedText$ = text$.pipe(debounceTime(100), distinctUntilChanged());
    const clicksWithClosedPopup$ = this.click$.pipe(filter(() => !this.instance.isPopupOpen()));
    const inputFocus$ = this.focus$;

    return merge(debouncedText$, inputFocus$, clicksWithClosedPopup$).pipe(
      finalize(() => this.dataForm.get('bigDataset')?.enable()),
      map(term => (term === '' ? this.dataset
        : this.dataset.filter(data => data.name.toLowerCase().indexOf(term.toLowerCase()) > -1)).slice(0, 5))
    );
  };

Any help regarding this would be greatly appreciated.对此的任何帮助将不胜感激。

A friend of mine actually gave me a very simple fix for this, which was to add optional chaining after data filtering (before the splicing process).我的一个朋友实际上给了我一个非常简单的解决方法,就是在数据过滤之后(拼接过程之前)添加可选链接。 Adding '?'添加'? before calling the slice method worked like a charm and stopped giving an error if it tried returning the value before the filtering process is completed.在调用 slice 方法之前,如果它尝试在过滤过程完成之前返回值,它就像一个魅力一样工作并且停止给出错误。

search: OperatorFunction<string, readonly BigDataset[]> = (text$: Observable<string>) => {
    const debouncedText$ = text$.pipe(debounceTime(100), distinctUntilChanged());
    const clicksWithClosedPopup$ = this.click$.pipe(filter(() => !this.instance.isPopupOpen()));
    const inputFocus$ = this.focus$;

    return merge(debouncedText$, inputFocus$, clicksWithClosedPopup$).pipe(map(term => (term === '' ? this.dataset
        : this.dataset.filter(data => data.name.toLowerCase().indexOf(term.toLowerCase()) > -1))?.slice(0, 5))
    );
  };

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

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