Week 2: Angular CLI, Tooling & Project Anatomy

Every week from here on assumes you're comfortable with the CLI and can explain what's happening inside a generated project. This week is entirely about that — no framework concepts yet, just the tools you'll run dozens of times a day for the rest of the course.

Phase 1 of 7 Week 2 of 26 ~3 Hours Hands-on Exercise Included

By the end of this week, you'll be able to

  • Scaffold, run, build and analyze an Angular project from the command line
  • Explain what every file in a generated project is actually for
  • Set up an editor and browser DevTools that make the rest of the course faster

1. Installing the CLI & Creating Your First App

You don't need to permanently install the Angular CLI globally — npx always runs the right version for the command you're calling. Installing it globally is still convenient if you're generating projects often, so both are worth knowing.

terminal
# Run without installing anything globally
npx @angular/cli@latest new my-app

# Or install once, then use `ng` directly
npm install -g @angular/cli@latest
ng new my-app

ng new asks a handful of questions — stylesheet format, SSR, routing — and scaffolds a complete, runnable project. The flags below skip the prompts, which is worth doing once you know what you want:

terminal
ng new my-app --style=scss --ssr=false --routing=true
cd my-app
ng serve

ng serve starts a local dev server (default: http://localhost:4200) with live reload — save a file, the browser updates without a manual refresh.

2. The Build System: Vite & esbuild

Modern Angular projects use the @angular/build:application builder, which runs on esbuild for production bundling and Vite for the dev server. The practical effect: cold starts and rebuilds that used to take seconds now take a fraction of a second.

angular.json (excerpt)
{
  "projects": {
    "my-app": {
      "architect": {
        "build": {
          "builder": "@angular/build:application",
          "options": {
            "outputPath": "dist/my-app",
            "index": "src/index.html",
            "browser": "src/main.ts",
            "tsConfig": "tsconfig.app.json"
          }
        },
        "serve": {
          "builder": "@angular/build:dev-server"
        }
      }
    }
  }
}

You'll rarely hand-edit this — ng generate and ng add schematics update it for you — but knowing it exists demystifies what ng build and ng serve actually invoke.

Why this matters later

Week 20 (Performance) and Week 21 (SSR) both involve reading build output and bundle stats. Recognizing this config now means you won't be learning it cold under a deadline.

3. Project Anatomy — What Every File Does

A freshly generated app looks like this:

folder structure
my-app/
├── public/                 # Static assets copied as-is (favicon, images)
├── src/
│   ├── app/
│   │   ├── app.ts           # Root standalone component
│   │   ├── app.html
│   │   ├── app.css
│   │   ├── app.config.ts    # Application-wide providers (see Section 4)
│   │   └── app.routes.ts    # Route definitions
│   ├── index.html           # The single HTML shell the app mounts into
│   ├── main.ts               # Entry point — calls bootstrapApplication()
│   └── styles.css            # Global styles
├── angular.json               # Build/serve/test configuration
├── package.json
├── tsconfig.json              # Base TypeScript config
├── tsconfig.app.json           # Extends base, used for the app build
└── tsconfig.spec.json          # Extends base, used for tests

The only two files you'll touch on day one are app.ts and app.html. Everything else is configuration you'll grow into over the next few weeks — you'll meet app.config.ts properly in Week 3, and app.routes.ts in Week 10.

4. Workspaces, Multi-App & Shared Libraries

A single Angular workspace can contain more than one application, plus shared libraries they both depend on — useful for an admin app and a public app that share a UI kit, for example.

terminal
# Create an empty workspace (no default app)
ng new my-workspace --no-create-application
cd my-workspace

# Add two applications
ng generate application admin
ng generate application storefront

# Add a shared library
ng generate library ui-kit

Each application gets its own entry in angular.json under projects, and the library gets a TypeScript path mapping so both apps can import it directly:

tsconfig.json (excerpt)
{
  "compilerOptions": {
    "paths": {
      "ui-kit": ["projects/ui-kit/src/public-api.ts"]
    }
  }
}

5. Environment Configuration & Build Targets

angular.json supports named configurations (typically development and production) that swap build options — and optionally whole files — based on how you build.

terminal
# Uses the "production" configuration by default
ng build

# Explicit configuration
ng build --configuration=development
ng serve --configuration=production

The classic pattern for environment-specific values is a pair of files swapped at build time via fileReplacements:

src/environments/environment.ts
export const environment = {
  production: false,
  apiUrl: 'http://localhost:3000/api',
};

angular.json then replaces that file with environment.production.ts when you build with the production configuration — same import path in your code, different values at build time, with zero runtime cost.

6. Editor Setup & DevTools

Two tools are worth installing before Week 3:

  • Angular Language Service — the official VS Code extension. It gives you autocomplete, type-checking and go-to-definition inside your templates, not just your TypeScript files.
  • Angular DevTools — a Chrome/Edge extension that adds an "Angular" panel to DevTools, letting you inspect the live component tree, view signal/property values, and profile change detection.
You'll use both constantly

Angular DevTools' component tree view is how you'll inspect signal values in Week 5, and its profiler is exactly what Week 20 (Performance) uses to find real bottlenecks.

7. Hands-on Exercise

Hands-on

Scaffold a multi-app workspace with a shared library

Build the structure from Section 4 for real, then wire the library into both apps.

Requirements:

  1. Create an empty workspace named course-workspace (no default application).
  2. Generate two applications: admin and storefront.
  3. Generate a library named ui-kit, and add one exported component to it (a simple button is fine) via its public-api.ts.
  4. Import and render that component in the root component of both admin and storefront.
  5. Confirm ng serve admin and ng serve storefront both run (on different ports) and both show the shared component.
  6. Add an npm script to package.json that builds both applications in one command.
Hint

If the library import doesn't resolve, check tsconfig.json's paths entry — ng generate library should add it automatically, but it's worth confirming you can read what it wrote.

8. Knowledge Check

Four quick questions. Expand each to check your answer.

Q1

What's the practical difference between npx @angular/cli new and a global ng install?

npx downloads and runs the latest CLI for that one command without installing anything permanently — useful for occasional use or trying a new major version. A global install (npm install -g) gives you the shorter ng command everywhere, but you're responsible for updating it yourself.

Q2

Which two tools does the @angular/build:application builder use, and for what?

esbuild for production bundling (fast, minified output) and Vite for the dev server (fast cold starts and near-instant rebuilds during ng serve).

Q3

In a multi-app workspace, how does an app actually import code from a generated library?

Via a TypeScript path mapping in tsconfig.jsonng generate library adds an entry under compilerOptions.paths pointing at the library's public-api.ts, so any app in the workspace can import { Thing } from 'ui-kit' without a published npm package.

Q4

How does fileReplacements let the same import path resolve to different values in dev vs. production?

angular.json's production configuration swaps environment.ts for environment.production.ts at build time, before bundling even starts. Your code always imports ./environments/environment — the builder decides which physical file that path resolves to, so there's no runtime branching or extra bundle weight.