This repository contains a minimal Angular (v18) application scaffolded manually without using the Angular CLI generator, suitable for development and learning.
- Node.js 18 or later (recommend LTS)
- npm 9+
npm install
npm start
Then open your browser at http://localhost:4200/.
npm run build
The output will be in dist/angular-training.
- angular.json — Angular workspace and build configuration
- package.json — Dependencies and npm scripts
- tsconfig*.json — TypeScript configuration
- src/ — Application source code
- main.ts — Bootstraps the standalone AppComponent
- app/app.component.ts — Root component
- app/signals-demo.component.ts — Standalone component with a brief explanation and live demo of Angular Signals
- app/ng-content-demo/ — NgContentDemoModule with multiple components and a demo page explaining ng-content
- ng-content-demo.module.ts — Angular NgModule bundling demo components
- ng-content-demo-page.component.ts — Page that showcases usage of ng-content patterns
- parts/basic-slot.component.ts — Simple example
- parts/multi-slot-card.component.ts — Multiple slots via select
- parts/content-queries.component.ts — Using @ContentChild to detect projected content and show fallbacks
- app/ng-template-demo/ — NgTemplateDemoModule with a page explaining ng-template and how it differs from ng-content
- ng-template-demo.module.ts — Angular NgModule for the demo page (imports FormsModule and NgContentDemoModule)
- ng-template-demo-page.component.ts — Demonstrates ng-template basics, ngTemplateOutlet with context, *ngIf sugar, and contrasts with ng-content by reusing MultiSlotCardComponent
- index.html — Host page
- styles.css — Global styles
- assets/ — Static assets
This repository contains a minimal Angular (v18) application scaffolded manually without using the Angular CLI generator, suitable for development and learning.
- Node.js 18 or later (recommend LTS)
- npm 9+
npm install
npm start
Then open your browser at http://localhost:4200/.
- angular.json — Angular workspace and build configuration
- package.json — Dependencies and npm scripts
- tsconfig*.json — TypeScript configuration
- src/ — Application source code
- main.ts — Bootstraps the standalone AppComponent
- app/app.component.ts — Root component
- app/signals-demo.component.ts — Standalone component with a brief explanation and live demo of Angular Signals
- app/ng-content-demo/ — NgContentDemoModule with multiple components and a demo page explaining ng-content
- ng-content-demo.module.ts — Angular NgModule bundling demo components
- ng-content-demo-page.component.ts — Page that showcases usage of ng-content patterns
- parts/basic-slot.component.ts — Simple example
- parts/multi-slot-card.component.ts — Multiple slots via select
- parts/content-queries.component.ts — Using @ContentChild to detect projected content and show fallbacks
- app/ng-template-demo/ — NgTemplateDemoModule with a page explaining ng-template and how it differs from ng-content
- ng-template-demo.module.ts — Angular NgModule for the demo page (imports FormsModule and NgContentDemoModule)
- ng-template-demo-page.component.ts — Demonstrates ng-template basics, ngTemplateOutlet with context, *ngIf sugar, and contrasts with ng-content by reusing MultiSlotCardComponent
- index.html — Host page
- styles.css — Global styles
- assets/ — Static assets
You can create variables in Angular templates in a few different ways, depending on what you need them for. Here are the most common patterns with short examples.
- Template reference variables (DOM references)
- What: A reference to a DOM element, component, or directive instance in the template.
- How: Prefix the name with # on an element.
- Scope: Available in the element’s subtree. Example:
<input #nameInput placeholder="Your name">
<button (click)="greet(nameInput.value)">Greet</button>
- Variables from structural directive microsyntax
- What: Local variables exposed by directives like *ngFor, and aliases created with as.
- How: Use let and the directive’s built-in context variables. Examples:
<ul>
<li *ngFor="let item of items; let i = index; let first = first">
{{ i }} — {{ item }} — first: {{ first }}
</li>
</ul>
<!-- Alias the value of an expression -->
<div *ngIf="user as u">
Hello {{ u.name }}
</div>
- Variables inside ng-template via let- syntax
- What: Declare variables that receive values from the template context when the template is instantiated.
- How: Use let-name (for $implicit use let-name without =), or map names with let-name="contextProp". Example (this repo already demonstrates this in app/ng-template-demo/ng-template-demo-page.component.ts):
<ng-template #box let-msg="message">
<div>Template says: {{ msg }}</div>
</ng-template>
<!-- Provide context when rendering -->
<div [ngTemplateOutlet]="box" [ngTemplateOutletContext]="{ message: 'Hello from context!' }"></div>
- New Angular control flow (@if, @for, @switch) and @let (Angular v18)
- What: Modern template syntax that also lets you bind a value to a local variable using @let.
- How: Use @let name = expression; then reference name below it within the same block. Example:
@if (user) {
@let fullName = user.first + ' ' + user.last;
<p>Welcome, {{ fullName }}!</p>
}
Quick tips
- #var is for referencing elements/components.
- let varName and as are for structural directive contexts.
- ng-template lets you define and re-use chunks of template with context variables.
- With Angular v18+, @let is the easiest way to compute and reuse a value within a block.
See also
- src/app/ng-template-demo/ng-template-demo-page.component.ts — shows #box, let- variables, and passing context.
- Angular docs: Template reference variables, Structural directives microsyntax, and Angular control flow.
You pass data into an ng-template by supplying a context object when you instantiate the template, typically using ngTemplateOutlet. Inside the template, declare local variables with let- to read values from that context.
Basic pattern
<!-- Define a template that expects context -->
<ng-template #tpl let-msg="message">
<p>{{ msg }}</p>
</ng-template>
<!-- Instantiate it somewhere, providing context -->
<div [ngTemplateOutlet]="tpl"
[ngTemplateOutletContext]="{ message: 'Hello from context!' }"></div>
Using $implicit (shorthand)
<!-- If you bind $implicit, capture it with let-name (no = ) -->
<ng-template #userTpl let-user>
<p>User: {{ user.name }}</p>
</ng-template>
<div [ngTemplateOutlet]="userTpl"
[ngTemplateOutletContext]="{ $implicit: { name: 'Ana' } }"></div>
Multiple values in context
<ng-template #statsTpl let-user let-count="count">
<p>{{ user.name }} — visits: {{ count }}</p>
</ng-template>
<div [ngTemplateOutlet]="statsTpl"
[ngTemplateOutletContext]="{ $implicit: { name: 'Ben' }, count: 7 }"></div>
Notes and tips
- let-x binds the $implicit value; let-x="prop" binds a named property from the context object.
- The context only exists inside that template instance; it doesn’t mutate your component state.
- You can reuse the same template many times with different context objects.
- Structural directives (e.g., *ngIf, *ngFor) also use implicit ng-template and pass a context (e.g., *ngFor exposes index, first, etc.).
See it live in this repo
- src/app/ng-template-demo/ng-template-demo-page.component.ts — includes examples showing named context and $implicit with repeated instantiation.
- Use Standalone Components for feature isolation and simpler imports where appropriate.
- Prefer OnPush-like patterns (signals/computed or push pipe) and immutable data to reduce change detection work.
- Typed APIs: leverage strict TypeScript settings, typed forms, and explicit interfaces.
- Smart vs. Presentational: keep containers thin (data fetching/state) and components dumb (inputs/outputs) when composition grows.
- Signals and RxJS interop: use signals for local UI state and RxJS for async streams; convert between them thoughtfully.
- TrackBy in ngFor to avoid excessive DOM churn.
- Unsubscribe when using manual subscriptions (or use takeUntilDestroyed/AsyncPipe).
- Accessibility: semantic HTML, ARIA when needed, focus management for dialogs/menus.
- Routing structure: lazy-load features, route guards/resolvers where suitable.
- Folder structure: group by feature; colocate components, services, and tests.
- Use OnPush-like CD: prefer signals/computed or ChangeDetectionStrategy.OnPush to limit checks.
- Immutable updates: replace arrays/objects instead of mutating; helps trackBy and CD.
- trackBy for ngFor: provide a stable id-based trackBy to minimize DOM re-renders.
- Async data: prefer
asyncpipe ortoSignal()over manual subscribe; auto-unsubscribes. - Memoize derived values: use
computed()or pure pipes for expensive derivations. - Defer/lazy-load heavy features, routes, or components; use Angular
@deferwhere suitable. - Virtualize large lists and paginate server-side when data is big.
- Cache HTTP results where appropriate (e.g., shareReplay) and enable server/browser caching.
- Avoid global listeners: scope event handlers; use
passivelisteners for scroll/touch when safe. - Measure: use Angular DevTools, Performance tab, and profiler marks before/after changes.
- Don’t bind heavy functions directly in templates (e.g., sorting/filtering); precompute in component.
- Don’t mutate inputs or shared state; it breaks predictability and forces re-renders.
- Avoid needless subscriptions; prefer async pipe/signals, and always unsubscribe if you must subscribe.
- Avoid creating new objects in templates (e.g.,
[style]="{...}") each change detection tick. - Avoid huge component trees inside one view; split into smaller components with clear inputs/outputs.
- Don’t fetch on every change; debounce/throttle user input and cache results when valid.
- Don’t load everything upfront; lazy-load routes and heavy dependencies.
Keep it simple and measurable—optimize where it matters, guided by real profiling data.