One place for hosting & domains

      Angular

      All the Ways to Add CSS to Angular 2 Components

      Introduction

      Writing styles for large applications can be a really challenging task as styles get easily mixed up and confusing. The major issue is usually encountered when trying to structure your styles and give proper naming of individual styles.

      With time, patterns were introduced to enhance style organization and most of these patterns are implemented when we make use of pre-processors like Sass and Less. The significant thing about these patterns is that they suggest organizing our styles and templates in the form of COMPONENTS.

      Angular 2 is component-based which means that every UI functionality is built as a component. Therefore, as component-based styling is a recommended pattern, Angular 2 is just about to make writing styles a rather enjoyable experience. We will discuss different styling techniques and how to use them, but before that, we need to understand the concept of Shadow DOM and View Encapsulation.

      Shadow DOM is included in the Web Components standard by W3C. Shadow DOM basically allows a group of DOM implementations to be hidden inside a single element (which is the basic idea of components) and encapsulate styles to the element. This means that encapsulated styles will only be available for that group of DOM elements and nothing more.

      Abstraction with Shadow DOM

      Remember that the idea of web components and shadow DOM is relatively new and not all browsers can handle the concept. This is where one of the major advantages of Angular 2 comes in as it allows us to choose whether to implement Shadow DOM, just emulate it (default), or not use it at all. This technique of handling Shadow DOM in Angular 2 is known as View Encapsulation.

      The 3 states of view encapsulation are:

      • None: All elements are spit out – no Shadow DOM at all.
      • Emulated: This actually tries to emulate Shadow DOM to give us the feeling that we are scoping our styles. This is not a real Shadow DOM but a strategy to make all browsers smile at our code.
      • Native: This is the real deal as shadow DOM is completely enabled.

      Setting encapsulation is quite simple and is done right inside the @component decorator:

      @Component({
        templateUrl: 'card.html',
        styles: [`
          .card {
            height: 70px;
            width: 100px;
          }
        `],
        encapsulation: ViewEncapsulation.Native
        
        
      })
      

      Now that we have taken some time to put Shadow DOM and View Encapsulation straight, we can go ahead to understand the different techniques of styling an Angular component. Cards are common components that we are familiar with, so permit me to use them for the illustrations.

      Component Inline Styles

      This technique is the most obvious styling technique in Angular 2. This is because it is recommended, makes sense with the concept of components in mind and found everywhere in the Angular 2 documentation. It is implemented in the @Component decorator of our component class like so:

      @Component({
        templateUrl: 'card.html',
        styles: [`
          .card {
            height: 70px;
            width: 100px;
          }
        `],
      })
      

      The expected behavior in various view encapsulation techniques are:

      • None: The style is wrapped in a style tag and pushed to the head.
      • Emulated: The style is wrapped in a style tag, pushed to head, and uniquely identified so it can be matched with its component’s template. With that, the styles will be used for only the template in the same component.
      • Native: Behaves as expected of web components.

      External Stylesheets

      Just like our everyday method of including styles from external styles which have an extension of .css, we could also import external styles in an Angular 2 component. It is as simple as importing templates with the templateUrl property in @Component decorator.

      @Component({
        styleUrls: ['css/style.css'],
        templateUrl: 'card.html',
      })
      

      The expected behavior in various view encapsulation techniques are:

      • None: The style is wrapped in a style tag and pushed to the head. It is appended right after the component inline style.
      • Emulated: The style is wrapped in style tag, pushed to head, and uniquely identified so it can be matched with its component’s template just like the component inline style. As you can see, you must have guessed wrong if you expected the style to be imported with link
      • Native: Behaves as expected of web components.

      Template Inline Style

      This is achievable with two methods:

      1. The styles can be wrapped in a style tag and placed before the templates:
      @Component({
        template: `
          <style>
          h1 {
            color: purple;
          }
          </style>
          <h1>Styling Angular Components</h1>
          `
      })
      
      1. The style can be written as normal inline styles in the template tags:
      @Component({
        template: '<h1 style="color:pink">Styling Angular Components</h1>'
      })
      

      The expected behavior in various view encapsulation techniques are:

      • None: For method 1, the style is wrapped in a style tag and pushed to the head. It is appended right after the component inline and external styles. For method 2, the style just remains in the tag.
      • Emulated: For method 1, the style is wrapped in style tag, pushed to head, and uniquely identified so it can be matched with its component’s template just like the component inline style. For method 2, the style still remains in the tag.
      • Native: Behaves as expected of web components.

      This is the point where we need to pay attention as it can be quite tricky. If you have been following the article carefully, you will realize that component styles, if any, are always appended to the head first.

      Where it then becomes confusing is that in the first method of template inline styles are appended before the external styles. This makes external styles take precedence because in CSS the last is the greatest.

      Playing With the Demo

      To better understand priorities, I have created a Plunk with all the styling techniques we discussed. What I suggest is that you switch these styles, mess around with them and see the results. The comment section of this article is a great place to discuss your findings.

      Whatever method you choose is accepted and that is the good thing about components and Angular 2. You don’t have to listen to the preaching of not using internal styles or inline styles as they are within components and will be scoped. On the other hand, we are now able to organize our code better in a modular pattern.

      Angular 2 is awesome, right?

      Sort and Filter a Table Using Angular

      Introduction

      When building Angular applications, one of the cornerstones we will use is ng-repeat. Showing data is something that we do in applications like when we show a table of users or whatever other data we need to show our users. Today we’ll be looking at a way to sort and filter our tabular data. This is a common feature that is always useful so let’s look at what we’ll be building and dive right into the code.

      Here’s a quick demo: http://codepen.io/sevilayha/pen/AmFLE/

      Our application will allow us to:

      • Show a table of data (ng-repeat)
      • Sort by ascending or descending columns (orderBy)
      • Filter by using a search field (filter)

      These are three common functions in any application and Angular lets us implement these features in a very simple way. Let’s set up our sample application’s HTML and Angular parts and then look at how we can sort and filter.

      We’ll be using Bootstrap and Font Awesome to style our app. Let’s take a look at the Angular module first. This will be a simple module with one controller where we define a few variables and the list of data we’ll show to our users (we’ll be using sushi, yum). The files we will need are:

      Our demo is built inside of CodePen, so you can create the two files above or just work within your own CodePen. Here is the code for app.js

      
      angular.module('sortApp', [])
        .controller('mainController', function($scope) {
          $scope.sortType     = 'name'; 
          $scope.sortReverse  = false;  
          $scope.searchFish   = '';     
      
          
          $scope.sushi = [
            { name: 'Cali Roll', fish: 'Crab', tastiness: 2 },
            { name: 'Philly', fish: 'Tuna', tastiness: 4 },
            { name: 'Tiger', fish: 'Eel', tastiness: 7 },
            { name: 'Rainbow', fish: 'Variety', tastiness: 6 }
          ];
        });
      

      We have set the 3 variables and the list of sushi. Now let’s use this module in our HTML. Here is the HTML we’ll need for index.html:

      <div class="container" ng-app="sortApp" ng-controller="mainController">
        <div class="alert alert-info">
          <p>Sort Type: {{ sortType }}</p>
          <p>Sort Reverse: {{ sortReverse }}</p>
          <p>Search Query: {{ searchFish }}</p>
        </div>
      
        <table class="table table-bordered table-striped">
          <thead>
            <tr>
              <td>
                  Sushi Roll
                </a>
              </td>
              <td>
                Fish Type
                </a>
              </td>
              <td>
                Taste Level
                </a>
              </td>
            </tr>
          </thead>
          <tbody>
            <tr ng-repeat="roll in sushi">
              <td>{{ roll.name }}</td>
              <td>{{ roll.fish }}</td>
              <td>{{ roll.tastiness }}</td>
            </tr>
          </tbody>
        </table>
      </div>
      

      We are loading Bootstrap, Font Awesome, and Angular. We will also apply the Angular module named sortApp and the Angular controller called mainController to the tag.

      We are also using an ngRepeat to loop over the sushi in our $scope.sushi array we created in our Angular module.

      Great. We have the list of data displayed all nicely for our users. Now let’s offer them some functionality by letting them sort the table.

      We will be accomplishing this sorting feature using two of the variables that we created earlier ($scope.sortType and $scope.sortReverse). We will also be using the Angular orderBy filter. Basically, applying a combination of sortType and sortReverse variables to an orderBy clause in our ng-repeat will sort the table.

      <tr ng-repeat="roll in sushi | orderBy:sortType:sortReverse">
      

      That’s all we need to change the sort order of our ngRepeat. If you refresh your page, you’ll see that your list is sorted by name in normal order. Now go into your Angular module and change the sortType variable to $scope.sortType="fish" and refresh the page. You’ll now see the table sorted by Fish Type. The next step is to change the headings of our table so that they will change the sortType variable. That will automatically sort our table without refreshing the page (as is the Angular way).

      Making Table Headings Clickable

      We’ll be adding links to our table headings. Let’s look at the thead section of our site and use ng-click to adjust the sortType variable.

      <td>
        <a href="#" ng-click="sortType = 'name';">
          Sushi Roll
        </a>
      </td>
      

      Now as you click the links across your table headers, you’ll see your table sorted since we are setting the sortType variable using ng-click.

      Changing the Sort Order

      Next up, we’ll be adding a way to change the sort order so users can sort by ascending or descending. The orderBy filter arguments offer a third parameter for reverse. We just have to pass in true or false to change the sort order. Currently, we have it set to false since we defined that as one of the variables earlier ($scope.sortReverse). The way we will give users the option to reverse the sort is to add sortReverse = !sortReverse in the ng-click of our table headers. This will change the order if users click the link.

      <td>
        <a href="#" ng-click="sortType = 'name'; sortReverse = !sortReverse">
          Sushi Roll
        </a>
      </td>
      

      Just add that to all the other ng-clicks in the code as well. Now if you click your header links, you’ll see the sort order changing. This isn’t very intuitive right now though since we don’t provide any sort of visual feedback that the sort is changing. Let’s add carets to show up and down to represent our current sort order. We’ll add an up and down caret here and then use ngShow and ngHide to show and hide the caret based on order.

      <td>
        <a href="#" ng-click="sortType = 'name'; sortReverse = !sortReverse">
          Sushi Roll
          <span ng-show="sortType == 'name' && !sortReverse" class="fa fa-caret-down"></span>
          <span ng-show="sortType == 'name' && sortReverse" class="fa fa-caret-up"></span>
        </a>
      </td>
      

      Now the up arrow will only show if sortType is name and sortReverse is set to true. Applying this to the other headers will give you the same effect. With a few Angular directives, we are now able to show proper feedback for sorting and for sort order. The last part of this tutorial will deal with filtering the table of data.

      Filtering data in an ng-repeat is fairly easy since Angular also comes with the filter module. There are only two things we need to do here: create the form and apply the form variable to the ng-repeat.

      Let’s create the form first. Above the code for the table and below the code for the alert.

      <form>
        <div class="form-group">
          <div class="input-group">
            <div class="input-group-addon"><i class="fa fa-search"></i></div>
            <input type="text" class="form-control" placeholder="Search the Fish" ng-model="searchFish">
          </div>
        </div>
      </form>
      

      A lot of that is Bootstrap markup to style our form beautifully, but the line we need to pay attention to is the input. This is where we define our ng-model to adjust the searchFish variable. Now as we type into that input box, you should see that variable change in the alert box above. With that variable bound and ready to go, all we have to do is apply it to our ng-repeat.

      Just like that, our filter will now be applied to the table. Go ahead and type into your filter box and see the table data change. You’ll also notice that the orderBy and filter will work together to find you the exact sushi roll that you want.

      Using some built-in Angular tools like ngRepeat, orderBy, filter, ngClick, and ngShow, we’ve been able to build a clean and fast table of data. This is a great look at the power that Angular has in building some great functionality into your own applications without too much work.

      Further Reading

      For more Angular goodness, here are some other great articles:

      Responsive Equal Height with Angular Directive

      Introduction

      Let’s look at a very common use case. You have a list of items, you need to display all nicely on the screen in card form.

      Malaysia states

      It looks okay but you want all cards to always maintain the same height. It should always match the height of the tallest object, and resize accordingly when screen size changed, like this:

      always same height

      We can achieve this by using creating a custom directive.

      Interesting? Let’s code.

      GitHub: https://github.com/chybie/ng-musing/tree/master/src/app/same-height

      Let’s look at our main component.

      page-same-height.component.ts

      import { Component } from '@angular/core';
      
      @Component({
          selector: 'page-same-height',
          template: `
              <main class="container">
                  <h2>Malaysia States</h2>
      
                  <div class="row">
                      <div class="col-sm-4" *ngFor="let state of list">
                          <div class="card card-block">
                              <h4 class="card-title">{{ state.title }}</h4>
                              <p class="card-text">
                                  {{ state.content }}
                              </p>
                          </div>
                      </div>
                  </div>
              </main>
         `
      })
      export class PageSameHeightComponent {
          list = [
              {
                  title: 'Selangor',
                  content: 'Selangor is a state ....'
              },
              {
                  title: 'Kuala Lumpur',
                  content: 'Kuala Lumpur is the capital of Malaysia...'
              },
              {
                  title: 'Perak',
                  content: 'Perak is a state in the northwest of Peninsular Malaysia...'
              }
          ]
      }
      

      The code is pretty expressive itself. We have a list of states. We loop the list with *ngFor in our template and display each item accordingly.

      Please note that in this example, I am using Boostrap 4 to style the CSS, but it’s not necessary.

      There are a few ways to match the height. In this tutorial, we will match height by using the CSS class name.

      In our example, we want to match the height of all elements with class name card on the same row. Row is the parent and all Card are the children.

      To align all the children Card of the Row, let’s modify our HTML template and assign an attribute called myMatchHeight to the row and pass in card as the attribute value.

      page-same-height.component.ts

      ...
      
      @Component({
          selector: 'page-same-height',
          template: `
              <main class="container">
                  <h2>Malaysia States</h2>
      
                  <!-- Assign myMatchHeight here -->
                  <div class="row" myMatchHeight="card">
                      <div class="col-sm-4" *ngFor="let state of list">
                          <div class="card card-block">
      
      ...
      

      Now you might be wondering, where is the myMatchHeight coming from? That is the custom directive that we are going to build next!

      Let’s create our match height directive.

      match-height.directive.ts

      
      import {
          Directive, ElementRef, AfterViewChecked,
          Input, HostListener
      } from '@angular/core';
      
      @Directive({
          selector: '[myMatchHeight]'
      })
      export class MatchHeightDirective implements AfterViewChecked {
          
          @Input()
          myMatchHeight: string;
      
          constructor(private el: ElementRef) {
          }
      
          ngAfterViewChecked() {
              
          }
      
          matchHeight(parent: HTMLElement, className: string) {
              
          }
      }
      
      • We create the directive by using @Directive decorator. We specify [myMatchHeight] in the selector, this means that we use it as an attribute in any HTML tag. e.g. We use that in our main component.
      • We have an input myMatchHeight with the same name as our selector. By doing this, we can then use it like this myMatchHeight="some_value", some_value will then be assigned to myMatchHeight variable. e.g. We use that in our main component, we pass in card as the value.
      • Directive has a few lifecycle hooks that we can utilize. To understand more about lifecycle hooks, please refer to this Angular official documentation. In our case, we will do apply our match height magic during AfterViewChecked.
      • We will write all our logic in matchHeight function.

      How Should We Start?

      Let’s breakdown what should we do step by step:-

      1. We need to find all the child elements with the selected class name from the parent.
      2. We need to get all the child elements heights and find out the tallest.
      3. We need to update all the child elements to the tallest height.

      Let’s code it.

      match-height.directive.ts

      ...
          matchHeight(parent: HTMLElement, className: string) {
              
      
              if (!parent) return;
      
              
              const children = parent.getElementsByClassName(className);
      
              if (!children) return;
      
              
              const itemHeights = Array.from(children)
                  .map(x => x.getBoundingClientRect().height);
      
              
              const maxHeight = itemHeights.reduce((prev, curr) => {
                  return curr > prev ? curr : prev;
              }, 0);
      
              
              Array.from(children)
                  .forEach((x: HTMLElement) => x.style.height = `${maxHeight}px`);
          }
      ...
      

      I guess the code is very expressive itself.

      Apply the Magic

      Now that we have completed our match height logic, let’s use it.

      match-height.directive.ts

      ...
          ngAfterViewChecked() {
              
              this.matchHeight(this.el.nativeElement, this.myMatchHeight);
          }
      ...
      

      Remember to import the directive in your app module and add it in declarations.

      Refresh your browser and you should see all the cards are with same height!

      Now, let’s resize your browser. The card height is not adjusted automatically until you refresh the browser again. Let’s update our code.

      Listen to Window Resize event

      We need to listen to the window resize event and update the elements’ height.

      match-height.directive.ts

      ...
          @HostListener('window:resize')
          onResize() {
              
              this.matchHeight(this.el.nativeElement, this.myMatchHeight);
          }
      ...
      

      Always Reset all child elements’ height first

      Another problem you’ll see is that when you scale down your browser, the height will be updated accordingly (grow taller). However, when you scale up your browser, the height is not updated (not shrink down).

      Why is this happening? It is because once the card size grows taller, all content can fit in and no height adjustment is needed.

      To solve this, we need to reset the height of all elements before we recalculate the tallest height. We do this after step 1.

      match-height.directive.ts

      ...
          matchHeight(parent: HTMLElement, className: string) {
              
              const children = parent.getElementsByClassName(className);
      
              if (!children) return;
      
              
              Array.from(children).forEach((x: HTMLElement) => {
                  x.style.height = 'initial';
              });
      
              ...
          }
      ...
      

      That’s it. Remember that whenever we need to manipulate DOM element, it’s recommended that we do it in the directive. Creating a custom directive and listening to the custom event is easy with Angular Directive.

      Our directive works well with any element including nested components too. Check out the source code for another 2 more examples.

      GitHub: https://github.com/chybie/ng-musing/tree/master/src/app/same-height

      Happy coding!