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 newwith--skip-install(in two versions),ng generate componentandng generate service,ng build(exit code 0, 1 for deliberate errors),ng test --no-watchwith Vitest (exit code 0, 2 tests, in the project of 0102) and a shortng servewith a proxy against a small HTTPS test server on port 7180 (curl: 200, withoutsecure: false502). The packages were installed by a reference project with a byte-identicalpackage.json. Not run: creating the project with the package installation in one step, the key pair fromdotnet 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):
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):
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.tsstarts withbootstrapApplication(App, appConfig).src/app/app.config.tsis the configuration at startup, a list ofproviders(the template setsprovideBrowserGlobalErrorListeners()andprovideRouter(routes)). HereprovideHttpClient(...)(0202) and sign-in (0301) are added.src/app/app.tsis the root component (imports: [RouterOutlet],selector: 'kl-root');app.htmlis a placeholder page of 20 KB that you replace in the exercise;app.routes.tsholds the routes (empty; 0201).angular.jsondescribes the targetsbuild,serve,test. The build uses@angular/buildwith the builderapplication;inlineStyleLanguageis set toscss.tsconfig.json,tsconfig.app.json(excludes*.spec.ts) andtsconfig.spec.json(vitest/globals) configure TypeScript.package.jsonlists scripts and dependencies, withoutzone.js(below);package-lock.jsonbelongs in the repository, and with itnpm cifetches the same packages everywhere.public/is delivered unchanged;src/index.htmlis the one page with<kl-root></kl-root>. There you immediately change<html lang="en">tolang="de"(screen readers then choose the language) and the<title>toKladde.
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:
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:
{
"/api/**": {
"target": "https://localhost:7180",
"secure": false
}
}
It is hooked in under the target serve of angular.json (next to configurations):
"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.
- Check
node --version(line 24) and create the project in thewebfolder with the command from the text. - In
src/index.html, changelangtodeand the<title>toKladde. - Replace
src/app/app.htmlwith<main><h1>Kladde</h1></main>and removeRouterOutletfromimportsinapp.ts(otherwise the build may report it as unused; not checked). - Create
proxy.conf.jsonand enterproxyConfiginangular.json. - Start the Kladde API (check the K2 state) and
npx ng serve. Openhttp://localhost:4200/andhttp://localhost:4200/api/contacts?page=1&size=20. Change the heading and watch the page reload. - Adapt
app.spec.ts(toContain('Kladde')instead ofHello, kladde-angular), thennpx ng buildandnpx ng test --no-watch, each with the exit code read. - The probe: add
protected laenge(text: string | undefined): number { return text.length; }toapp.tsand build. Then set"strict": falsein thetsconfig.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.jsonor/api/...ends in 404: check the path inangular.jsonand the JSON syntax, restartng 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: falseis 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
tsconfigfiles forstrict; 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.jsonandtsconfig.jsonand generate files withng generate. - You attach the dev server to
https://localhost:7180withproxy.conf.json;secure: falsebelongs 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
- Releases and support windows: major versions, active and long-term support.
- Versions: Node and TypeScript per Angular version.
- Dev server and proxy:
proxyConfigand the pattern/api/**. - Zoneless: the default since version 21 and what counts for it.
- Style Guide: file names,
protected,readonly,inject(). - tsconfig: strict: what the family of strict options switches on.
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).