Javascript is required
10 min read

How I Set Up A New Angular Project

How I Set Up A New Angular Project Image

I think Angular is the best choice for large enterprise applications. The basic project setup, which is generated using the Angular CLI is good, but I prefer another way to set up a new project. In this article, I want to talk about these topics:

  • Using Nx instead of the Angular CLI
  • TypeScript configuration
  • Internationalization
  • UI Component Explorer
  • Domain-driven Design for your models
  • Error Handling
  • Build Complex Components
  • Miscellaneous


Nx is not a replacement for the Angular CLI but uses the Angular CLI's power and enhances it with additional tools. Anything you can do with the Angular CLI can also be done with Nx, and you configure your project (as usual) with the angular.json configuration file.

I love Nx due to these facts:

  • I can easily integrate modern tools like Cypress, Jest and Prettier to my Angular project
  • I can use effective development practices which are pioneered at Google, Facebook, and Microsoft

Nx is an easy to use version of the powerful monorepo tools used at companies like Google.

Let us first talk about the usage of Cypress and Jest in Angular projects.

Why should I consider using Cypress instead of Protractor?

Check out this nice comparison to get more information about the differences between the two technologies.

Cypress is modern and interesting because it is not based on Selenium. Whereas Selenium executes remote commands through the network, Cypress runs in the same run-loop as your application. Additionally, it is fast and has nice features like:

  • Time travel
  • Debuggability
  • Real-time reloads
  • Automatic waiting
  • Spies, stubs and clocks
  • Network traffic control
  • Consistent results
  • Screenshots and videos

You can find further details about these features on the official feature website.

The most significant disadvantage of Cypress is, in my opinion, that it does not have full integration with tools like SauceLabs and BrowserStack and does not support other browsers than Chrome. This probably might change in the future, but these features are not available at the time of writing.

In my opinion, Cypress is not a perfect choice for every Angular project but I would recommend that you should give it a try and make your own decision.

Why should I consider using Jest instead of Karma/jasmine?

In my experience, the testing experience using Karma + jasmine is worse when the projects become bigger:

  • Slow build times (especially initially)
  • Recompiling does not work reliably
  • HTML reporter like karma-jasmine-html-reporter tend to be buggy

Jest was created by Facebook and is faster than other test runners because it is parallelizing tests. Additionally, it provides a CLI and has less configuration effort than other testing frameworks.

Some of the advantages of Jest compared to Karma + jasmine:

  • Tests run faster as it can execute tests without building the whole app
  • Using the CLI it is possible to filter by a filename or regex, which reduces the need for fdescribe
  • Nearly no configuration needed to get started
  • Stable tests
  • The syntax is similar to jasmine
  • Provides snapshot testing
  • More active community

I haven't used Jest in any of my Angular projects yet, but I will try it in one of my following Angular projects. The main reason why I haven't used it yet is that I worked on existing codebases with many jasmine tests, and there was no need/time/budget to migrate them to Jest. But I already used Jest in a Vue.js project and liked it.

If you are just annoyed with the verbose code produced by using Angular's TestBed API I would suggest trying Spectator, which allows us to write "readable, sleek and streamlined unit tests".

A summary of my testing suggestions:

  • Consider using Spectator instead of the TestBed API of Angular.
  • Consider using Jest instead of Karma/Jasmine (Migration is relatively easy)
  • Consider using ng-mocks to mock your component, directives, services, pipes, and more. Your unit tests should be pure and therefore isolated.
  • Consider using a functional component testing approach over the technical class testing approach. Test your component from the DOM and not the class. It will help if you think of user events instead of methods.

Effective Development Practices

Using Nx you can work in a "monorepo" way of building your application. This approach is used by large software companies like Google, Facebook, Twitter, and more to make it easier to work with multiple applications and libraries. These are some of the advantages of a monorepo approach:

  • You commit a working piece of software which may include multiple parts like frontend and backend
  • One toolchain setup
  • Dependency management is easier, e.g. all applications & libs in a Nx workspace share one package.json and can thus use the same Angular version
  • Code can be split into composable modules
  • Consistent developer experience

What I also like is the possibility to create applications and libraries in Nx, which provide an excellent way to structure larger applications:

  • An application is anything that can run in the browser or on the server. It's similar to a binary.
  • A library is a piece of code with a well-defined public API. A library can be imported into another library or application. You cannot run a library.

For example, we could define a TypeScript library that shares our TypeScript interfaces between our TS-based applications in our workspace. Of course, our workspace can contain applications that rely on different frontend (or backend) frameworks like React, Angular, NestJS, and even more.

One of my favorite features is the dependency graph which can show me a graphical representation of my workspace by running nx affected:dep-graph:

Angular Nx Dependency Graph

As we used affected, we can see what parts of our workspace are affected by our current changes (highlighted in red). This way, we can also run only tests or recompile code that was effected by our changes:

1nx affected:apps  # prints the apps affected by a PR
3nx affected:build  # reruns build for all the projects affected by a PR
5nx affected:test  # reruns unit tests for all the projects affected by a PR
7nx affected:e2e  # reruns e2e tests for all the projects affected by a PR
9nx affected --target=lint  # reruns any target (for instance lint) for projects affected by a PR

See the official documentation to learn how to use these mechanics in Nx.

TypeScript Configuration

I prefer to start with this tslint configuration as it uses the tslint configuration of Angular CLI and aligns with the Angular style guide.

In my tsconfig.json file I enable strictNullChecks which makes the code base more robust against possible null or undefined errors during runtime.

2  "compilerOptions": {
3    "strictNullChecks": true
4  }

From the official documentation:

In strict null checking mode, the null and undefined values are not in the domain of every type and are only assignable to themselves and any (the one exception being that undefined is also assignable to void).

Internationalization (i18n)

I configure internationalization from the beginning of a project even if the product is only planned for one country. It has two reasons:

  • You get used to storing your translated texts in one file and not as hardcoded strings across the whole application.
  • If the application needs to get translated into another language you are prepared for it.

I always use ngx-translate in my Angular projects, especially as it allows you to switch between languages during your application's runtime. This can be handy if you implement a language switcher in your app.

UI Component Explorer

If you develop your components, creating a custom view with all available components can be helpful, or using existing solutions like StoryBook.

In some projects, I created a separate page in the application (which was only visible to certain people) that showed a list of all available components. This page was used in manual testing sessions and provided a quick way to see if a new feature impacted any existing component. Additionally, it was possible to test the components in isolation.

Use Domain-driven Design for your models

One of the main ideas behind Domain-Driven Design is the separation of business logic (domain) from the rest of the application or implementation details. This can be easily implemented in Angular using TypeScript.

The goal of our domain model is to represent business logic. We want to avoid that certain business logic is split across multiple components and services but is available at a certain place. This way, we can easily react and change the logic if something in the business requirement has changed.

An example of such a domain model could look like this:

1export class User {
2  private firstName: string
3  private lastName: string
4  private age: number
6  get firstName() {
7    return this.firstName
8  }
10  get lastName() {
11    return this.lastName
12  }
14  get fullName() {
15    return `${this.firstName} ${this.lastName}`
16  }
18  get age() {
19    return this.age
20  }
22  constructor(firstName: string, lastName: string, age: number) {
23    this.setName(firstName, lastName)
24    this.setAge(age)
25  }
27  setName(firstName: string, lastName: string) {
28    if (this.validName(firstName) && this.validName(lastName)) {
29      this.firstName = firstName
30      this.lastName = lastName
31    }
32  }
34  setAge(age: number) {
35    if (age >= 18) {
36      this.age = age
37    } else {
38      throw new Error('User age must be greater than 18')
39    }
40  }
42  private validName(name: string) {
43    if (name.length > 0 && /^[a-zA-Z]+$/.test(name)) {
44      return true
45    } else {
46      throw new Error('Invalid name format')
47    }
48  }

If, for example, the minimum age should be changed from 18 to 16 this logic needs only to be changed in this domain model class.

This article provides further details and a good approach to handling server-side business logic in your frontend application.

Error Handling

I would always add a LoggerService and global error handler at the beginning of the project.

Additionally, try to use an error tracking software like Sentry to be able to monitor and fix crashes in real-time.

Example for a LoggerService: