This guide explores the most frequently used tools available for styling Angular components through the core directives ngClass and ngStyle.
This is the first installment of a two-part series on Angular Component Styling. If you want to understand Angular's style isolation and the Emulated View Encapsulation mode (the default setting), check out the second part:
Angular :host, :host-context, ::ng-deep - The Complete Guide.
We will cover the following topics:
- Component Styling with
ngClass- best use cases and alternatives ngClasswith Arrays, strings of classes, and configuration objects- Delegating style logic to component functions via
ngClass - Features of
ngStyle - Summary
To demonstrate each feature, we'll add multiple examples to this Angular CLI sample application, which uses a Bootstrap default theme for external styles.
Let's dive right into ngClass and its various usage patterns!
Component Styling using ngClass - when to use it?
Most styles are applied permanently and can simply be written as standard HTML in our templates, like this:
However, there are cases where styles are applied conditionally in our templates; they are added to an element only when a specific programmatic condition is met.
This is particularly relevant for state styles (using SMACSS terminology).
Is ngClass necessary for these cases?
Many state styles can be implemented natively using browser CSS pseudo-classes. Examples include:
- styles for identifying an element with the focus, via the
:focuspseudo-class - hover styles and on-click active state styles (using
:hoverand:active)
Whenever possible, it's better to use these native CSS pseudo-classes for such common state styles. So, for these specific cases, ngClass is not required.
Some good examples for the use of ngClass
However, many other state styles lack native browser support. These styles could include:
- styles for highlighting the currently selected elements in a list
- styles for highlighting the active menu entry in a navigation menu
- styles that identify a specific feature; for example, marking a new element on an e-commerce site
If an element only requires one state style, you can apply it directly using the plain input property template syntax without any extra directive:
Notice the syntax [class.btn-primary] that activates the btn-primary CSS class, effectively adding it to the button.
This expression adds the class to the element based on the truthiness of the expression, which is always true in this instance.
But more often than not, an element ends up with multiple state styles, and that is precisely when the
ngClassdirective becomes useful!
The ngClass directive accepts an expression that determines which state styles are applied to the styled element at any given time.
The expression passed to ngClass can be:
- an object
- an array
- a string
Let's examine each of these three cases with examples, and then explore how we can keep our templates clean and readable.
Passing an Array of CSS classes to ngClass
One way to define which classes should be active is to pass an array of strings to the ngClass directive.
For instance, the following expression contains an array of classes:
Angular will take the array passed to ngClass and apply the CSS classes it contains to the HTML button element. The resulting HTML would look like this:
Crucially, these CSS classes don't have to be hard-coded in the template with this syntax (it's just an example); we'll see more on this later.
Passing a String of CSS classes to ngClass
It's also possible to pass a string of CSS classes to ngClass to define which should be applied to a given element:
This syntax would yield identical results as before; the CSS classes btn and btn-primary would still be applied.
Passing a configuration object to ngClass
The last and most widely used method to configure ngClass is by passing it an object:
- the keys of the object represent the names of the CSS classes we intend to apply (or not)
- the values of the configuration object should be booleans (or expressions that evaluate to booleans) indicating whether the CSS class should be applied
Here's an example of how to use this syntax:
This example would produce the same outcomes as earlier: both CSS classes would be applied. However, as expressions grow longer or when handling multiple state classes, this syntax can quickly become difficult to read, overloading the template with excess logic.
Let's see how we can handle such scenarios!
Delegating to the component which styles should be applied
One of the component class's responsibilities is to:
- coordinate the link between the View definition (the template) and the data passed to the component (the Model)
- manage any other transient visual component state tied uniquely to the component (like a flag indicating if a collapsible panel is open)
If our ngClass expressions start to become unwieldy and hard to read, we can pass the output of a component method to ngClass instead:
Note that parameters can be passed to this method if required. Let's break down what happens in this example:
- The component includes a member variable
stateFlag, which indicates whether a given component state is active. - This could equally be an
enumor a calculation derived from input data. - The
calculateClassesmethod returns a configuration object equivalent to the one shown earlier. - The CSS class
btn-extra-classis conditionally added to the HTML button based on the value of thestateFlagvariable.
This time, however, the configuration object computation occurs within a component method, making the template more readable. This function could also return an array or string of CSS classes, and the example would continue to function correctly!
With native browser functionality and ngClass combined, we can handle most styling needs for our components.
But are there scenarios where applying styles directly to an element is necessary?
How to use ngStyle to add embedded styles to our templates
Similar to plain CSS, there are legitimate cases for applying styles directly to an HTML element, even though this is generally avoided.
Here's why: these embedded styles take precedence over all other CSS styles unless those styles are marked with !important.
As an example: imagine a color picker that sets the color of a sample rectangle based on a handle dragged by the user.
The element's variable color requires an embedded HTML style because it isn't known in advance. In Angular, we can address this scenario using the ngStyle built-in core directive:
The resulting HTML would look like this:
As with ngClass, when the ngStyle expression grows too large, we can delegate the computation to a component method that calculates the configuration object:
This covers how to conditionally add both CSS classes and embedded styles to our components!
However, another key Angular feature we haven't covered yet is isolating a component's styles to prevent interference with other page elements; that's the focus of part two in this series:
Angular :host, :host-context, ::ng-deep - The Complete Guide.
Summary
With so many styling options available using ngClass and ngStyle, it's important to know which one to use and why. Here's a brief recap:
- Most styles can be added directly to the HTML; for those cases, simply assign them to the class property without any special Angular functionality
- Many state styles can be implemented using browser-supported pseudo-class selectors like
:focus; it's wise to prefer these conventional solutions - for state styles lacking a corresponding pseudo-class selector,
ngClassis the best choice - when
ngClassexpressions get too large, consider moving the style calculations to the component class - only use
ngStylefor dynamically computed embedded styles; this should generally be a rare need
I hope this guide assists you in choosing the right approach for styling your components. If you have any questions, feel free to leave them in the comments below.
To stay updated on similar posts, we invite you to subscribe to our newsletter:
If you'd like to explore more Angular core features, we recommend the Angular Core Deep Dive course, where component styling is covered in greater detail.
For those just starting with Angular, have a look at the Angular for Beginners Course:
Other posts on Angular
Check out these other popular articles that might be interesting:
- Getting Started With Angular - Development Environment Best Practices With Yarn, the Angular CLI, Setup an IDE
- Why a Single Page Application, What are the Benefits ? What is a SPA ?
- Angular Smart Components vs Presentation Components: What's the Difference, When to Use Each and Why?
- Angular Router - How To Build a Navigation Menu with Bootstrap 4 and Nested Routes
- Angular Router - Extended Guided Tour, Avoid Common Pitfalls
- Angular Components - The Fundamentals
- How to build Angular apps using Observable Data Services - Pitfalls to avoid
- Introduction to Angular Forms - Template Driven vs Model Driven
- Angular ngFor - Learn all Features including trackBy, why is it not only for Arrays ?
- Angular Universal In Practice - How to build SEO Friendly Single Page Apps with Angular
- How does Angular Change Detection Really Work ?
