In this tutorial, you learn how to use Bootstrap to style Angular CLI Apps.
The goal is to build an application layout with a header and footer, a home page, about page, and a login page with form.
Check the live demo here or the GitHub repo.
Requirements
Make sure you have the following tools installed:
- Node.js and NPM, visit the homepage for installation instructions.
- Run
node -v
to verify you have version 12 or higher. - Run
npm -v
to verify you have version 6 or higher.
- Run
- Angular CLI (
npm install -g @angular/cli@latest
to install)- Run
ng --version
to verify you have version 10 or higher.
- Run
If you prefer using yarn
, first configure Angular CLI's default package manager. This makes sure the generated application has a yarn.lock
file instead of a package-lock.json
.
1. Create a new application
Open a terminal and run the following command:
ng new sandbox --routing --style scss --strict
The ng new
command generates a basic Angular application in a directory called sandbox
and installs the dependencies.
The --routing
flag instructs Angular CLI to generate a routing module.
The --style scss
parameter sets SCSS as the style preprocessor.
The --strict
flag configures the application to run in strict mode.
At the end of the setup, the Angular CLI also initializes a git repository and does an initial commit.
2. Start the application in development mode
After the installation is finished, run the following command to navigate to the project directory.
cd sandbox
In the project directory you can start the development server using ng serve
:
ng serve
** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
Navigate to the link displayed by the Dev server and verify that it works. The app is now ready to get some style(s)! π
3. Install Bootstrap
Run the following command in your project directory to install Bootstrap:
npm install bootstrap
When the installation is finished we now can tell Angular CLI to use these styles.
Open src/styles.scss
and add the following lines:
@import '~bootstrap/scss/bootstrap';
// Set the full page height so we can stick the footer to the bottom
html,
body {
height: 100%;
}
Next, open src/app/app.component.html
and delete all the content, replace it with the following snippet:
<h1 class="text-center text-primary">Hello Bootstrap!</h1>
When you go back to your browser, you should see Hello Bootstrap
in big blue letters! π
Let's move on to make our app look a little better!
4. Configure the application layout
In this step we create a UiModule
and add three components to it: LayoutComponent
, HeaderComponent
and FooterComponent
.
π‘ It's a good idea to keep the UI separate from the rest of the app. This 'separation of concerns' also allows you to easily re-use the UI in other projects.
4.1 Create the UiModule
Run the following command to generate the UiModule
.
ng generate module ui --module app
The --module
parameter imports the UiModule
in our main AppModule
:
Next, generate the 3 components inside of this new module:
ng generate component ui/layout
ng generate component ui/header
ng generate component ui/footer
π‘ The
ng generate
command supports shortcuts: useng g c
to generate a component,ng g m
to generate a module, etc.
4.2 Implement the LayoutComponent
Open src/app/ui/layout/layout.component.html
replace the content with the following snippet:
<!-- This flex container takes the full height -->
<div class="d-flex flex-column h-100">
<app-header></app-header>
<!-- The main area does not shrink, 'pushing down' the footer -->
<main class="flex-shrink-0">
<!-- This will render the routes -->
<router-outlet></router-outlet>
</main>
<!-- This keeps the footer down if the main content does not fill up the space -->
<footer class="mt-auto">
<app-footer></app-footer>
</footer>
</div>
We use this LayoutComponent
in the router, and render the children in the location of router-outlet
.
Before moving on, make sure to import RouterModule
in UiModule
.
Open src/app/ui/ui.module.ts
and add the following code next to the other imports:
import { RouterModule } from '@angular/router';
Add RouterModule
to the imports
array:
@NgModule({
declarations: [LayoutComponent, HeaderComponent, FooterComponent],
imports: [CommonModule, RouterModule],
})
π‘ If you forget to import the
RouterModule
, the server will tell you:
ERROR in src/app/ui/layout/layout.component.html:3:3 - error NG8001: 'router-outlet' is not a known element:
1. If 'router-outlet' is an Angular component, then verify that it is part of this module.
2. If 'router-outlet' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.</pre>
4.3 Use the LayoutComponent
Open src/app/app-routing.module.ts
and replace the line const routes: Routes = []
with the following snippet:
const routes: Routes = [
{
path: '',
component: LayoutComponent,
children: [
// Here we will add our application pages
],
},
];
Make sure to import LayoutComponent
in src/app/app-routing.module.ts
:
import { LayoutComponent } from './ui/layout/layout.component';
Open src/app/app.component.html
and replace the content with the following snippet:
<router-outlet></router-outlet>
Save all the files and check your browser, you should see the default HeaderComponent
and FooterComponent
being rendered. Time to spice them up!
4.4 Implement the Header
Open src/app/ui/header/header.component.html
and replace the content with the following snippet:
<!-- You can change the values `dark` here with any of the following: -->
<!-- dark, light, primary, secondary, success, info, danger, warning -->
<nav class="navbar navbar-dark bg-dark">
<!-- This is the application title with a link to the root -->
<a class="navbar-brand" routerLinkActive="active" routerLink="/">Angular & Bootstrap</a>
<!-- This is a spacer so the links move to the end -->
<div class="mr-auto"></div>
<!-- This main navigation links are defined here -->
<div class="navbar-expand">
<div class="navbar-nav">
<!-- Each link has the routerLink property set to a different route -->
<a class="nav-item nav-link" routerLinkActive="active" routerLink="/home">Home</a>
<a class="nav-item nav-link" routerLinkActive="active" routerLink="/about">About</a>
<a class="nav-item nav-link" routerLinkActive="active" routerLink="/login">Login</a>
</div>
</div>
</nav>
Refer to the bootstrap documentation of the navbar for more details on the syntax of the navbar and how to make it responsive.
4.5 Implement the Footer
Open src/app/ui/footer/footer.component.html
and replace the content with this:
<div class="py-3 bg-dark text-center text-muted">
<small>Copyright © 2020</small>
</div>
5. Add application Pages
With the application layout in place, it's time to add a few pages.
The command we use creates a module with a component and lazy-loads it in the AppModule
.
π‘ Lazy loading is the recommended way of routing in an Angular app as it makes sure the users don't download any code they won't run.
5.1 Create a Home page
Run the following command to generate the HomeModule
:
ng g module pages/home --route home --module app
Open src/app/pages/home/home.component.html
and replace the content with this:
<div class="container py-5">
<div class="jumbotron">
<h1 class="text-secondary">Home</h1>
</div>
</div>
Go to your application in the browser and click the Home link in the header.
You will be taken to the route /home
with the text 'Home'. However, the layout with the header and footer is gone!
To fix this, open src/app/app-routing.module.ts
and move the newly created route inside the children
array:
const routes: Routes = [
{
path: '',
component: LayoutComponent,
children: [
// Here we will add our application pages
{
path: 'home',
loadChildren: () => import('./pages/home/home.module').then(m => m.HomeModule),
},
],
},
];
After saving this file, the page should render properly.
5.2 Create an About page
Run the following command to generate the AboutModule
:
ng g module pages/about --route about --module app
Open src/app/pages/about/about.component.html
and replace the content with this snippet:
<div class="container py-5">
<div class="jumbotron">
<h1 class="text-secondary">About</h1>
</div>
</div>
Open src/app/app-routing.module.ts
and move the about route inside the children
, so it sits next to the route to the HomeModule
.
5.3 Create a Login page
The login page is a bit more complex because it has a form and uses the router to redirect.
Run the following command to generate the LoginModule
:
ng g module pages/login --route login --module app
Open src/app/pages/login/login.component.ts
and add the following code next to the other imports:
import { FormControl, FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
Change the content of the LoginComponent
class to this snippet:
export class LoginComponent implements OnInit {
// The form group defines the fields used in the form
form = new FormGroup({
email: new FormControl(''),
password: new FormControl(''),
});
// Inject the router so we can navigate after a successful login
constructor(private readonly router: Router) {}
ngOnInit(): void {}
public submit() {
// Use the form value to do authentication
console.log(this.form.value);
// Navigate after successful login
return this.router.navigate(['/']);
}
}
Open src/app/pages/login/login.component.html
and replace the content with this snippet:
<!-- This flex container takes the full height and vertically centers the content -->
<div class="d-flex flex-column h-100 justify-content-center">
<div class="container">
<div class="row">
<!-- This is a single column that is responsive -->
<div class="col-12 col-md-6 offset-md-3">
<div class="card">
<div class="card-header">Login</div>
<div class="card-body">
<!-- The formGroup 'form' is defined in the component class -->
<form [formGroup]="form">
<div class="form-group">
<label for="email">Email address</label>
<!-- The formControlName defines the name of the field in the formGroup -->
<input id="email" formControlName="email" type="email" required class="form-control" />
</div>
<div class="form-group">
<label for="password">Password</label>
<input id="password" formControlName="password" type="password" required class="form-control" />
</div>
</form>
</div>
<div class="card-footer">
<div class="d-flex justify-content-between">
<a routerLink="/" class="ml-2 btn btn-outline-secondary">
Home
</a>
<!-- The button has a click handler, it will be disabled if the form is not valid -->
<button (click)="submit()" [disabled]="!form.valid" type="submit" class="btn btn-outline-success">
Log in
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
π‘ The template of the
LoginComponent
is quite large. When adding more authentication pages, consider creating a separateAuthLayoutComponent
and use that likeLayoutComponent
, then addLoginComponent
,RegisterComponent
, etc as the children ofAuthLayoutComponent
.
Go to your application in the browser and click the Login link in the header.
The login page renders the form in the center of the screen, and we don't need to add the route to the children
array.
There is one last thing to fix. If you click the Home link in the login page, you will be taken back to the root of the application, which is blank.
Wouldn't it be great if we could go to the Home page instead?
5.4 Redirect the root route
Open src/app/app-routing.module.ts
and add the following object on the top of the routes
array:
const routes: Routes = [
{
path: '',
// If this path is the 'full' match...
pathMatch: 'full',
// ...redirect to this route.
redirectTo: 'home',
},
// The other routes go here
];
Where to go from here?
As stated in the introduction, this app is a starting point and should be fairly straight-forward to enhance it to your liking.
Additional libraries
Use either ng-bootstrap or ngx-bootstrap if you want to use Angular implementations of Bootstrap components like dropdowns, tabs, collapse, etc. Both libraries are great options, pick the one you like best.
If your apps have a lot of forms, use formly for a declarative way of defining your forms, without writing any of the form templates.
Themes and colors
To tweak Bootstrap's appearance, open src/styles.scss
and set the variables. Make sure to define the variables before importing Bootstrap:
$dark: hotpink;
$jumbotron-bg: #333;
$secondary: limegreen;
$body-bg: #555;
@import '~bootstrap/scss/bootstrap';
Another great option is Bootswatch which offers more than 20 different Bootstrap based layouts
Conclusion
In this tutorial, you learned how to create a basic Angular application, and use Bootstrap to create a layout with header and footer.
The app has several pages that are lazy loaded. The Login page has a form with basic validation and a redirect back to the Home page.
In case you have any questions, feel free to leave a comment on DEV or send me a message on Twitter!
Big thanks to Jeffrey and Chau for reviewing this post!
Happy coding!
Cheers, beeman π
Top comments (4)
an error will occur when compiling the " login.component.html"...to fix this you need to add
import { FormsModule, ReactiveFormsModule} from '@angular/forms'; to the login module
Thanks, you're totally right - my bad!
I'll update the tutorial today!
No worries...thanks for the nice writeup!
If I want some route to use Bootstrap style and some other route to use not use Bootstrap, or use another css library. Is it possible? Any thoughts?