-
-
Notifications
You must be signed in to change notification settings - Fork 2k
docs(signals): apply new naming convention from Angular styleguide #4790
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -15,7 +15,7 @@ import { map, pipe, tap } from 'rxjs'; | |||||
import { rxMethod } from '@ngrx/signals/rxjs-interop'; | ||||||
|
||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent { | ||||||
export class Numbers { | ||||||
// 👇 This reactive method will have an input argument | ||||||
// of type `number | Signal<number> | Observable<number>`. | ||||||
readonly logDoubledNumber = rxMethod<number>( | ||||||
|
@@ -32,20 +32,20 @@ Each invocation of the reactive method pushes the input value through the reacti | |||||
When called with a static value, the reactive chain executes once. | ||||||
|
||||||
```ts | ||||||
import { Component, OnInit } from '@angular/core'; | ||||||
import { Component } from '@angular/core'; | ||||||
import { map, pipe, tap } from 'rxjs'; | ||||||
import { rxMethod } from '@ngrx/signals/rxjs-interop'; | ||||||
|
||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent implements OnInit { | ||||||
export class Numbers { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
readonly logDoubledNumber = rxMethod<number>( | ||||||
pipe( | ||||||
map((num) => num * 2), | ||||||
tap(console.log) | ||||||
) | ||||||
); | ||||||
|
||||||
ngOnInit(): void { | ||||||
constructor() { | ||||||
this.logDoubledNumber(1); | ||||||
// console output: 2 | ||||||
|
||||||
|
@@ -58,20 +58,20 @@ export class NumbersComponent implements OnInit { | |||||
When a reactive method is called with a signal, the reactive chain is executed every time the signal value changes. | ||||||
|
||||||
```ts | ||||||
import { Component, OnInit, signal } from '@angular/core'; | ||||||
import { Component, signal } from '@angular/core'; | ||||||
import { map, pipe, tap } from 'rxjs'; | ||||||
import { rxMethod } from '@ngrx/signals/rxjs-interop'; | ||||||
|
||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent implements OnInit { | ||||||
export class Numbers { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
readonly logDoubledNumber = rxMethod<number>( | ||||||
pipe( | ||||||
map((num) => num * 2), | ||||||
tap(console.log) | ||||||
) | ||||||
); | ||||||
|
||||||
ngOnInit(): void { | ||||||
constructor() { | ||||||
const num = signal(10); | ||||||
this.logDoubledNumber(num); | ||||||
// console output: 20 | ||||||
|
@@ -85,20 +85,20 @@ export class NumbersComponent implements OnInit { | |||||
When a reactive method is called with an observable, the reactive chain is executed every time the observable emits a new value. | ||||||
|
||||||
```ts | ||||||
import { Component, OnInit } from '@angular/core'; | ||||||
import { Component } from '@angular/core'; | ||||||
import { interval, map, of, pipe, tap } from 'rxjs'; | ||||||
import { rxMethod } from '@ngrx/signals/rxjs-interop'; | ||||||
|
||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent implements OnInit { | ||||||
export class Numbers { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
readonly logDoubledNumber = rxMethod<number>( | ||||||
pipe( | ||||||
map((num) => num * 2), | ||||||
tap(console.log) | ||||||
) | ||||||
); | ||||||
|
||||||
ngOnInit(): void { | ||||||
constructor() { | ||||||
const num1$ = of(100, 200, 300); | ||||||
this.logDoubledNumber(num1$); | ||||||
// console output: 200, 400, 600 | ||||||
|
@@ -119,15 +119,15 @@ The `rxMethod` is a great choice for handling API calls in a reactive manner. | |||||
The subsequent example demonstrates how to use `rxMethod` to fetch the book by id whenever the `selectedBookId` signal value changes. | ||||||
|
||||||
```ts | ||||||
import { Component, inject, OnInit, signal } from '@angular/core'; | ||||||
import { Component, inject, signal } from '@angular/core'; | ||||||
import { concatMap, filter, pipe } from 'rxjs'; | ||||||
import { rxMethod } from '@ngrx/signals/rxjs-interop'; | ||||||
import { tapResponse } from '@ngrx/operators'; | ||||||
import { Book } from './book.model'; | ||||||
import { BooksService } from './books.service'; | ||||||
import { BooksService } from './books-service'; | ||||||
import { Book } from './book'; | ||||||
|
||||||
@Component({ /* ... */ }) | ||||||
export class BooksComponent implements OnInit { | ||||||
export class BookList { | ||||||
readonly #booksService = inject(BooksService); | ||||||
|
||||||
readonly bookMap = signal<Record<string, Book>>({}); | ||||||
|
@@ -147,7 +147,7 @@ export class BooksComponent implements OnInit { | |||||
) | ||||||
); | ||||||
|
||||||
ngOnInit(): void { | ||||||
constructor() { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a question for my understanding of the new guide. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven’t found anything in the documentation that explicitly recommends preferring the constructor over lifecycle hooks. The closest reference is this guideline about keeping lifecycle methods short: https://next.angular.dev/style-guide#keep-lifecycle-methods-simple There are also some hooks which will get deprecated for Signal Components, but That said, I personally default to using the constructor—unless I need to implicitly access an InputSignal. The main reason is that the constructor allows for early field initialization and any setup that needs to happen as soon as possible. It also runs within the injection context, which can be important for certain use cases. Summary: I'm fine for moving to constructor, even if it is not listed in the new style guide. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a reactive method is called with a signal or observable, it should be called within the injection context (constructor). Otherwise, the warning will be displayed in development mode. At the time when I wrote this guide, we didn't have this warning. The guide is now in accordance with the latest changes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it, thanks for both answers 👍 |
||||||
// 👇 Load book by id whenever the `selectedBookId` value changes. | ||||||
this.loadBookById(this.selectedBookId); | ||||||
} | ||||||
|
@@ -173,15 +173,15 @@ Further details can be found in the [Reactive Store Methods](guide/signals/signa | |||||
To create a reactive method without arguments, the `void` type should be specified as a generic argument to the `rxMethod` function. | ||||||
|
||||||
```ts | ||||||
import { Component, inject, OnInit, signal } from '@angular/core'; | ||||||
import { Component, inject, signal } from '@angular/core'; | ||||||
import { exhaustMap } from 'rxjs'; | ||||||
import { rxMethod } from '@ngrx/signals/rxjs-interop'; | ||||||
import { tapResponse } from '@ngrx/operators'; | ||||||
import { Book } from './book.model'; | ||||||
import { BooksService } from './books.service'; | ||||||
import { BooksService } from './books-service'; | ||||||
import { Book } from './book'; | ||||||
|
||||||
@Component({ /* ... */ }) | ||||||
export class BooksComponent implements OnInit { | ||||||
export class BookList { | ||||||
readonly #booksService = inject(BooksService); | ||||||
readonly books = signal<Book[]>([]); | ||||||
|
||||||
|
@@ -197,7 +197,7 @@ export class BooksComponent implements OnInit { | |||||
}) | ||||||
); | ||||||
|
||||||
ngOnInit(): void { | ||||||
constructor() { | ||||||
this.loadAllBooks(); | ||||||
} | ||||||
} | ||||||
|
@@ -221,7 +221,7 @@ export class NumbersService { | |||||
} | ||||||
|
||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent implements OnInit { | ||||||
export class Numbers implements OnInit { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
readonly #injector = inject(Injector); | ||||||
readonly #numbersService = inject(NumbersService); | ||||||
|
||||||
|
@@ -250,15 +250,15 @@ If the injector is not provided when calling the reactive method with a signal o | |||||
If a reactive method needs to be cleaned up before the injector is destroyed, manual cleanup can be performed by calling the `destroy` method. | ||||||
|
||||||
```ts | ||||||
import { Component, OnInit } from '@angular/core'; | ||||||
import { Component } from '@angular/core'; | ||||||
import { interval, tap } from 'rxjs'; | ||||||
import { rxMethod } from '@ngrx/signals/rxjs-interop'; | ||||||
|
||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent implements OnInit { | ||||||
export class Numbers { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
readonly logNumber = rxMethod<number>(tap(console.log)); | ||||||
|
||||||
ngOnInit(): void { | ||||||
constructor() { | ||||||
const num1$ = interval(500); | ||||||
const num2$ = interval(1_000); | ||||||
|
||||||
|
@@ -277,15 +277,15 @@ When invoked, the reactive method returns the object with the `destroy` method. | |||||
This allows manual cleanup of a specific call, preserving the activity of other reactive method calls until the corresponding injector is destroyed. | ||||||
|
||||||
```ts | ||||||
import { Component, OnInit } from '@angular/core'; | ||||||
import { Component } from '@angular/core'; | ||||||
import { interval, tap } from 'rxjs'; | ||||||
import { rxMethod } from '@ngrx/signals/rxjs-interop'; | ||||||
|
||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent implements OnInit { | ||||||
export class Numbers { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
readonly logNumber = rxMethod<number>(tap(console.log)); | ||||||
|
||||||
ngOnInit(): void { | ||||||
constructor() { | ||||||
const num1$ = interval(500); | ||||||
const num2$ = interval(1_000); | ||||||
|
||||||
|
@@ -310,7 +310,7 @@ import { tap } from 'rxjs'; | |||||
import { rxMethod } from '@ngrx/signals/rxjs-interop'; | ||||||
|
||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent implements OnInit { | ||||||
export class Numbers implements OnInit { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
readonly #injector = inject(Injector); | ||||||
|
||||||
ngOnInit(): void { | ||||||
|
@@ -322,4 +322,4 @@ export class NumbersComponent implements OnInit { | |||||
logNumber(10); | ||||||
} | ||||||
} | ||||||
``` | ||||||
``` |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -7,7 +7,7 @@ import { Component } from '@angular/core'; | |||||
import { signalMethod } from '@ngrx/signals'; | ||||||
|
||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent { | ||||||
export class Numbers { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
// 👇 This method will have an input argument | ||||||
// of type `number | Signal<number>`. | ||||||
readonly logDoubledNumber = signalMethod<number>((num) => { | ||||||
|
@@ -21,7 +21,7 @@ export class NumbersComponent { | |||||
|
||||||
```ts | ||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent { | ||||||
export class Numbers { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
readonly logDoubledNumber = signalMethod<number>((num) => { | ||||||
const double = num * 2; | ||||||
console.log(double); | ||||||
|
@@ -44,27 +44,27 @@ export class NumbersComponent { | |||||
## Automatic Cleanup | ||||||
|
||||||
`signalMethod` uses an `effect` internally to track the Signal changes. | ||||||
By default, the `effect` runs in the injection context of the caller. In the example above, that is `NumbersComponent`. That means, that the `effect` is automatically cleaned up when the component is destroyed. | ||||||
By default, the `effect` runs in the injection context of the caller. In the example above, that is the `Numbers` component. That means, that the `effect` is automatically cleaned up when the component is destroyed. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
If the call happens outside an injection context, then the injector of the `signalMethod` is used. This would be the case, if `logDoubledNumber` runs in `ngOnInit`: | ||||||
|
||||||
```ts | ||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent implements OnInit { | ||||||
export class Numbers implements OnInit { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
readonly logDoubledNumber = signalMethod<number>((num) => { | ||||||
const double = num * 2; | ||||||
console.log(double); | ||||||
}); | ||||||
|
||||||
ngOnInit(): void { | ||||||
const value = signal(2); | ||||||
// 👇 Uses the injection context of the `NumbersComponent`. | ||||||
// 👇 Uses the injection context of the `Numbers` component. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
this.logDoubledNumber(value); | ||||||
} | ||||||
} | ||||||
``` | ||||||
|
||||||
Even though `logDoubledNumber` is called outside an injection context, automatic cleanup occurs when `NumbersComponent` is destroyed, since `logDoubledNumber` was created within the component's injection context. | ||||||
Even though `logDoubledNumber` is called outside an injection context, automatic cleanup occurs when the `Numbers` component is destroyed, since `logDoubledNumber` was created within the component's injection context. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
However, when creating a `signalMethod` in an ancestor injection context, the cleanup behavior is different: | ||||||
|
||||||
|
@@ -78,7 +78,7 @@ export class NumbersService { | |||||
} | ||||||
|
||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent implements OnInit { | ||||||
export class Numbers implements OnInit { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
readonly numbersService = inject(NumbersService); | ||||||
|
||||||
ngOnInit(): void { | ||||||
|
@@ -89,7 +89,7 @@ export class NumbersComponent implements OnInit { | |||||
} | ||||||
``` | ||||||
|
||||||
Here, the `effect` outlives the component, which would produce a memory leak. | ||||||
Here, the `effect` used internally by `signalMethod` outlives the component, which would produce a memory leak. | ||||||
|
||||||
<div class="alert is-important"> | ||||||
|
||||||
|
@@ -103,13 +103,13 @@ When a `signalMethod` is created in an ancestor injection context, it's necessar | |||||
|
||||||
```ts | ||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent implements OnInit { | ||||||
export class Numbers implements OnInit { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
readonly numbersService = inject(NumbersService); | ||||||
readonly injector = inject(Injector); | ||||||
|
||||||
ngOnInit(): void { | ||||||
const value = signal(1); | ||||||
// 👇 Providing the `NumbersComponent` injector | ||||||
// 👇 Providing the `Numbers` component injector | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
// to ensure cleanup on component destroy. | ||||||
this.numbersService.logDoubledNumber(value, { | ||||||
injector: this.injector, | ||||||
|
@@ -127,10 +127,10 @@ The `signalMethod` must be initialized within an injection context. To initializ | |||||
|
||||||
```ts | ||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent implements OnInit { | ||||||
export class Numbers implements OnInit { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
readonly injector = inject(Injector); | ||||||
|
||||||
ngOnInit() { | ||||||
ngOnInit(): void { | ||||||
const logDoubledNumber = signalMethod<number>( | ||||||
(num) => console.log(num * 2), | ||||||
{ injector: this.injector }, | ||||||
|
@@ -145,7 +145,7 @@ At first sight, `signalMethod`, might be the same as `effect`: | |||||
|
||||||
```ts | ||||||
@Component({ /* ... */ }) | ||||||
export class NumbersComponent { | ||||||
export class Numbers { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
readonly num = signal(2); | ||||||
readonly logDoubledNumberEffect = effect(() => { | ||||||
console.log(this.num() * 2); | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to this comment from Matthieu, maybe a more specific name?
https://x.com/Jean__Meche/status/1925866491506831784
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no recommendation in the official docs that components with a single word or generic name should be avoided.
https://next.angular.dev/style-guide#naming
This component is created just to show how
rxMethod
should be used. It does not have a template that visualizes a specific use case, and because of that, I haven't named itNumberList
,NumbersCard
,NumberDetails
, or similar. In real-world scenarios, we would not create a component for logging double numbers anyway.