Marcin Jahn | Dev Notebook
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects

Components

Usually we have the AppComponent, which is the root of the app. Each component is split into 4 files:

  • HTML
  • CSS
  • TypeScript
  • tests

Usually, each component has its own folder. We store them in the app folder (that is pregenerated by ng new) or some subfolders of it.

Here’s the simplest component:

import { Component } from "@angular/core";
@Component({
standalone: true,
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
})
export class AppComponent {}

Angular promotes the separation of things into different files, opposed to frameworks like Vue.js. However, we are able to have HTML/CSS in the ts file simply by replacing templateUrl/styleUrls with template/styles and providing the HTML or CSS there as a string.

selector

The selector is usually prefixed with app- to keep us from overwriting HTML tags.

The selector does not have to be an HTML tag. It can also be an attribute or a class:

  • selector: 'app-root' - it will match HTML elements <app-root></app-root>.
  • selector: '[app-root]' - it will match any HTML element with that attribue, e.g. <div app-root></div>.
  • selector: '.app-root' - it will match any HTML element that has the app-root class on it, e.g. <div class="app-root"></div>.

The selector property is actually not just the way to name the component. It is a specifier of where to render it. It’s kind of similar to Kubernetes’ Service object and its selector, which specifies where to send the traffic.

Most often we just specify our own tag for the component.

Data Binding

Output

We can output data with:

{{ something }}

The variable to output needs to exist on the component:

@Component({
selector: "app-component",
templateUrl: "./app.component.html",
})
export class AppComponent {
something = 10;
}

Inside of {{}} you can actually have any JS/TS expression, like a call to some method.

It can’t contain if condition though.

Property Binding

<button [disabled]="!allowNewServer">Add Server</button>

The disabled property of a button is controlled by the allowNewServer property on our component:

@Component({
selector: "app-servers",
templateUrl: "./servers.component.html",
styleUrls: ["./servers.component.css"],
})
export class ServersComponent implements OnInit {
allowNewServer = false;
}

innerText

Every tag has innerText property, which sets the content of the tag, e.g.:

<p [innerText]="Content"></p>

produces

<p>Content</p>

The {{}} would also work.

Brackets vs. no brackets

We don’t always have to provide square brackets when binding to some @Input.

If brackets are there, the value is treated as an expression.

If there are no brackets the value is treated as a string.

Two-way Binding

In forms, it is useful to bind in two-ways, so that we can control the contents of the input and also read that content.

Here’s an example:

<input type="text" [(ngModel)]="username" />

I am able to access username in the TS code and I am also able to modify it.

Events

We can bind to “native” events with (event)="expression". For example:

<button (click)="onButtonClick()">Add</button>

The onButtonClick method would have to exist on our component. We could also put some expression directly in the HTML instead of invoking a method.

Event Details

We can pass an object with event details by including $event:

<button (click)="onButtonClick($event)">Add</button>

Custom Events

Our comonents can emit custom events that the parent of the component can handle:

@Component(...)
export class MyComponent {
@Output() userCreated = new EventEmitter<User>();
someMethod() {
this.userCreated.emit({name: 'Greg', age: 22});
}
}

The parent comonent would attach to that event like this:

<app-my-component (userCreated)="onUserCreated($event)"></ap-my-component>

The onUserCreated would be some method defined on that parent. The $event would be the instance of User the MyComponent passed to the event.

EventEmitter<> and Output must be imported from @angular/core.

We can alias the name of the event similarly to how we can do that in Inputs that I will describe next.

Inputs

In order to allow our components to accept “arguments”, we need to define them, like this:

@Component(...)
export class MyComponent {
@Input() name: string;
}

The parent component would pass data into MyComponent like this:

<app-my-component [name]="Steve"></app-my-component>

Aliasing

We can change the property’s name that the parent sees with:

@Input('username') name: string;

Now, the parent has to bind to username, not name:

<app-my-component [username]="Steve"></app-my-component>

Accepting Children

Our components may display some children that would be passed by the parent component. Here’s how to do that.

The component that accepts children needs to have this somewhere in its template:

<ng-content></ng-content>

The children would be passed like that:

<app-some-component>
<p>I am being passed into SomeComponent!</p>
</app-some-component>

If a component does not define <ng-content> anywhere in its template, any content that we pass into it is just lost.

@ViewContent

We might need to access some HTML element that is passed to our component as a child. We can’t do that with @ViewChild, because it can only be used for elements that belong to our component directly. There’s another decorator though - @ViewContent.

Here’s an example:

From the parent we pass some child into SomeComponent:

<app-some-component>
<p #paragraph>Bla bla bla</p>
</app-some-component>

From the SomeComponent (that received the paragraph into its <ng-content>):

// The property can be called whatever we want
@ViewContent('paragraph') paragraph: ElementRef;

If you’re about to use the ElementRef from the ngOnInit hook, you should add { static: true }:

@ViewContent('paragraph', { static: true }) paragraph: ElementRef;

I don’t really know why you’d even want to do that since the target element would not be even initialized yet. It will be initialized in the ngAfterContentInit though.

Component Lifecycle

  • ngOnInit - fired once the component is initialized. It’s not yet displayed, but the object for it was created in memory (the constructor finished its execution at this point). Accessing DOM elements from here (e.g. via references) might not give us the expected result since they might not be initialized yet. It’s better to use ngAfterViewInit for that.
  • ngOnChanges - fired when the component is initialized and also each time any property with the @Input decorator is modified. It is the only hook that receives an argument - these are the changes that Angular found (with current and previous values).
  • ngDoCheck - run whenever Angular checks for changes in the component. Even if nothing really changed, Angular has to check it first, and this method will be called. The change detection is triggered by various things: events, Promise resolution and others.
  • ngAfterContentInit - called when the child passed to this component gets initialized (<ng-content>).
  • ngAfterContentChecked - fired when the Angular’s change detection finishes checking the child component.
  • ngAfterViewInit - fired when the component gets rendered on the screen
  • ngAfterViewChecked - fired when the view (including children) has been checked by Angular.
  • ngOnDestroy - called once the component is about to be destroyed.

Imports

Each lifecycle method implementation requires our component to implement a specific type. For example, ngOnInit requires OnInit to be implemented.

Instantiating Components from TypeScript

Normally, we include all the components that we want to render in our template .html files. However, in more dynamic scenarios, it might be impossible to know upfront which components will be needed on a given page. That’s where Dynamic Components might be used. We’re able to create components within our TypeScript code and render them on the page.

Here’s how to do it: StackOverflow.

In Angular <13, we’d use ComponentFactoryResolver. Nowadays, it’s deprecated.

←  Overview
Directives  →
© 2023 Marcin Jahn | Dev Notebook | All Rights Reserved. | Built with Astro.