Custom Theme for Angular Material Components Series: Part 3 — Apply Theme
Apply Theme to Angular Material and its different Components. 😎
👉 Update: This article was written for Angular v8
for Angular v9, please visit my article on DEV.to: Custom Theme for Angular Material Components Series: Part 3 — Apply Theme
for Angular v13, please visit my article: Angular Material Theming System: Complete Guide | Angular Material Dev (angular-material.dev)
Summary
This is third article of Custom Theme for Angular Material Components Series. In previous article, we understood how Angular Material’s and its components themes work. In this article we will proceed to modify default theme of components. Below is what we will be doing:
- Understand theme of
MatToolbar
- Apply
MatToolbar
’s theme toMatSidenav
andMatDialog
- Create a different theme for
MatSnackbar
and nice styling for different kind of notifications (default, info, success, warning, error)
1. Understand theme of MatToolbar
Now as you know, how each components theme is included in our application, let’s move ahead and see MatToolbar
’s theme, src/lib/toolbar/_toolbar-theme.scss
:
Look at line 31, they have created a mixin called mat-toolbar-theme
. In this mixin, first they are fetching all theme colors using palette functions. Then, default background and font-colors are assigned. For each themed toolbar, they are creating classes .mat-primary
, .mat-accent
and .mat-warn
. In those classes, they are calling a mixin _mat-toolbar_color
(which is defined at lines 6–9). That mixin is responsible for assigning background and font colors based on theme color.
You can go through each component’s theme file and see how they’re created. Ok, so now we know how MatToolbar
’s theme is created. Let’s apply it to MatSidenav
and MatDialog
.
2. Apply MatToolbar
’s theme to MatSidenav
and MatDialog
MatToolbar
As you know (if not read this), MatToolbar
has an attribute/property called color
that’s the reason, when we directly call color="primary"
(or [color]=”themeColor”
in our application) it applies the respective theme. But, color
attribute is not there for MatSidenav
. So, we would need to handle that.
Let’s open up our project and go to file: src/app/shared/components/sidenav/sidenav.component.html
:
To really check if MatSidenav
supports color
attribute or not, you can try by adding color="primary"
in line 5. You will nothing changed in the output. To actually see the error, change it to [color]=”themeColor”
. You will see the error in browser console: Error: Template parse errors: Can’t bind to ‘color’ since it isn’t a known property of ‘mat-sidenav’
. So, to assign themeColor
to color
attribute of MatSidenav
, we can do the Attribute Binding. Change the line 5 to below:
<mat-sidenav #sidenav mode="side" opened [attr.color]="themeColor">
</mat-sidenav>
You can set the value of an attribute directly with an attribute binding. You must use attribute binding when there is no element property to bind. You can read more about the same at Attribute Binding Guide.
As you would see, attribute biding won’t cause any errors, but as we haven’t done anything related to styling, it won’t make any difference to the output. Let’s do that.
For custom component theme, we will follow this naming convention: <component-name>.scss-theme.scss
. Let’s create a stylesheet at: src/app/shared/components/sidenav/sidenav.component.scss-theme.scss
:
As you can see, we are importing ~@angular/material/theming
, so that we can use some basic Material mixins, like map-get, mat-color and MatToolbar
’s colored mixin _mat-toolbar-color
. We are creating a mixin called sidenav-component-theme
to handle our SidenavComponent
’s theme. In our mixin, we are first fetching all theme colors using map-get
. Then, in class .mat-sidenav
, which is applied to <mat-sidenav>
tag, we are giving MatToolbar
default theme and then colored themes to color
attributes. Now, we need to include sidenav-component-theme
mixin somewhere.
Let’s include it in src/custom-component-themes.scss
:
Let’s run the project: ng serve -o
and you should see the output like below:
Hooray… 🎉🎉🎉 We have applied same theme as MatToolbar
to MatSidenav
.
MatDialog
Let’s apply the same to MatDialog
’s header. Create a component using terminal command: ng g c shared/components/dialog
.
Note that we have added [attr.color]=”data.themeColor”
in h2[mat-dialog-title]
. That will help us to apply dynamic theme to header.
Class file is pretty straight-forward. We are injecting data
using MAT_DIALOG_DATA
, this will be coming from where we make a function to open the dialog.
We will also create a theme file for dialog at : src/app/shared/components/dialog/dialog.component.scss-theme.scss
.
We have to make some adjustments to make mat-dialog-title
take some spacing, so that background color is nicely visible. Also note that we have created a class .custom-dialog
, we will use this when creating dialog.
We have to include this theme in our src/custom-component-themes.scss
:
...
@import './app/shared/components/dialog/dialog.component.scss-theme.scss';@mixin custom-components-theme($theme) {
...
@include dialog-component-theme($theme);
}
AppComponent
Let’s open MatDialog
from app.component.html
. Just change the content to:
<app-sidenav [themeColor]="themeColor">Main Content<button mat-raised-button [color]="themeColor" (click)="openDialog()">Open Dialog</button></app-sidenav>
As you can see, we will also need to create openDialog
function in class file, which looks like below:
themeColor = '';
constructor(public dialog: MatDialog) { }openDialog(): void {
const dialogRef = this.dialog.open(DialogComponent, {
panelClass: 'custom-dialog',
data: {
themeColor: this.themeColor
}
});
}
Note that, we have created themeColor
property and used the same in HTML and in creation of dialog. Also note the use of panelClass: 'custom-dialog'
, this class we have created in dialog theme file.
Let’s look at the output:
It’s looking great, isn’t it!!??
3. Create a different theme for MatSnackbar
and create nice styling for different kind of notifications (default, info, success, warning, error)
Let’s create a notification service, which will help us to call notifications from anywhere in the application:
ng g s shared/services/notification
We will change the content to below:
Note the use of panelClass
. For each type of notification, we are adding different panelClass
, which will help us in styling.
Next, create a style theme file: src\app\shared\services\notification.scss-theme.scss
:
Notice the use of $notifications-theme
. That would be a map of colors needed for notifications’ left borders. As you can see in the theme file, I have added relevant icon to indicate it’s importance. These icons are from Material Icons’ Codepoints, you can change if you wish.
Let’ create that map of colors in src/theme.scss
. Add below content:
// Theme for notifications / snackbar$notifications-theme : ( default: #FFF, info: mat-color(mat-palette($mat-blue), 400), success: mat-color(mat-palette($mat-green), 400), warning: mat-color(mat-palette($mat-yellow), 400), error: mat-color(mat-palette($mat-red), 400));
Of course, you can use colors of your choice. We will also need to export this theme to styles.scss
and eventually to custom-component-theme.scss
and notification.scss-theme.scss
. Let’s do that.
In styles.scss
, add our new theme in custom-component-theme
. If you are following me from part 1, you would need to change line 17 to this:
@include custom-components-theme($theming-material-components-theme, $notifications-theme);
Next, include notification theme in custom-component-theme.scss
:
...
@import './app/shared/services/notification.scss-theme.scss';
...
@mixin custom-components-theme($theme, $notifications-theme) {
...
@include notification-theme($notifications-theme);
}
Let’s open a notification on a button click from our app.component.html
:
<app-sidenav [themeColor]="themeColor"> Main Content
<br> <button mat-raised-button [color]="themeColor" (click)="openDialog()">Open Dialog</button>
<br> <button mat-raised-button [color]="themeColor" (click)="openNotification()">Open Default Notification</button></app-sidenav>
Modifications are required in app.component.ts
file, too:
import { NotificationService } from './shared/services/notification.service';
...
constructor(public dialog: MatDialog,
public notification: NotificationService){}
...
openNotification() {
this.notification.default('Default Notification');
}
...
If you change this.notification.default(…)
to info(…)
, success(…)
, warn(…)
or error(…)
, you would see respective outputs:
Great!!! And we’re done.
Thank You,
for reading this article. This was the last part of the series. You can visit Part One and Part Two on their respective links.
The final code is uploaded as part-2 at Custom-Theme-for-Angular-Material-Components-Series. I have also created a sample on stackblitz, if you wish to look it online.
And yes, always believe in yourself.