This course puts an Angular front end in front of the Kladde API from K2. You know C#, ASP.NET Core, web APIs, signing in with Entra ID and tests (K2 2.2, 2.3, 2.6, 2.7, 2.12), and you learn Angular from zero, with the eye of the person who wrote the API. This lesson creates the project web/kladde-angular, shows what the CLI generates, attaches the dev server to the API and settles a question for the whole course: does strict have to appear in the tsconfig.json? The course does not teach TypeScript itself (K2 2.2).

State of the checks. The following was run with Node 24.22.3 and Angular CLI 22.2.7: ng new with --skip-install (in two versions), ng generate component and ng generate service, ng build (exit code 0, 1 for deliberate errors), ng test --no-watch with Vitest (exit code 0, 2 tests, in the project of 0102) and a short ng serve with a proxy against a small HTTPS test server on port 7180 (curl: 200, without secure: false 502). The packages were installed by a reference project with a byte-identical package.json. Not run: creating the project with the package installation in one step, the key pair from dotnet dev-certs, the real Kladde API, a browser, the test from exercise 1 after your change. Facts about versions come from angular.dev (read on the day of writing); what was not read is marked as open.

What Angular is for you

Angular is a framework with conventions: it brings tooling (CLI, build, test), a fixed project shape, dependency injection, a router, an HTTP client and forms. For a .NET developer this reads as familiar: services arrive through inject(), app.config.ts is the counterpart of Program.cs, and the CLI generates files like dotnet new. The price: you learn the conventions before you are fast. It pays off for a larger user interface with several people; for a small page, Blazor is often enough (K2 2.5).

An Angular program ends up as static files (HTML, JavaScript, CSS) that run in the browser and call the API over HTTP. Everything in the bundle is public; a secret never belongs in it.

Versions and support windows

On the day of reading, the releases page (angular.dev/reference/releases) names the major version 22 (@angular/core 22.2.6; the documentation says "Built at v22.2.6"). One major leap comes each year, and each version is active for 12 months and in long-term support (LTS) for 12 months. Version 21 is in long-term support, and that of 20 ends soon; the dates are on the page. For Angular 22, the versions page (angular.dev/reference/versions) names Node ^22.22.3 || ^24.15.0 || ^26.0.0 and TypeScript >=6.0.0 <6.1.0.

The course takes Node line 24. Check the version before every creation (the machine used for writing had 22.12.0 as its default, for which npm reports warnings; all runs here used 24.22.3):

PowerShell
node --version
npm --version

Do not confuse: K2 builds the API side with TypeScript 7 through NuGet, a different project. Here, npm is only Angular's tool.

The CLI and creating the project

The CLI is called ng: ng new creates, ng serve starts the dev server, ng build builds, ng generate generates files, ng test tests. From ng new --help (22.2.7) we read the options that matter to us: --routing, --style=scss (our rule), --prefix (the prefix of the selectors, default app), --strict (default on), --zoneless, --test-runner (vitest default, karma), --file-name-style-guide (2025 default with app.ts, 2016 with app.component.ts), --skip-git, --skip-install and --ai-config (none leaves out files for AI tools). In the web folder of the Kladde repository (which already has a Git repository):

PowerShell
npx @angular/cli@22 new kladde-angular --routing --style=scss --prefix=kl --skip-git --ai-config=none

While writing, the same command ran with version 22.2.7 and additionally --skip-install; it generated (without README.md, .editorconfig, .gitignore, .vscode/*, app.spec.ts, app.scss):

CREATE kladde-angular/.prettierrc (173 bytes)
CREATE kladde-angular/angular.json (2134 bytes)
CREATE kladde-angular/package.json (789 bytes)
CREATE kladde-angular/tsconfig.json (939 bytes)
CREATE kladde-angular/tsconfig.app.json (412 bytes)
CREATE kladde-angular/tsconfig.spec.json (423 bytes)
CREATE kladde-angular/src/main.ts (228 bytes)
CREATE kladde-angular/src/index.html (310 bytes)
CREATE kladde-angular/src/styles.scss (81 bytes)
CREATE kladde-angular/src/app/app.ts (309 bytes)
CREATE kladde-angular/src/app/app.html (20488 bytes)
CREATE kladde-angular/src/app/app.config.ts (322 bytes)
CREATE kladde-angular/src/app/app.routes.ts (80 bytes)
CREATE kladde-angular/public/favicon.ico (15086 bytes)

The names without .component follow the "Style Guide" page: App lives in app.ts, app.html, app.scss, without a suffix.

  • src/main.ts starts with bootstrapApplication(App, appConfig).
  • src/app/app.config.ts is the configuration at startup, a list of providers (the template sets provideBrowserGlobalErrorListeners() and provideRouter(routes)). Here provideHttpClient(...) (0202) and sign-in (0301) are added.
  • src/app/app.ts is the root component (imports: [RouterOutlet], selector: 'kl-root'); app.html is a placeholder page of 20 KB that you replace in the exercise; app.routes.ts holds the routes (empty; 0201).
  • angular.json describes the targets build, serve, test. The build uses @angular/build with the builder application; inlineStyleLanguage is set to scss.
  • tsconfig.json, tsconfig.app.json (excludes *.spec.ts) and tsconfig.spec.json (vitest/globals) configure TypeScript.
  • package.json lists scripts and dependencies, without zone.js (below); package-lock.json belongs in the repository, and with it npm ci fetches the same packages everywhere.
  • public/ is delivered unchanged; src/index.html is the one page with <kl-root></kl-root>. There you immediately change <html lang="en"> to lang="de" (screen readers then choose the language) and the <title> to Kladde.

The default test runner is Vitest; check runs: ng test --no-watch, read the exit code (0401).

Strict: does it belong in the tsconfig explicitly?

Our rule: TypeScript is strict. The generated tsconfig.json, however, contains no "strict": true; under compilerOptions there are noImplicitOverride, noPropertyAccessFromIndexSignature, noImplicitReturns and noFallthroughCasesInSwitch, and under angularCompilerOptions strictInjectionParameters and strictInputAccessModifiers. Is the project then not strict?

The experiment, in a copy with TypeScript 6.0.3: App got a method with text.length for text: string | undefined and a field leer: string = null. Four runs of ng build:

Run tsconfig.json Result
1 as generated, without strict Exit code 1: TS18048: 'text' is possibly 'undefined' and TS2322: Type 'null' is not assignable to type 'string'
2 with "strict": true Exit code 1, the same two errors
3 with "strict": false Exit code 0, the same code
4 as generated, template with [anzahl]="'drei'" for an input.required<number>() Exit code 1: TS2322: Type 'string' is not assignable to type 'number'

Result: With this template and TypeScript 6.0.3, the strict checking is on even without the entry, until "strict": false switches it off. The "tsconfig" page of typescriptlang.org gives the default for strict as true (read); it says nothing about a change in 6.0, so the observation holds for 6.0.3. ng new --no-strict writes "strict": false and "strictTemplates": false in the generated file.

Decision for the course: We do not additionally write "strict": true into it; it does not change the result. Instead, the probe from exercise 1 applies in every project of this course: the tsconfig files contain no "strict": false, and a deliberate error breaks the build. Anyone who inherits a project with --no-strict or switches the TypeScript version pins the rule down with one line.

SCSS and ng generate

--style=scss switches components (app.scss) and the global src/styles.scss to SCSS; this holds throughout the course. Files are generated with ng generate:

PowerShell
npx ng generate component kontakte/kontakt-liste
npx ng generate service kontakte/kontakt-daten
CREATE src/app/kontakte/kontakt-liste/kontakt-liste.spec.ts (597 bytes)
CREATE src/app/kontakte/kontakt-liste/kontakt-liste.ts (222 bytes)
CREATE src/app/kontakte/kontakt-liste/kontakt-liste.scss (0 bytes)
CREATE src/app/kontakte/kontakt-liste/kontakt-liste.html (29 bytes)

CREATE src/app/kontakte/kontakt-daten.spec.ts (366 bytes)
CREATE src/app/kontakte/kontakt-daten.ts (88 bytes)

The component gets the selector kl-kontakt-liste and the class KontaktListe, the service KontaktDaten gets the decorator @Service(). The "Creating and using services" page calls it a shorthand for @Injectable({ providedIn: 'root' }) and carries no note "new" or "preview" for it; we use what the CLI generates. The folders kontakte, termine, buchung, shared and core under src/app are fixed names of the Kladde.

Zoneless in one paragraph

The "Zoneless" page says: "Zoneless is the default in Angular v21+", without the library zone.js. That is why the template has no zone.js in package.json and no polyfills in angular.json. Angular checks views on notifications, according to the page roughly when a signal read in the template changes, an event listener in the template fires or markForCheck is called. Anyone who uses signals (0102) does not have to do anything else.

Dev server and proxy to the API

ng serve serves on http://localhost:4200, rebuilt on every change. The Kladde API runs on https://localhost:7180 (check the K2 state). If the browser calls port 7180 from port 4200, it demands CORS permissions (0302). In development you get around that with a proxy: the dev server accepts everything under /api and forwards it; the browser only sees port 4200. The file proxy.conf.json in the project folder:

JSON
{
  "/api/**": {
    "target": "https://localhost:7180",
    "secure": false
  }
}

It is hooked in under the target serve of angular.json (next to configurations):

JSON
"options": {
  "proxyConfig": "proxy.conf.json"
},

According to the "Serve" page, /api/** matches /api, /api/users and /api/users/123, but not /api-docs; changes to the file need a restart of ng serve. The page says nothing about the effect of secure. Observed: the test server on 7180 used a self-issued TLS key pair. With "secure": false, curl.exe http://localhost:4200/api/contacts answered with 200 and the JSON of the test server (via showed /api/contacts?page=1&size=20); without the line, the dev server answered with 502 Bad Gateway. How the key pair of dotnet dev-certs https behaves was not checked (open): if the proxy reports 502, secure: false is the first suspect.

Exercise 1

Create the project, start it, change it, prove strict.

  1. Check node --version (line 24) and create the project in the web folder with the command from the text.
  2. In src/index.html, change lang to de and the <title> to Kladde.
  3. Replace src/app/app.html with <main><h1>Kladde</h1></main> and remove RouterOutlet from imports in app.ts (otherwise the build may report it as unused; not checked).
  4. Create proxy.conf.json and enter proxyConfig in angular.json.
  5. Start the Kladde API (check the K2 state) and npx ng serve. Open http://localhost:4200/ and http://localhost:4200/api/contacts?page=1&size=20. Change the heading and watch the page reload.
  6. Adapt app.spec.ts (toContain('Kladde') instead of Hello, kladde-angular), then npx ng build and npx ng test --no-watch, each with the exit code read.
  7. The probe: add protected laenge(text: string | undefined): number { return text.length; } to app.ts and build. Then set "strict": false in the tsconfig.json, build once more and take both out again.

How to check: the page shows "Kladde"; the second call shows the JSON of the API (contacts, total, page, pageCount). Build and test end with 0. The probe: first build exit code 1 with TS18048, second exit code 0.

Solution 1

  • An error with proxy.conf.json or /api/... ends in 404: check the path in angular.json and the JSON syntax, restart ng serve.
  • The browser shows 502: the API is not running, listens on a different port, or the proxy does not like its key pair (secure: false is missing).
  • The test fails with Hello, kladde-angular: it still checks the text of the placeholder page (step 6).
  • The probe builds green the first time: search all three tsconfig files for strict; an entry in the app file overrides that of the main file. The project is then not strict.

What you can do now

  • You say what Angular offers an API developer, and you look up the version, Node range and support window on the page instead of guessing them.
  • You check Node 24, create web/kladde-angular, build, start and test it, and read the exit code each time.
  • You name main.ts, app.config.ts, app.ts, angular.json and tsconfig.json and generate files with ng generate.
  • You attach the dev server to https://localhost:7180 with proxy.conf.json; secure: false belongs only in development, and in delivery there is no proxy (0403).
  • You know that the template is strict even without an entry, and you prove it with a deliberate error.

Further reading

The Kladde: the scaffold web/kladde-angular stands with the prefix kl, SCSS, Vitest and a proxy to the API. Next you build the contact list from fixed data (0102).