Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@
// Custom DSpace Angular rules
"dspace-angular-html/themed-component-usages": "error",
"dspace-angular-html/no-disabled-attribute-on-button": "error",
"dspace-angular-html/no-method-call-with-async-pipe": "error",
"@angular-eslint/template/prefer-control-flow": "error"
}
},
Expand Down
1 change: 1 addition & 0 deletions docs/lint/html/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ _______

- [`dspace-angular-html/themed-component-usages`](./rules/themed-component-usages.md): Themeable components should be used via the selector of their `ThemedComponent` wrapper class
- [`dspace-angular-html/no-disabled-attribute-on-button`](./rules/no-disabled-attribute-on-button.md): Buttons should use the `dsBtnDisabled` directive instead of the HTML `disabled` attribute.
- [`dspace-angular-html/no-method-call-with-async-pipe`](./rules/no-method-call-with-async-pipe.md): Don't call a method directly as the input of the `async` pipe (e.g. `getFoo$() | async`).
161 changes: 161 additions & 0 deletions docs/lint/html/rules/no-method-call-with-async-pipe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
[DSpace ESLint plugins](../../../../lint/README.md) > [HTML rules](../index.md) > `dspace-angular-html/no-method-call-with-async-pipe`
_______

Don't call a method directly as the input of the `async` pipe (e.g. `getFoo$() | async`).

This re-executes the method - and the RxJS pipeline it returns - on every change detection cycle, which often leads to performance issues.

Instead:
- Subscribe to the Observable in `ngOnInit()`
- Push emitted values into a `BehaviorSubject`
- Bind `| async` to that `BehaviorSubject` in the template
- Unsubscribe in `ngOnDestroy()`


_______

[Source code](../../../../lint/src/rules/html/no-method-call-with-async-pipe.ts)



### Examples


#### Valid code

##### binding the async pipe to a property is still valid

```html
<span>{{ foo$ | async }}</span>
```


##### calling a method without piping the result through async is still valid

```html
<span>{{ getFoo() }}</span>
```


##### piping a property through an unrelated pipe is still valid

```html
<span>{{ foo$ | someOtherPipe }}</span>
```


##### binding the async pipe to a property accessed through a member expression is still valid

```html
<span>{{ (foo$ | async)?.length }}</span>
```





#### Invalid code

##### should not call a method as the direct input of the async pipe

```html
<span>{{ getFoo$() | async }}</span>



```
Will produce the following error(s):
```
Don't call '{{ methodName }}' directly in the template as the input of the async pipe; subscribe in ngOnInit(), push values into a BehaviorSubject, and bind `| async` to that instead.
```


##### should not call a method with arguments as the direct input of the async pipe

```html
<span>{{ getFoo$(id) | async }}</span>



```
Will produce the following error(s):
```
Don't call '{{ methodName }}' directly in the template as the input of the async pipe; subscribe in ngOnInit(), push values into a BehaviorSubject, and bind `| async` to that instead.
```


##### should not use the safe-call variant as the direct input of the async pipe

```html
<span>{{ getFoo$?.() | async }}</span>



```
Will produce the following error(s):
```
Don't call '{{ methodName }}' directly in the template as the input of the async pipe; subscribe in ngOnInit(), push values into a BehaviorSubject, and bind `| async` to that instead.
```


##### should not call a method as the direct input of the async pipe in the *ngIf microsyntax

```html
<div *ngIf="getFoo$() | async as foo">{{ foo }}</div>



```
Will produce the following error(s):
```
Don't call '{{ methodName }}' directly in the template as the input of the async pipe; subscribe in ngOnInit(), push values into a BehaviorSubject, and bind `| async` to that instead.
```


##### should not call a method as the direct input of the async pipe in an @if condition, even when wrapped in a member access

```html
@if ((getFoo$() | async)?.length > 0) {
<span>hi</span>
}



```
Will produce the following error(s):
```
Don't call '{{ methodName }}' directly in the template as the input of the async pipe; subscribe in ngOnInit(), push values into a BehaviorSubject, and bind `| async` to that instead.
```


##### should not call a method as the direct input of the async pipe in an @for expression

```html
@for (item of getFoo$() | async; track item) {
<span>{{ item }}</span>
}



```
Will produce the following error(s):
```
Don't call '{{ methodName }}' directly in the template as the input of the async pipe; subscribe in ngOnInit(), push values into a BehaviorSubject, and bind `| async` to that instead.
```


##### should not call a method as the direct input of the async pipe inside an object literal binding

```html
<div [ngClass]="{'active': (getFoo$() | async)?.length > 0}"></div>



```
Will produce the following error(s):
```
Don't call '{{ methodName }}' directly in the template as the input of the async pipe; subscribe in ngOnInit(), push values into a BehaviorSubject, and bind `| async` to that instead.
```



2 changes: 2 additions & 0 deletions lint/src/rules/html/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import {
RuleExports,
} from '../../util/structure';
import * as noDisabledAttributeOnButton from './no-disabled-attribute-on-button';
import * as noMethodCallWithAsyncPipe from './no-method-call-with-async-pipe';
import * as themedComponentUsages from './themed-component-usages';

const index = [
themedComponentUsages,
noDisabledAttributeOnButton,
noMethodCallWithAsyncPipe,

] as unknown as RuleExports[];

Expand Down
Loading
Loading