Cookie Consent

By clicking “Accept”, you agree to the storing of cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts. View our Privacy Policy for more information.

Why Angular - Why we choose to use Angular at SpotDraft

Why should you consider Angular for your next frontend project?

I have been a professional developer for 8 years now, the last 4 years have been focused on the front-end side of things primarily working with Angular.

Over that time, I have come to appreciate what Angular provides and through this article, I want to share with others who might be considering Angular for their next project.

Opinionated and Structured

  • Angular is a framework, not a library. This means that unlike React, there is an Angular way of doing most things that you would want to do in a typical web application.
  • Angular is opinionated, it was one of the first major frameworks to fully embrace TypeScript and uses Rxjs out of the box (more on that later). There is a pretty comprehensive style guide for inspiration too.

If you are working with a small/growing team having these conventions and securities enforced by the framework can be a good thing.

Batteries included

Since Angular is a framework, it comes with a ton of built-in utilities that you would have otherwise used third-party libraries for. Angular comes with an HttpClient, a Router and even a Dependency Injection mechanism

Directives

Angular Directives are little pieces of re-usable enhancements for custom components and existing DOM elements. While you can create your own directives, Angular comes with some good ones built-in

Conditional Rendering

ngIf

Conditional rendering of elements is one of the most common things one needs to do, to make this simpler, Angular has the *ngIf directive.


<!-- my-component.html -->

<app-loader *ngIf="isLoading" ; else loadingComplete> </app-loader>

<ng-template #loadingComplete>Loading Completed...</ng-template>

Note: You can use ngIf without else too.

ngSwitch

Too many conditions for your ngif? No worries, Angular has you covered, you can use the ngSwitch directive to toggle b/w various components.

It behaves just like a switch..case...default in JavaScript!


<!-- Example taken from -  https://angular.io/api/common/NgSwitch -->
<container-element [ngSwitch]="switch_expression">
  <!-- the same view can be shown in more than one case -->
  <some-element *ngSwitchCase="match_expression_1">...</some-element>
  <some-element *ngSwitchCase="match_expression_2">...</some-element>
  <some-other-element *ngSwitchCase="match_expression_3"
    >...</some-other-element
  >
  <!--default case when there are no matches -->
  <some-element *ngSwitchDefault>...</some-element>
</container-element>

ngClass & ngStyle

Applying classes/styles conditionally is one of the most common use-cases in applications. Angular comes with some helpers that make implementing this a peach.


class MyComponent {
  loading: boolean;
  active: boolean;
  width = "100px";
}


<!-- is-loading is applied when loading === true; is-active is applied when active is true -->
<div
  [ngClass]="{'is-loading': loading, 
                'is-active': active}"
  [ngStyle]="{width: width}"
></div>

There is an alternative, short-hand syntax too -


<div
  [class.is-loading]="loading"
  [class.is-active]="active"
  [style.width]="width"
></div>


Custom Directives

Angular has 2 kinds of directives, Attribute and Structural. You can create your own directives to enhance functionality or add styles. For example, at SpotDraft, we use a custom structural directive to hide elements based on feature flags.

Pipes

Pipes are a way to manipulate data before its rendered. Pipes are pure and memoized by default.


<!-- use percent pipe to convert a decimal to percentage -->
<!-- when perc==0.259; output '26%' -->
<span> {{ perc | percent }} </span>

<!-- Converts text(foo) to FOO -->
<span> {{ text | uppercase }} </span>

Pipes can be used to transform date formats, convert enums to their label values and even enhance data structures. Angular comes with some built-in pipes and users can create their own too.

Routing

Angular comes with a powerful Routing module and makes it very easy to implement Lazy Loading, authentication checks and pre-loading of data

Lazy Loading

Developers can easily implement route based lazy loading of modules.

Eg: We want to load the contracts module only when the user visits the /contracts path. We just need to add the following to our apps routing config.


[
  {
    path: "/contracts",
    loadChildren: () =>
      import("./contract/contract.module").then((m) => m.ContractModule),
  },
];

That’s pretty much it. You can read more about lazy loading modules here.

Guards

Guards are a way to guard routes. For example, you might have a route that only admins can access, that check can be made easily in the guard and the user maybe taken to a 403 page if they don’t have the right permission.

Guards are part of the route config.


[
  {
    path: "/contracts",
    loadChildren: () =>
      import("./contract/contract.module").then((m) => m.ContractModule),
    canActivate: [AdminGuard],
  },
];


AdminGuard

@Injectable({
  providedIn: "root",
})
export class AdminGuard implements CanActivate {
  constructor(private userService: UserService) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    return this.userService.isAdmin;
  }
}

Read more about Guards here


Resolvers

Expecting some data to be present before a route is opened or a component is initialized is a fairly common requirement. Resolvers allow you to decoratively handle these dependencies in route configs.

In our route config, we add a resolver like -


{
  path: "/contract/:id";
  resolve: {
    contract: ContractResolver;
  }
}

This is what a resolver looks like -


@Injectable({ providedIn: "root" })
export class ContractResolver implements Resolve<Contract> {
  constructor(private service: ContractService) {}

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<Contract> {
    return this.service.getContract(route.paramMap.get("id"));
  }
}

HTTP Client and Interceptors

Angular comes with its own HTTPClient so you don’t need a third party library to do the same. The HttpClient is based on Rxjs Observables and works really well with the AsyncPipe in your templates.

Most http calls to the backend would involve adding some authentication/authorization headers/tokens to the API call so that the backend can validate the request before processing further.

Angular has a way to have global handlers that can modify outgoing requests and listen to errors/responses. They are called Interceptors.

Forms

Forms are a very common part of client side development, and Angular comes with powerful forms functionality like 2-Way Binding, Reactive Forms, Built-in validators etc.

About Reactive Forms

Reactive Forms use an immutable approach to handling form state, whenever something changes, a new state is created. 

Reactive forms also use observables so you can subscribe to things like change in values either on a form level or an individual control. This allows you to create observable chains to implement required business logic which you can combine with other streams and do some powerful stuff!

One great feature that I like about Reactive Forms is the ability to group individual controls together and add a validator on the combined value of that group. 

For example, you have a classic password reset form where you want to validate that the value typed in both the new password field and confirm password field are the same, this can be done like - 


const form = this.formBuilder.group({
    newPassword: [],
    confirmPassword: []
}, {
     validators: [this.validateSamePassword] 
})

function validateSamePassword(group: FormGroup) {
    // Check both values here  
}

Angular also allows developers to create custom form elements.

Powerful CLI

angular-cli is the official way to initialize and manage your Angular project. It abstracts away Webpack and other tooling and provides many configurations and options like fileReplacements, style pre-processor etc.

Schematics

Schematics are like yeoman generators. They integrate with angular-cli and allow generation of code in one command which helps with maintaining standards and consistency. A developer can create their own schematics too.


# Creates files for the user component (template/style/controller)
# updates the corresponding ng-module
ng generate component user

Updates

Updating a framework/library is always a daunting task, specially on larger applications with many dependencies. Angular-cli aims to help here with the ng update command that not only works with the core packages, but also with packages like angular-material. Angular also publishes an interactive update guide that is super helpful

Dependency Injection

This is one thing that in my opinion makes Angular really powerful.

Angular heavily uses the concept of Dependency Injection. This means that you can write components and services that depend on certain other services or functions or even constants without actually creating those instances manually.

Use cases for dependency injection

  • In test environments, you could replace your HTTP API service with a mock service.
  • Create reusable components that rely on an abstract service but are oblivious to the actual implementation.

Lightweight State Management

Services in Angular are Singletons by default. This coupled with Rxjs BehaviorSubject/Subject can be easily used to do lightweight state management and create an event bus to share events throughout the app.

As your application grows, you might want to look into proper state management like Redux, NgRx, Akita etc. But using services to manage and expose state works with those solutions as well.

As I said earlier, Angular’s dependency injection is a really powerful system that allows you to manage complexity that comes with medium-large scale applications.

CSS and Style Encapsulation

Angular comes with support for CSS Bundling out of the box. You can also use pre-processors like SCSS, LESS or Stylus. This is a good thing as you don’t need to deal with Webpack configs and loaders and you can get to building straight away.

Another great thing you get for free with Angular is Style Encapsulation, this means that by default, a component’s style declarations are limited to that component and you don’t have to worry about them leaking into the global scope and affecting other components.

There are many libraries out there today that are trying to solve the problem of scoping of styles. Again, not having to choose one and this working out of the box is a big help.

Component styles, coupled with CSS Properties allows you to keep styles private and still override the things you want to be overwritten.

RxJS

Rxjs is a library that allows us to write reactive, asynchronous and event driven programs with ease. It's functional in nature and fairly powerful once you wrap your head around it.

Rxjs can seem a bit confusing at first, with its subscribes and operators, but, with time you learn to think in Rx and eventually (if you are like me) you might dread not having Rxjs on some other project.

In my opinion, event driven - reactive programming is ideal for client side development and hence Angular uses Observables extensively, whether it's the HTTP Client, Reacting to Router Events, Consuming path/query params etc.

Think about creating an auto-complete/suggestions UI. With Rxjs it's very easy to write expressive, functional code for the same.


// Search Component
class SearchComponent {
  // This will emit new values as the user types in the search field.
  searchInputSubject$ = new Subject<string>();

  suggestion$ = this.searchInputSubject$.pipe(
    // debounce for 100ms.
    debounceTime(100),
    // fetch the suggestions. By using switchMap
    // we ensure that if a new phrase comes in while
    // the old API call was still inflight, the old API will
    // be discarded and the new request will be subscribed too.
    switchMap((phrase) => {
      return this.searchService.getSuggestions(phrase);
    })
  );
}

Once you create the observable you wanted, you should use the async pipe to subscribe to it.


<div *ngFor="let suggestions of suggestions$ | async">
  <!-- render the suggestion -->
</div>

By using the async pipe, you can offload the lifecycle of the observable to Angular. Once the component is destroyed, Angular will clean up the subscription for you!

PS. If you are interested in exploring Rxjs more, I highly recommend this video by Ben Lesh

Other Goodness

While this article just scratches the surface of all good things in Angular, there are some goodies that I just want to include.

Keyboard Binding

Let's say you want to listen to Shift Enter and do something with it, you would do something like -


<textarea (keyup)="doSomething($event)"></textarea>

With Angular,


<textarea (keyup.shift.enter)="doSomething($event)"></textarea>

Style binding

Angular already provides an easy way to bind template properties to styles. But there’s still a case where your template property is a number. So you have to something like -


get widthStyle(){
  return `${this.width}px`
}

We can actually use a shorthand here -


<div [style.width.px]="width"></div>

First Class Worker Support

Angular also has first class support for dealing with Web Workers.


ng generate web-worker app

This command creates the files, updates the configs and existing code.

As I said, this article is a (very) high-level overview of Angular and its features, but I hope it helps you evaluate using it for your next project!

What could be better?

This article will be incomplete if we don’t talk about trade-offs, or things that I wish were better.

  • Local development - Angular’s Hot Module Replacement story is not the greatest, and while with v12 it's supposed to be better, I haven’t checked it out yet. Creating smaller NgModules is helpful, but it takes a bit more time than I would like.
  • Production builds are time consuming and resource intensive, our large(ish) app at SpotDraft takes about 30 minutes to build. However, this time has gone down as we have moved from Angular 6->9.
  • Using Vite/ES Build/Snowpack - The Angular compiler (ngc) is a drop-in replacement for the TypeScript compiler (tsc), due to some differences b/w the behaviour of tsc and ngc, tools like Snowpack/ES Build etc do not work out of the box. There are some ongoing discussions to make this better.
  • Lastly; the ecosystem. Compared to React, Angular’s ecosystem is still smaller. While most common use-cases are taken care of and there are some great libraries like Angular Material/Taiga UI etc, you might have some troubles finding libraries for niche use-cases. But this will only improve as more and more developers adopt Angular.

Come work with us!

Check out our open job postings.