Skip to main content

Angular Workspace

Introduction

An Angular workspace is a development environment that allows you to manage multiple Angular projects (applications and libraries) within a single repository. This approach, often called a "monorepo" structure, is particularly useful for teams working on related applications that might share code, styles, or other resources.

In this tutorial, we'll explore what Angular workspaces are, how they work, and when you should consider using them for your projects. By the end, you'll understand how to create, configure, and manage an Angular workspace effectively.

What is an Angular Workspace?

An Angular workspace is essentially a folder structure with a set of configuration files that contains one or more Angular projects. Each project can be:

  • An application (something that runs in a browser)
  • A library (reusable code that can be shared across applications)

The key advantage of a workspace is that it creates a consistent environment where multiple related projects can be developed together, sharing code and configurations.

Workspace Structure

When you create a new Angular project using the Angular CLI, you're actually creating a workspace with a single application in it. The basic structure looks like this:

my-workspace/
├── node_modules/ # Shared dependencies
├── projects/ # Additional projects (apps, libraries)
├── src/ # Default application source code
├── angular.json # Workspace configuration
├── package.json # npm configuration
├── tsconfig.json # TypeScript configuration
└── ...other config files

Creating a New Workspace

Let's create a new workspace using the Angular CLI:

bash
ng new my-workspace --create-application=false

The --create-application=false flag tells the CLI to create a workspace without an initial application. This gives us a clean workspace where we can add multiple projects as needed.

After running this command, you'll have the following structure:

my-workspace/
├── node_modules/
├── .editorconfig
├── .gitignore
├── angular.json
├── package.json
├── package-lock.json
├── README.md
├── tsconfig.json
└── ...other config files

Adding Projects to a Workspace

Now that we have a workspace, let's add some projects to it:

Adding an Application

bash
cd my-workspace
ng generate application client-app

This creates an application called "client-app" in the workspace:

my-workspace/
├── projects/
│ └── client-app/ # Our new application
│ ├── src/
│ ├── e2e/
│ └── ...config files
├── ...other workspace files

Adding a Library

Libraries are an essential part of workspace management. They allow you to share code between applications:

bash
ng generate library shared-components

This creates a library project:

my-workspace/
├── projects/
│ ├── client-app/
│ └── shared-components/ # Our shared library
│ ├── src/
│ └── ...config files
├── ...other workspace files

Running Projects in a Workspace

To serve, build, or test a specific project in a workspace, you need to specify the project name:

bash
# Serve the client-app
ng serve client-app

# Build the client-app
ng build client-app

# Build the shared-components library
ng build shared-components

The angular.json File

The angular.json file is the heart of your workspace configuration. It defines all projects in the workspace and their settings. Here's a simplified example:

json
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"client-app": {
"projectType": "application",
"root": "projects/client-app",
"sourceRoot": "projects/client-app/src",
"prefix": "app",
"architect": {
"build": { /* build configuration */ },
"serve": { /* serve configuration */ },
"test": { /* test configuration */ }
}
},
"shared-components": {
"projectType": "library",
"root": "projects/shared-components",
"sourceRoot": "projects/shared-components/src",
"prefix": "lib",
"architect": {
"build": { /* build configuration */ },
"test": { /* test configuration */ }
}
}
}
}

Using Libraries in Applications

One of the main benefits of a workspace is the ability to share code between projects. Here's how to use your shared-components library in your client-app application:

  1. First, build the library:
bash
ng build shared-components
  1. Import and use components from the library in your application:
typescript
// In client-app/src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { SharedComponentsModule } from 'shared-components';

import { AppComponent } from './app.component';

@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
SharedComponentsModule // Import your library module
],
bootstrap: [AppComponent]
})
export class AppModule { }
typescript
// In client-app/src/app/app.component.ts
import { Component } from '@angular/core';

@Component({
selector: 'app-root',
template: `
<h1>My Application</h1>
<lib-shared-button>Click Me</lib-shared-button>
`
})
export class AppComponent { }

Real-World Example: Creating a Dashboard System

Let's consider a practical example: building a modular dashboard system with multiple applications sharing components.

First, create a workspace:

bash
ng new dashboard-system --create-application=false
cd dashboard-system

Next, add a UI library for shared components:

bash
ng generate library ui-components

Now, create a simple button component in the library:

typescript
// In projects/ui-components/src/lib/button/button.component.ts
import { Component, Input } from '@angular/core';

@Component({
selector: 'ui-button',
template: `
<button [class]="'ui-button ui-button-' + type">
<ng-content></ng-content>
</button>
`,
styles: [`
.ui-button {
padding: 8px 16px;
border-radius: 4px;
border: none;
cursor: pointer;
}
.ui-button-primary {
background-color: #007bff;
color: white;
}
.ui-button-secondary {
background-color: #6c757d;
color: white;
}
`]
})
export class ButtonComponent {
@Input() type: 'primary' | 'secondary' = 'primary';
}

Export it in the module:

typescript
// In projects/ui-components/src/lib/ui-components.module.ts
import { NgModule } from '@angular/core';
import { ButtonComponent } from './button/button.component';

@NgModule({
declarations: [ButtonComponent],
exports: [ButtonComponent]
})
export class UiComponentsModule { }

Update the public API:

typescript
// In projects/ui-components/src/public-api.ts
export * from './lib/ui-components.module';
export * from './lib/button/button.component';

Now, create two applications that will use this shared library:

bash
ng generate application admin-dashboard
ng generate application user-dashboard

Build the library:

bash
ng build ui-components

Use the library in both applications:

typescript
// In projects/admin-dashboard/src/app/app.module.ts
// and projects/user-dashboard/src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { UiComponentsModule } from 'ui-components';

import { AppComponent } from './app.component';

@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
UiComponentsModule
],
bootstrap: [AppComponent]
})
export class AppModule { }

And use the button component in both applications:

typescript
// In projects/admin-dashboard/src/app/app.component.ts
import { Component } from '@angular/core';

@Component({
selector: 'app-root',
template: `
<h1>Admin Dashboard</h1>
<ui-button type="primary">Admin Action</ui-button>
`
})
export class AppComponent { }
typescript
// In projects/user-dashboard/src/app/app.component.ts
import { Component } from '@angular/core';

@Component({
selector: 'app-root',
template: `
<h1>User Dashboard</h1>
<ui-button type="secondary">User Action</ui-button>
`
})
export class AppComponent { }

Now you can serve either application:

bash
ng serve admin-dashboard
# or
ng serve user-dashboard

This example demonstrates how a single UI component library can be shared across multiple applications within the same workspace, maintaining consistency while allowing for specialized implementations.

Workspace Configuration Best Practices

  1. Organize related projects together: Group projects that share code or are part of the same product family.

  2. Create focused libraries: Keep libraries focused on specific functionality (e.g., UI components, authentication, data models).

  3. Use path aliases: Configure TypeScript path aliases in tsconfig.json to make imports cleaner:

json
{
"compilerOptions": {
"paths": {
"@ui/*": ["projects/ui-components/src/lib/*"],
"@models/*": ["projects/data-models/src/lib/*"]
}
}
}
  1. Establish clear dependency direction: Libraries should not depend on applications; instead, applications should depend on libraries.

  2. Versioning: Consider using tools like Nx or Lerna to help with versioning in more complex workspaces.

Summary

Angular workspaces provide a powerful way to organize multiple related projects in a single repository. Key benefits include:

  • Code sharing: Easily share code between projects through libraries
  • Consistent configuration: Maintain consistent settings across projects
  • Efficient development: Test and build multiple projects together
  • Better organization: Logically group related projects

Workspaces are particularly valuable for teams building enterprise applications or product suites where multiple applications share common functionality, styles, or business logic.

Additional Resources

Exercises

  1. Create a workspace with two applications and a shared library for authentication.
  2. Build a theme library that can be used across multiple applications in a workspace.
  3. Configure TypeScript path aliases for cleaner imports in your workspace.
  4. Create a workspace with a main application and feature libraries for different sections of the application.
  5. Implement a state management solution that can be shared across applications in your workspace.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)