Skip to content

Training360/Angular-architect-nx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Angular-architect-nx

Architect-level Angular course about Nx workspaces

Base setup for Angular

npx nx@latest init or npx create-nx-workspace@latest angular-monorepo --preset=angular-monorepo

✔ Where should your workspace be created? · false

NX Let's create a new workspace [https://nx.dev/getting-started/intro]

✔ Where would you like to create your workspace? · airfield ✔ Which stack do you want to use? · angular ✔ Integrated monorepo, or standalone project? · integrated ✔ Application name · airfield ✔ Which bundler would you like to use? · esbuild ✔ Default stylesheet format · scss ✔ Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? · No

✔ Test runner to use for end to end (E2E) tests · playwright ✔ Enable distributed caching to make your CI faster · Yes

Angular code generation

code generation

Graph

npx nx graph

List all of commands

npx nx help npx nx list @nx/angular

CheetSheet

CheetSheet

Serving

  • npx nx serve --project
    or
  • npx nx serve

Building

  • npx nx build --project
    or
  • npx nx build

Nx - Environment variables

  • Documentation
  • npx nx@latest init create a new Angular app as standalone with webpack builder
  • Open the new project: code airfield-env -r
  • Install @types/node for using process.env variables
npm install -D @types/node
{
  "build": {
    // NOTE: change the executor to one that supports custom webpack config.
    "executor": "@nx/angular:webpack-browser",
    // snip
    "options": {
      // NOTE: This file needs to be created.
      "customWebpackConfig": {
        "path": "apps/{projectName}/webpack.config.js"
      }
      // snip
    }
  },
  "serve": {
    // NOTE: use dev-server that supports custom webpack config.
    "executor": "@nx/angular:dev-server"
    // snip
  }
}
const webpack = require("webpack");

function getClientEnvironment() {
  // Grab NX_* environment variables and prepare them to be injected
  // into the application via DefinePlugin in webpack configuration.
  const NX_APP = /^NX_/i;

  const raw = Object.keys(process.env)
    .filter((key) => NX_APP.test(key))
    .reduce((env, key) => {
      env[key] = process.env[key];
      return env;
    }, {});

  // Stringify all values so we can feed into webpack DefinePlugin
  return {
    "process.env": Object.keys(raw).reduce((env, key) => {
      env[key] = JSON.stringify(raw[key]);
      return env;
    }, {}),
  };
}

module.exports = (config, options, context) => {
  // Overwrite the mode set by Angular if the NODE_ENV is set
  config.mode = process.env.NODE_ENV || config.mode;
  config.plugins.push(new webpack.DefinePlugin(getClientEnvironment()));
  return config;
};
NX_API_URL=http://localhost:3333
NX_API_HOST=localhost
  • Update tsconfig.app.json and tsconfig.spec.json
"types": ["node"]
  • Get an environment variable
console.log(">>> NX_API_URL", process.env["NX_API_URL"]);
console.log(">>> NX_API_HOST", process.env["NX_API_HOST"]);
  • Generate a service:
  • npx nx g @nx/angular:service user --project=p-env
  • Use environment variables in the service. TADA

Module Federation ------------------------>

Create an empty workspace

npx create-nx-workspace@latest

  • Settings:
✔ Where would you like to create your workspace? · nx-mf
✔ Which stack do you want to use? · none
✔ Package-based monorepo, integrated monorepo, or standalone project? · integrated
✔ Enable distributed caching to make your CI faster · Yes

Install the framework specific plugin

  • code nx-mf
  • npm i -D @nx/angular

Generating host and remote applications

  • nx g @nx/angular:host --name=store --directory=apps/store --remotes=product,checkout

Serving the store app

  • npx nx serve-ssr store watching the store

  • npx nx serve-ssr store --devRemotes=checkout watching the checkout too

  • Server ports are in the corresponding project.json file!

Dynamic Federation ----------------------------->

  • npx create-nx-workspace ng-mf
  • Setup:
Need to install the following packages:
[email protected]
Ok to proceed? (y) y

 >  NX   Let's create a new workspace [https://nx.dev/getting-started/intro]

✔ Which stack do you want to use? · angular
✔ Integrated monorepo, or standalone project? · integrated
✔ Application name · angular-mf
✔ Which bundler would you like to use? · webpack
✔ Default stylesheet format · css
✔ Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? · No

✔ Test runner to use for end to end (E2E) tests · none
✔ Enable distributed caching to make your CI faster · Yes

Create the host end the remotes

  • code angular-mf
  • npx nx g @nx/angular:host dashboard
  • Setup:
npx nx g @nx/angular:host dashboard

>  NX  Generating @nx/angular:host

✔ Which stylesheet format would you like to use? · css
✔ Which E2E test runner would you like to use? · none
✔ What should be the project name and where should it be generated? · dashboard @ dashboard
import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
@Injectable({
  providedIn: "root",
})
export class UserService {
  private isUserLoggedIn = new BehaviorSubject(false);
  isUserLoggedIn$ = this.isUserLoggedIn.asObservable();
  checkCredentials(username: string, password: string) {
    if (username === "demo" && password === "demo") {
      this.isUserLoggedIn.next(true);
    }
  }
  logout() {
    this.isUserLoggedIn.next(false);
  }
}
export * from "./lib/user.service";

Login Application

import { CommonModule } from "@angular/common";
import { Component } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { UserService } from "shared/data-access-user/src/lib/user.service";
@Component({
  selector: "angular-mf-login-entry",
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `
    <div class="login-app">
      <form class="login-form" (ngSubmit)="login()">
        <label>
          Username:
          <input type="text" name="username" [(ngModel)]="username" />
        </label>
        <label>
          Password:
          <input type="password" name="password" [(ngModel)]="password" />
        </label>
        <button type="submit">Login</button>
      </form>
      <div *ngIf="isLoggedIn$ | async">User is logged in!</div>
    </div>
  `,
  styles: [
    `
      .login-app {
        width: 30vw;
        border: 2px dashed black;
        padding: 8px;
        margin: 0 auto;
      }
      .login-form {
        display: flex;
        align-items: center;
        flex-direction: column;
        margin: 0 auto;
        padding: 8px;
      }
      label {
        display: block;
      }
    `,
  ],
})
export class RemoteEntryComponent {
  username = "";
  password = "";
  isLoggedIn$ = this.userService.isUserLoggedIn$;
  constructor(private userService: UserService) {}
  login() {
    this.userService.checkCredentials(this.username, this.password);
  }
}

Dashboard Application

import { UserService } from "@angular-mf/data-access-user";

Converting the Dashboard to dynamic loading

  • Steps:
    • fetch the remote definitions
    • set the remote defs for Webpack
    • change loading method
{
  "login": "http://localhost:4201"
}
import { setRemoteDefinitions } from "@nx/angular/mf";

fetch("/assets/module-federation.manifest.json")
  .then((res) => res.json())
  .then((definitions) => setRemoteDefinitions(definitions))
  .then(() => import("./bootstrap").catch((err) => console.error(err)));
module.exports = {
  name: "dashboard",
  remotes: [],
};
{
    path: 'login',
    loadChildren: () =>
      loadRemoteModule('login', './Routes').then((m) => m.remoteRoutes),
  },
  • Run: npx nx serve dashboard --devRemotes=login

  • Watch the service from the dashboard:

import { UserService } from "@p-df/shared/data-access-user";
// ...
userService = inject(UserService);
isLoggedIn$ = this.userService.isUserLoggedIn$;
  • app.component.html:
@if (isLoggedIn$ | async) {
  <h2>User is logged in</h2>
}

Conclusion

  • Angular module federation is hard, but it's fun!

About

Architect-level Angular course about Nx workspaces

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages