简体   繁体   中英

ReferenceError: HTMLElement is not defined in TypeScript

I'm new to TypeScript, and frankly, I may be doing this wrong. But...I'm trying to create a "KeyUp Listener" for text-boxes that will "notify" when the user has stopped typing. However, when I try to register the class, I get the following error:

Exception: Call to Node module failed with error: Prerendering failed because of error: ReferenceError: HTMLElement is not defined

REGISTRATION:

// MODULES
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { UniversalModule } from 'angular2-universal';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

// COMPONENTS
import { AppComponent } from './components/app/app.component'
import { HeaderComponent } from './components/shared/components/shared-header.component';
import { SampleIndexComponent } from './components/samples/sample.index.component';
import { SampleFormComponent } from './components/samples/sample.form.component';
import { StatusFilterComponent } from './components/samples/filters/status-filter.component';
import { AuthorFilterComponent } from './components/samples/filters/author-filter.component';

// LISTENERS
import { TextboxKeyUpListener } from "./components/shared/services/textbox.keyup.listener";

@NgModule({
    bootstrap: [ AppComponent ],
    declarations: [
        AppComponent,
        HeaderComponent,
        SampleIndexComponent,
        StatusFilterComponent,
        AuthorFilterComponent,
        SampleFormComponent
    ],
    imports: [
        UniversalModule, // <-- Must be first import. This automatically imports BrowserModule, HttpModule, and JsonpModule too.
        FormsModule,
        ReactiveFormsModule,
        RouterModule.forRoot([
            { path: '', redirectTo: 'samples', pathMatch: 'full' },
            { path: 'samples', component: SampleIndexComponent },
            { path: 'form', component: SampleFormComponent },
            { path: '**', redirectTo: 'samples' }
        ])
    ],
    providers: [AuthorFilterNotifier, StatusFilterNotifier, TextboxKeyUpListener]
})
export class AppModule
{
    constructor() { }   
}

NOTIFIER LOOKS LIKE:

// MODULES
import { Injectable, Inject } from '@angular/core';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class TextboxKeyUpListener {

    // CONSTRUCTORS
    constructor(private textbox: HTMLInputElement)
    {
        this.init();
    }

    // PROPERTIES - private
    private typingTimer: NodeJS.Timer;
    private typingTimerInterval: number = 1000;
    private notifyDoneTyping = new Subject<string>();

    // PROPERTIES - public
    public notifyDoneTyping$ = this.notifyDoneTyping.asObservable();

    // METHODS - public
    private keyUp(ev: KeyboardEvent)
    {
        global.clearTimeout(this.typingTimer);
        if(this.textbox.value)
            this.typingTimer = global.setTimeout(this.notify, this.typingTimerInterval);
    }

    // METHODS - private
    private init()
    {
        this.textbox.onkeyup = this.keyUp;
    }

    private notify()
    {
        this.notifyDoneTyping.next(this.textbox.value);
        console.log("Done Typing")
    }
}

Some time ago I had a problem in having Typescript recognize some basic DOM classes, which has been solved adding

"lib": [
  "es2016",
  "dom"
]

to tsconfig.json file

I am not sure this can help your case, but it may be worth trying

==========================================================================

ADDITIONAL THOUGHTS TRIGGERED BY THE COMMENTS CHAIN - NOT RELATED TO THE ORIGINAL QUESTION

Use of debounce Rx operator to signal end-of-typing

If you need to signal end of typing in a simple and elegant way, you may want to consider to use Rx and the debounce operator. The idea is to create an Observable out of the event stream coming from the Front End element (in your case the Input element I guess) and use debounce to have an event emitted if no new value has been received from the input stream within a specified amount of time. You can look at these links for more details ( What does RxJS.Observable debounce do? , https://blog.thoughtram.io/angular/2016/01/06/taking-advantage-of-observables-in-angular2.html ) - many more examples are available online

Communication among components

I understand you have 2 components that need to communicate. Specifically the "listener" needs to tell the "other component" that typing is finished.

If the "other component" always contains the "listener" you may use @Output to define output events that the "listener" can emit and the "other component" can listen to.

If the "listener" is not contained by the "other component" , then the use of a service to communicate can be considered even if you have to also consider that services are Singletons (at least within their scope). Being a Singleton means that if you have more than one Input field you want to listen to for end-of-typing, than you need to figure out how to map the Singleton service with the many Inputs.

In case the UI is complex, you can consider to use a central store as per redux pattern (redux store pattern is available in Angular through https://github.com/ngrx/store or https://github.com/angular-redux/store - this is an interesting link that explains pros and cons of a redux-store based architecture)

Maybe this is actually an eslint and not a typescript error.

For me it solved completely by adding browser to the list of environments on .eslintrc.js .

{
  ...
  env:    {
    ...
    browser: true,
    ...
  },
  ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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