Angular Workspace 🏭
Tags: angular, javascript, typescript, tutorial
Published: 2023-01-31 12:13:34

If you want to manage multiple Angular Sub-Projects in one Main Project then **<u>Angular Workspace</u>** (a.k.a `Mono-repo Pattern`) is what you are looking for.

#### _What's the use ?_ 🤔

> You can create **multiple projects** that share the same workspace instead of having one workspace for each project, that would mean having one `node_modules` folder across projects.


![How individual Angular Projects are different from Angular Workspace with Sub-Projects](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7feowimexie8ah60gyes.png)

***

#### _What are we going to accomplish❓❓_


![What are we going to accomplish at the end of this blog ?](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/owkd69gn96v79iynxnnh.png)


> An important note before we proceed any further, each **_Angular Application_** is an individual / independent [SPA](https://en.wikipedia.org/wiki/Single-page_application). 

That means `admin-app` / `ticketing-app` should be build & deployed as separate applications. In local, `ng serve admin-app` / `ng serve ticketing-app` should be used.

***

#### _Pre-Requisites_

- NodeJS
- Angular CLI
- Typescript

***

⏳ Let's ~~get our hands dirty~~ code

***

#### _Creating an empty workspace_

Create an Angular application with **NO** scaffolded files.


```javascript
ng new main-app --create-application=false
```

`main-app` serves as a place holder for other Sub-Projects, without any actual code in it.

#### _What value did `--create-application=false` add ?_

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/059zox9cckl2plperfw7.png)

> If `ng new main-app` is used, the `src` folder contains the source code for default app. And `angular.json` contains a property called `defaultProject` to customize our default project.

***

#### _Creating application(s)_

Creating 2 independent Angular application's within the `main-app` workspace

```javascript
ng generate application admin-app --routing=false --style=scss
```

```javascript
ng generate application ticketing-app --routing=false --style=scss
```



***

#### _Creating library_

Creating new library  within the `main-app` workspace :

```javascript
ng generate library logging-lib
```

> Use `ng generate --help` to know what other option(s) are available.

***

All `application(s)` and `libraries` created inside the workspace will be added under `projects` folder, by default. Which can be overridden during the creation process :

```javascript
ng generate application ticketing-app --project-root=custom-proj-root
```

***

<u>Below are the changes added to `main-app` workspace :</u>


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/75qm9lso825c3akq41ob.png)


1. `angular.json` file will have 3 project definition(s)
   * `admin-app` & `ticketing-app` with "projectType" as **application**
   * `logging-lib` with "projectType" as **library**
1. Main workspace package.json will have new devDependency added `ng-packagr`
1. `tsconfig.json` will have new entry **paths**, pointing to dist or build destination of library .


> When we import an entity (component / service) from `library`, here it is `logging-lib`, inside applications like `admin-app` or `ticketing-app`, Angular will look up for requested imports within the **application dir** if not found then will look into **build destination** mapped to **paths** in `tsconfig.json`


```javascript
{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "logging-lib": [
        "dist/logging-lib"
      ]
    }
  }
}
```


***

With the help of Angular CLI we have added a `workspace`, a `library` & 2 Single Page `application(s)`

***

Now lets take a look at some important files under `projects/logging-lib`

```
main-app
├── projects/
│   └── logging-lib/
│       ├── src/
│       │   ├── lib/                     # (1)
│       │   │   ├── logging-lib.component.ts
│       │   │   ├── logging-lib.module.ts
│       │   │   └── logging-lib.service.ts
│       │   └── public-api.ts            # (2)
│       ├── ng-package.json              # (3)
│       └── package.json                 # (4)
├── angular.json
├── package.json
└── tsconfig.json
```

Now let's go into details of some files:

* **Library Code (1)**
This folders contains the code of the library, currently a module, component and service. This is the main and only entry point of our library right now.

* **Public API (2)**
The file contains the public API of the library. It exports all members that should be available to the outside world. That's the Angular Module, component and service inside the src/lib/ folder.

* **ng-package.json (3)**
The file contains the configuration for ng-packagr. It specifies the path of the build output and the entry file which points to public-api.ts.

* **package.json (4)**
This is the package.json of your library (not to be confused with the package.json of our Angular workspace in the root folder). Here you specify the name, version and dependencies of your library.

***

Our objective is to create `library` & use it across `application(s)`

As the name indicates we shall create a simple logger service & try to use it across `application(s)`

<u>New library structure after cleaning up :</u>

```
main-app
├── projects/
│   └── logging-lib/
│       ├── src/
│       │   ├── lib/                     
│       │   │   ├── logger.service.ts
│       │   │   └── logging-lib.module.ts
│       │   └── public-api.ts
│       ├── ng-package.json
│       └── package.json
├── angular.json
├── package.json
└── tsconfig.json
```



```javascript
// File Name : logger.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class LoggerService {

  constructor() { }

  log(value: string) {
    console.log(`Logging from Logger Service`);
    console.log(value);
  }
}

```

```javascript
// File Name : public-api.ts
/*
 * Public API Surface of logging-lib
 */

export * from './lib/logger-service';
export * from './lib/logging-lib.module';

```

Lets build `logging-lib` project, using below command :

```javascript
ng build logging-lib --watch

```

If the build is through without any errors, we can use our `library` service inside application(s).

File Structure after `library build` :

```
main-app
├── dist/logging-lib
├── node_modules/
├── projects/
├── angular.json
├── package.json
└── tsconfig.json
```

***

One super helpful command that i found in Angular CLI is to use `generate` command with `--dry-run` flag.

```javascript
ng generate component <path-to-create-component> --project admin-app --dry-run
```

```javascript
ng generate service <path-to-create-service> --project logging-lib --dry-run
```

> **--dry-run** flag will not make any changes to file structure but will show where the new generated files are going to be added.

***

Now let's make final change to consume our `library` service inside `application`


```
main-app
├── projects/
│   └── admin-app/
│       ├── src/
│       │   ├── app/                     
│       │   │   ├── app.component.ts
├── angular.json
├── package.json
└── tsconfig.json
```

```javascript
// File Name : app.component.ts

import { Component, OnInit } from '@angular/core';
import { LoggerService } from 'logging-lib';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit{
  title = 'admin-app';

  constructor(private loggerService: LoggerService) {}
  
  ngOnInit(): void {
    this.loggerService.log(this.title);
  }

}

```

```javascript
ng serve admin-app

```

***

If you are still around, you should see following output in Browser Console :


![Final output of using Logger Lib Service in Application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/elk5icssthlq2nhq9iuu.png)

***

#### _Key Takeaways_ 📝

1. Creating Angular Workspace with `Applications` & `Library`
2. Sharing `Library` code with other `Applications`

> If you are thinking of sharing one Application code in another, always use `Library` or `Integration Application` for that.