Ngx-bootstrap: Problem with Typeahead and Observable

Created on 14 Dec 2017  路  12Comments  路  Source: valor-software/ngx-bootstrap

I'm trying to copycat the example from demo site and I'm stuck with the Async sample. Everything works fine with plain array. However, with observable version of the same array, no drop down appears. I get no errors, the typeaheadLoading event and the mergeMap in the observable appear to work, just no dropdown what so ever. Here's my code:

Markup:

<pre class="card card-block card-header">Model: {{arraySelected | json}}</pre>
<input [(ngModel)]="arraySelected"
       [typeahead]="statesComplex"
       (typeaheadLoading)="changeTypeaheadLoading($event)"
       (typeaheadNoResults)="changeTypeaheadNoResults($event)"
       (typeaheadOnSelect)="typeaheadOnSelect($event)"
       [typeaheadOptionsLimit]="7"
       typeaheadOptionField="name"
       placeholder="Locations loaded from array - this works great!"
       class="form-control">

<pre class="card card-block card-header">Model: {{asyncSelected | json}}</pre>
<input [(ngModel)]="asyncSelected"
       [typeahead]="dataSource"
       (typeaheadLoading)="changeTypeaheadLoading($event)"
       (typeaheadNoResults)="changeTypeaheadNoResults($event)"
       (typeaheadOnSelect)="typeaheadOnSelect($event)"
       [typeaheadOptionsLimit]="7"
       typeaheadOptionField="name"
       placeholder="Locations loaded with timeout - no dropdown here!"
       class="form-control">
<div *ngIf="typeaheadLoading===true">Loading</div>
<div *ngIf="typeaheadNoResults===true">&#10060; No Results Found</div>

Component:

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { TypeaheadMatch } from 'ngx-bootstrap';

@Component({
    selector: 'app-home',
    templateUrl: './home.component.html'
})
export class HomeComponent {

    asyncSelected: string;
    arraySelected: string;

    typeaheadLoading: boolean;
    typeaheadNoResults: boolean;

    dataSource: Observable<any>;

    statesComplex: any[] = [
        { id: 1, name: 'Alabama', region: 'South' },
        { id: 2, name: 'Alaska', region: 'West' },
        { id: 4, name: 'Arkansas', region: 'South' },
        { id: 5, name: 'California', region: 'West' },
        { id: 6, name: 'Colorado', region: 'West' },
        { id: 7, name: 'Connecticut', region: 'Northeast' },
        { id: 50, name: 'Wisconsin', region: 'Midwest' },
        { id: 51, name: 'Wyoming', region: 'West' }
    ];

    constructor() {
        this.dataSource = Observable.create((observer: any) => {
            // Runs on every search
            observer.next(this.asyncSelected);
        }).mergeMap((token: string) => this.getStatesAsObservable(token));
    }

    getStatesAsObservable(token: string): Observable<any> {
        let query = new RegExp(token, 'ig');

        return Observable.of(
            this.statesComplex.filter((state: any) => {
                return query.test(state.name);
            })
        );
    }

    changeTypeaheadLoading(e: boolean): void {
        this.typeaheadLoading = e;
    }

    changeTypeaheadNoResults(e: boolean): void {
        this.typeaheadNoResults = e;
    }

    typeaheadOnSelect(e: TypeaheadMatch): void {
        console.log('Selected value: ', e.value);
    }
}

App module:

import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppRoutes } from "./application-routes";
import { BsDropdownModule, BsDatepickerModule, AccordionModule, TabsModule, TypeaheadModule } from 'ngx-bootstrap';
import { SplitPaneModule } from 'ng2-split-pane/lib/ng2-split-pane';
import { SharedModule } from './shared/shared.module';
import { SessionService } from "./services/session.service";
import { HttpService } from './services/http.service';
import { BlockUIService } from './services/blockui.service';
import { DocumentService } from './services/document.service';
import { CaseService } from './services/case.service';
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';

@NgModule({
    declarations: [
        AppComponent, 
        HomeComponent
    ],
    imports: [
        BrowserModule, FormsModule, HttpModule, RouterModule.forRoot(AppRoutes),
        PdfViewerModule,
        SplitPaneModule,
        SharedModule,
        BsDropdownModule.forRoot(),
        BsDatepickerModule.forRoot(),
        AccordionModule.forRoot(),
        TabsModule.forRoot(),
        TypeaheadModule.forRoot()
    ],
    providers: [
        { provide: LocationStrategy, useClass: PathLocationStrategy },
        HttpService,
        DocumentService, CaseService
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }

In package file:

  "dependencies": {
    "@angular/animations": "^4.0.0",
    "@angular/common": "^4.0.0",
    "@angular/compiler": "^4.0.0",
    "@angular/core": "^4.0.0",
    "@angular/forms": "^4.0.0",
    "@angular/http": "^4.0.0",
    "@angular/platform-browser": "^4.0.0",
    "@angular/platform-browser-dynamic": "^4.0.0",
    "@angular/router": "^4.0.0",
    "bootstrap": "^3.3.7",
    "core-js": "^2.5.1",
    "ng2-pdf-viewer": "^3.0.2",
    "ng2-split-pane": "^1.4.0",
    "ngx-bootstrap": "^1.9.3",
    "pdfjs-dist": "^2.0.195",
    "rxjs": "^5.5.2",
    "zone.js": "^0.8.14"
  },
  "devDependencies": {
    "@angular/cli": "^1.5.5",
    "@angular/compiler-cli": "^4.0.0",
    "@angular/language-service": "^4.0.0",
    "@types/jasmine": "~2.5.53",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "~6.0.60",
    "codelyzer": "^4.0.1",
    "jasmine-core": "~2.6.2",
    "jasmine-spec-reporter": "~4.1.0",
    "karma": "~1.7.0",
    "karma-chrome-launcher": "~2.1.1",
    "karma-cli": "~1.0.1",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.1.2",
    "ts-node": "~3.2.0",
    "tslint": "~4.5.0",
    "typescript": "~2.4.2"
  }

I tried this in IE11 and Chrome v63 with same result.

Not sure what is wrong. Is there anything I'm doing wrong?

P.S. I have tried it with Angular 5.0.0 and [email protected] with same result, then downgraded to Angular 4 & [email protected] all in vain

comp(typeahead) reproduce

Most helpful comment

After some digging I've managed to get it working.
The problem was that the following condition in the typeahead directive was failing:

https://github.com/valor-software/ngx-bootstrap/blob/951d38ef762ba4c8bafcaf99c81ea172fd8f7bfc/src/typeahead/typeahead.directive.ts#L169

The condition was always false because the previous this.typeahead instanceof Observable conditions were returning false.

I was importing the Observable like this:

import {Observable} from "rxjs";

and changed to:

import {Observable} from "rxjs/Observable";

Maybe it was picking some other instance of Observable because it looks like I have 2 implementations in:

  • node_modules/rxjs
  • node_modules/@ angular/cli/node_modules/rxjs

I am working with v5.5.6 of rxjs and angular 5.2.6

Maybe this information can help you.

All 12 comments

Hi! I don't see any errors in your code now. Could you create a reproduction in plunkr/stackblitz or provide a repo?
You can use one of starter templates:
Plunkr: https://plnkr.co/edit/0NipkZrnckZZROAcnjzB?p=preview
StackBlitz: https://stackblitz.com/edit/ngx-bootstrap?file=app%2Fapp.module.ts

I was able to wrap up a sample here: https://stackblitz.com/edit/ngx-bootstrap-xhckpu
And it appears to work on the same computer. I have carefully compared all package versions and I found that the solution only works with the following RxJS version:

"dependencies": { "@angular/animations": "^5.0.0", "@angular/common": "^5.0.0", "@angular/compiler": "^5.0.0", "@angular/core": "^5.0.0", "@angular/forms": "^5.0.0", "@angular/http": "^5.0.0", "@angular/platform-browser": "^5.0.0", "@angular/platform-browser-dynamic": "^5.0.0", "@angular/router": "^5.0.0", "bootstrap": "3.3.7", "core-js": "2.5.1", "ng2-split-pane": "^1.4.0", "ngx-bootstrap": "^2.0.0-rc.0", "rxjs": "5.4.2", <----- downgraded from ^5.5.2 "zone.js": "^0.8.14" },
The problem begins as soon as I upgrade RxJS to 5.5.2+.

Never mind, I figured that I was missing some rxjs dependencies. Works great with RxJS@^5.5.0

Sounds like this is still an issue. I had to downgrade RxJS to 5.4.* to get it to work.

I'm having the same issue. However even when downgrading RxJS to 5.4.* I'm unable to get it up-and-running. Which other dependencies did you add?

we stuch with rxjs 5.4.3 too, until issue with BehSubject is not fixed in rxjs(

Experiencing the same issue here with the latest angular/angular-cli project. Reverting to 5.4.3 (from 5.5.6) is not really an option as plenty of modules from '@angular/*: ^5.2.0' require at least 5.5.0.

I am having the same problem and with same versions that @vajnorcan mentioned. Is this really a rxjs issue?

I鈥檓 with you @mrafaelgomes . I鈥檓 not convinced that this is really an rxjs issue. Perhaps there were changes made to BhSbj implementation in 5.5, but that does not necessarily mean that the issue lies within rxjs and there is an intent to fix anything by the rxjs team.

@valorkin - if you are referring to any existing open issue with behavioral subject which you believe is affecting us, could you please direct us to bug report? Perhaps it could give us clues and/or workarounds had we known the exact nature of the issue?

After some digging I've managed to get it working.
The problem was that the following condition in the typeahead directive was failing:

https://github.com/valor-software/ngx-bootstrap/blob/951d38ef762ba4c8bafcaf99c81ea172fd8f7bfc/src/typeahead/typeahead.directive.ts#L169

The condition was always false because the previous this.typeahead instanceof Observable conditions were returning false.

I was importing the Observable like this:

import {Observable} from "rxjs";

and changed to:

import {Observable} from "rxjs/Observable";

Maybe it was picking some other instance of Observable because it looks like I have 2 implementations in:

  • node_modules/rxjs
  • node_modules/@ angular/cli/node_modules/rxjs

I am working with v5.5.6 of rxjs and angular 5.2.6

Maybe this information can help you.

I have experienced this problem as well, but since this library has moved forward to RXJS 6, we can use "isObservable" to check the typeahead property, I have tested it in an application where in one page the "this.typehead instanceof Observable" works as expected ..and in some other page of the same app..it just doesn't, using "isObservable" made it work consistently.

I'm sending a PR for this fix

If the Model is dynamic how can we observe like observer.next(this.asyncSelected); this ?Is there anyway to solve this

Was this page helpful?
0 / 5 - 0 ratings