Animating UI Elements in Angular #1
Using inkbars to highlight buttons, links and more
This article is the first of a series about animating UI elements in Angular extending the component set provided by AngularMaterial.
If you are familiar with Angular Material components you may have come across MatTab, a multi-tabs component to organize content enriched by a nice animating ‘inkbar’ smoothly shifting below the tabs to highlight the currently selected one.
Sometimes using the whole MatTab is simply too much, so, I created an InkbarComponent suitable to be used with regular buttons, links and basically all the kind of elements.
How to use it
Let’s start from the end taking a look on how to use InkbarComponent in a template:
In the template above the
wm-inkbar selector acts as a container of buttons where each button is turned into an ‘inkbar element’ by means of the
wmInkbarIf directive stating the condition under which the element has to be highlighted.
In the example, clicking a button sets the variable
filter with the relevant application specific value and the same variable is checked upon to verify the condition of the inkbar directive.
The last button shows how to hide the inkbar by calling the component’s
The Inkbar Component
The InkbarComponent component is pretty straight:
The template is just the transcluded content followed by a
div element acting as the colored bar to slide upon request thanks to the
So, basically, the
div moves to the specified
left/top position and stretches to the specified
width on every transition of the trigger.
The trigger is returned by the
animate() getter while the component implements just few methods:
activate()to compute the new inkbar position/size based on the ElementRef of the target.
update()to update the activation. This method is automatically called on every window:resize event.
clear()to hide the inkbar by zeroing its width while keeping the position for smoother transitions to follow.
The component is completed by a
color input mimicking the way of selecting among primary, accent and warn color palettes in Angular Material;
height input to define the height of the inkbar in pixels defaulting to 2px;
done output emitting the current position whenever the last animation completes.
You might have noticed the component is using ViewEncapsulation.None, so, the component styles are applied globally. This is a design choice to align with Angular Material, so, the component is actually benefiting from the very same theming strategy blending-in smoothly with all the other material components.
In order to define the global
wm-inkbar css class setting up the theming colors for our InkbarComponent, you’ll have to include the
wm-inkbar-theme() mixin right after the inclusion of the
angular-material-theme() in the global style.scss.
The Inkbar Directive
The inkbar directive is even simpler!
wmInkbarIf input simply calls the
activate() method of the parent InkbarComponent container whenever its condition is set to true.
In order to get the instance of the parent container, we simply inject the InkbarComponent into the directive constructor letting Angular doing the work for us.
How about Routing
Let’s imagine we’d like to use such a nice feature on router links too:
Routing is a big deal and we do not want to mess around, so, for the implementation of the RouterInkbarComponent component I went leveraging on the existing Angular’s RouterLinkActive directive.
The RouterInkbar Component
To handle routing events, the InkbarComponent has been extended into a RouterInkbarComponent:
Compared with the other, this component works the other way around by listing all the RouterLinkActive children activating the relevant one upon the router NavigationEnd event.
Is in the constructor that the component subscribes to the router navigation events filtering for the NavigationEnd and delaying the emission to make sure updating the inkbar position after the view has been completely updated.
If not, you’ll get the notorious “Expression has changed after it has been checked” exception from Angular while debugging.
The component activates the very first child link during the
AfterViewInit() lifecycle hook;
update() method is overridden to activate the currently active child link instead.
Try it Yourself
Feel free to reach me out at email@example.com for whatever question or curiosity.
A fully functional demo is also available on StackBlitz here: