Adding autofocus to an input field in an Angular 5 Bootstrap 4 application


A couple of days back I faced trouble adding autofocus to an input field that was rendered inside a Bootstrap modal. The project is built using Angular 5 and Bootstrap 4. Also, I was using ng-bootstrap component library. The issue was that input field was auto focussed only the first time I opened the modal. In subsequent requests, the input field was not auto-focussed. It took me sometime to figure out why this is happening and how to fix the problem. In this quick post, I will walk you through how to enable autofocus in an input field in an Angular 5 Bootstrap 4 application.

Prerequisite

To follow along you will need following on your machine.

  1. Node.js : Download and install the latest version of node.
  2. Angular CLI: It is the official way to bootstrap Angular applications. You can install it using your favourite package manager. For npm, you can type npm install -g @angular/cli on your command-line. If you use yarn, you can type yarn global add @angular/cli on your command-line.

Now that we have installed all the prerequisites let’s get started.

Step 1: Create an Angular 5 application

We will use Angular CLI to create an Angular 5 application.

$ ng new modal-app

This will generate the sample project in the modal-app directory and install all the dependencies using npm package manager. You can run the application using ng serve command.

You can start the application by running following commands.

$ cd modal-app
$ npm start

Step 2: Install bootstrap 4, ng-bootstrap, and their dependencies

Now, that we have created the Angular 5 application we can install the dependencies that we need for this application. This includes bootstrap 4, ng-bootstrap, jQuery, and popper.js.

$ npm install bootstrap@4.0.0-beta.2 @ng-bootstrap/ng-bootstrap jquery popper.js --save

Step 3: Configure ng-bootstrap

Next, we will import NgbModule and declare it in the app.module.ts as shown below. In Angular every application has one application level module that bootstraps the application. The convention is to use AppModule as the name of the module class and app.module.ts as name of the file.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    NgbModule.forRoot()
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

As shown in the code snippet above, we added declaration of NgbModule in the imports section. imports defines all the dependencies of a module. The forRoot is a convention for modules that expose a singleton service.

Step 4: Add bootstrap to the project

If you see the application, you will notice that bootstrap styles are not currently applied. This is because we still have to add bootstrap related CSS and javascript files. To do that, replace the styles and scripts tags of the .angular-cli.json with the one shown below:

"styles": [
  "styles.css",
  "../node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
  "../node_modules/jquery/dist/jquery.min.js",
  "../node_modules/popper.js/dist/umd/popper.min.js",
  "../node_modules/bootstrap/dist/js/bootstrap.min.js"
],

Step 5: Add modal to the AppComponent

Update app.component.html with the one shown below.

<!--The content below is only a placeholder and can be replaced.-->
<div class="container">
  <div class="row">
    <div class="col-12">
      Open form
    </div>
  </div>
</div>

<ng-template #content l>
  <div class="modal-header">
    <h4 class="modal-title">Create User</h4>
  </div>
  <div class="modal-body">
    <div class="form-body">
      <div class="form-group row">
        Name: 
        <div class="col-md-9">

        </div>
      </div>
    </div>
  </div>
</ng-template>

Update app.component.ts with the one shown below.

import { Component } from '@angular/core';

import { NgbModal } from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  constructor(private modalService: NgbModal) { }
  open(content) {
    this.modalService.open(content).result.then((result) => {
      console.log(result);
    }, (reason) => {
      console.log('Closed with reason', reason);
    });
  }
}

Now, when you open the modal by pressing the Open form button, you will see that autofocus is only respected the first time you open the modal. On subsequent request, autofocus is not respected.

This happens because of the way autofocus is defined in the HTML 5 specification. The specification says

The autofocus content attribute allows the author to indicate that a control is to be focused as soon as the page is loaded, allowing the user to just start typing without having to manually focus the main control.

The autofocus attribute only works when page is loaded. The one time it works because it happens after the page load. When we press the button next time, browser does not respect autofocus value as page is already solution.

This is also a popular issue in bootstrap Github repository.

Step 6: Angular Directive to the rescue

Create a new directive using the command shown below.

$ ng g directive autofocus

It will generate the autofocus directive stub. Replace content of autofocus.directive.ts with the one shown below.

import { Directive, AfterViewInit, ElementRef } from '@angular/core';

@Directive({
  selector: '[appAutofocus]'
})
export class AutofocusDirective implements AfterViewInit {

  constructor(private el: ElementRef) {
  }

  ngAfterViewInit() {
    this.el.nativeElement.focus();
  }

}

Now, update the input tag to use this directive as shown below.

<input type="text" id="name" class="form-control" name="name" ngModel appAutofocus required>

Now, your app will correctly work with autofocus.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s