Tips for preventing the ngbTypeahead input field from automatically opening when focused until all data is fully mapped

When clicking on the input field, I want the typeahead feature to display the first 5 results. I have created a solution based on the ngbTypeahead documentation.

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

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

Although the code works as intended, there is an issue when clicking on the input field immediately after page initialization, resulting in the following error:

ERROR TypeError: Cannot read properties of undefined (reading 'slice')
    at org-address.component.ts:93:109
    // The rest of the error messages follow...

The root cause seems to be that the typeahead attempts to display data before completing the map function. Since the mapping process duration can vary with dataset size, I am seeking a way to disable or implement an alternative until the mapping completes without having to wait indefinitely.

I attempted to programmatically disable and enable the form field using finalize(), but encountered difficulty as the field remained disabled even after the completion of mapping.

search: OperatorFunction<string, readonly BigDataset[]> = (text$: Observable<string>) => {
    this.dataForm.get('bigDataset')?.disable();
    // The remaining functionality and code snippets go here...
    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))
    );
  };

If anyone can provide assistance or suggestions regarding this matter, it would be greatly appreciated.

Answer №1

I was fortunate enough to receive a helpful tip from a friend regarding this issue. The solution involved implementing optional chaining after data filtering, right before the splicing process. Simply adding '?' before invoking the slice method resolved the error seamlessly, preventing it from attempting to return a value prior to the completion of the filtering process.

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Storing a reference globally in React and Typescript: Best practices

In my application, I have multiple instances of a specific component called <Item>. Each <Item> needs to display a dynamic tooltip when hovered over. To achieve this, I am utilizing semantic-ui-react and its Popup component. The conventional m ...

Android layout featuring Ionic2 navbar with icons aligned on both sides at the top

<ion-header> <ion-navbar align-title="center" color="primary" hideBackButton> <ion-buttons ion-button icon-only start class="card-buttons"> <button class="card-buttons" (t ...

Angular2: Leveraging click events to manage CSS classes on elements

I am currently developing a web app using Angular 2. I'm facing an issue where the active element should receive an extra CSS class when clicked, but adding the ":active" CSS attribute with a custom class doesn't work as expected. The ":focus" pr ...

Is it possible to determine the type of a variable by simply clicking on it in IntelliJ (specifically in typescript)?

Having the ability to hover over a variable and see the expected type in TypeScript would be incredibly beneficial. I'm curious if there is some sort of internal static analysis being conducted that stores this information. Is there a method for acces ...

The utilization of 'new' is not allowed with an expression that does not have a call or construct signature in TypeScript

While searching for a solution, I stumbled upon this link which unfortunately did not provide the answer I was looking for: Cannot use 'new' with an expression whose type lacks a call or construct signature I am facing a similar issue. In my JavaS ...

Struggling to incorporate generics into a Typescript method without sacrificing the typing of object keys

Currently, I am working on a method in Typescript that is responsible for extracting allowable property types from an object of a constrained generic type. The scenario involves a type called ParticipantBase which consists of properties like first: string ...

What is the best way to pass createdDt and updatedDat values in an Angular form without displaying them on the template?

I am working on a message creation form in Angular where I need to include createdDt and updatedDt as hidden values. These values should not be visible in the template. I want to automatically set the current date and time for both createdDt and updatedD ...

Error: Attempting to access the 'pipe' property of an object that is undefined in the context of Jasmine and Angular

I'm encountering an issue with unit testing in Angular. Specifically, I'm getting a TypeError: Cannot read property 'pipe' of undefined when trying to test the output of an observable subscribe function. Any assistance on resolving this ...

Creating a dynamic multi-line list in Ionic application is a breeze!

I'm a beginner in Ionic and I am interested in creating a Multi-line List similar to how we generate list-views in Android with custom views and using an Array of custom objects programmatically. Can Multi-line Lists be generated with data from an Ar ...

Exploring the Differences Between Angular 2 Two-Way Data Binding and One-Way

My query pertains to the distinction between Angular 2's two-way and one-way data binding mechanisms. From my understanding, one-way data binding involves data flowing strictly from parent to child. However, this changes when the source of the binding ...

Having trouble with validation messages not displaying correctly in Angular after removing a value. What is the correct approach to fix this issue

I've encountered an issue with Angular validation where it's triggering validation on page load, which is not desired. Additionally, when I enter a value, the validation message disappears, but if I return to the textbox and delete the input, the ...

Angular Material Table with Pagination: Fetch data from server by triggering API call upon clicking on the "Next Page

When working with MatPaginator in conjunction with mat-table, what is the most effective approach for handling the next page click event? I have developed a custom DataSource with connect() and disconnect() functions. Is it necessary to explicitly manage ...

Error: Unable to use import statement outside of a module when deploying Firebase with TypeScript

Every time I try to run firebase deploy, an error pops up while parsing my function triggers. It specifically points to this line of code: import * as functions from 'firebase-functions'; at the beginning of my file. This is a new problem for me ...

What is the way to obtain loading start/end events while using a loader widget in conjunction with an asynchronous Firebase request (observable)?

I have implemented AngularFire in the following manner: constructor(private afDb: AngularFireDatabase) { allRRs$ = this.afDb.list(`research_reports-published/`).valueChanges(); } The variable allRRs$ is an observable that I am utilizing in my templat ...

Obtain the data from a different HTML element

When a user clicks on a button, I want to send the value of an input element in Angular2. What would be the most effective approach for achieving this? <input type="text" class="form-control" placeholder="Search for images..." /> <span class="i ...

Remember to store a reference of ViewChild within the parent class

In order to access my graph in a child component using @ViewChild, which is declared in the parent class, I am looking for a way to avoid having to create a new variable for each child class instance. Parent class: import { Component, ViewChild} from &ap ...

An error occurred with useState and localStorage: the parameter type 'string null' cannot be assigned to a parameter of type 'string'

I am currently using NextJS and attempting to persist a state using localStorage. Here is my code implementation: const [reportFavorite, setReportFavorite] = useState([ 'captura', 'software', 'upload', ] as any) ...

What is the best approach to creating customizable modules in Angular2?

I'm exploring the most effective approach to configuring modules in Angular 2. In Angular 1, this was typically achieved through providers. As providers have been altered significantly, what is the preferred method for passing configuration parameters ...

Error encountered: The input value does not correspond to any valid input type for the specified field in Prisma -Seed

When trying to run the seed command tsx prisma/seed.ts, it failed to create a post and returned an error. → 6 await prisma.habit.create( Validation failed for the query: Unable to match input value to any allowed input type for the field. Parse erro ...

Transforming JSON data into XML using Angular 7

It turns out the xml2js npm package I was using doesn't support converting JSON to XML, which is exactly what I need for my API that communicates with an application only accepting XML format. In my service file shipment.service.ts import { Injecta ...