Reproduction:
{
"rules": {
"templates-no-negated-async": true
}
}
@Component({
selector: 'app-test',
template: `
<div *ngFor="let a of collection; trackBy: trackByFn"></div>
{{ (foo | async) == false }} // not reporting failure here
{{ !(bar | async) }} // not reporting failure here
`,
})
export class TestComponent {}
@Component({
selector: 'app-test',
template: `
<div *ngIf="!(a | async)"></div> // idk if it's intentional, but it's not reporting failure
{{ (foo | async) == false }} // not reporting failure here
{{ !(bar | async) }} // not reporting failure here
{{ !(isHandset | async) ? 'dialog' : 'navigation' }} // not reporting failure here
`,
})
export class TestComponent {}
I've started to looking at this issue and I found what's the problem:
For the first template above the results are:
endLeftSpan = 28
startRightSpan = 33
this.codeWithMap.code.slice(endLeftSpan, startRightSpan); returns "t a o" (from *ngFor line).
It seems that there is a real problem with multiline templates. It's only getting the code from the first line, I really don't know why.
Why do we always assume that the expression is being compared to false? What if the user put ! trying to represent a nil check (null/undefined)?
@Component({
selector: 'app-test',
template: '<div *ngIf="!(data$ | async)">Some content</div>'
})
class TestComponent implements OnInit {
data$: Observable<User | undefined>;
ngOnInit() {
this.data$ = someServiceThatCanReturnUserOrUndefined;
}
}
In this case, an user is trying to use ! to check if Observable emits a undefined or even a null value, so the expression could be changed to:
<div *ngIf="(data$ | async) == null">Some content</div>
<!-- or -->
<div *ngIf="(data$ | async) === undefined">Some content</div>
... instead, we replace the expression with === false, what brings an unexpected behavior, a completely unsafe replace.
Also, there are real failures related to the current replacement:
<div *ngIf="!(foo | async)">Something</div>
... After running --fix:
<div *ngIf="(foo | async) === false >Something</div>
<!-- final quote removed and extra whitespace added -->
IMHO, we should remove the "fix" option for this rule, since we can't assume what the "expression" is trying to do... or maybe leave only the replace for strict equality (== false -> === false).
@wKoza @mgechev let me know your thoughts.
IMHO, we should remove the "fix" option for this rule, since we can't assume what the "expression" is trying to do... or maybe leave only the replace for strict equality (== false -> === false).
Yes, we should remove the fix because it's not safe.
@mgechev I'm still trying to find a valid use case for this rule, do you have one?
Here...
https://github.com/mgechev/codelyzer/blob/5a84041253dc07ffd6445bef5d6d6733ff43fecd/src/templatesNoNegatedAsyncRule.ts#L50
... we say "_Async pipes can not be negated_". Why not?
Take a look at this sample:
@Component({
selector: 'app-root',
template: `
<div *ngIf="!(obs$ | async)">
Loading... <!-- this will be displayed until the observable emits a value -->
</div>
`
})
export class AppComponent {
obs$ = observableOf([1, 2, 3]).pipe(
delay(2000) // simulates an http request
);
}
What would be the benefits of an explicit check like this <div *ngIf="(obs$ | async) === null" instead of what I did above?
@mgechev just to complement: unless there's a valid case that this rule could be useful, I'm voting in favor of deprecate this rule.
I'll look at the story behind it over the weekend.
@mgechev Friendly bump, have you looked?
Yes, here's the motivation. I'm not sure if the value it brings justifies the corner cases you pointed. I'd vote to drop the rule. Let's keep the issue open for a few more days and collect opinions.
@mgechev better close this and create a new issue about deprecate this rule or keep the discussion here?
Sounds good!
@rafaelss95 since we had this conversation, we reevaluated the pros and cons of this rule and introduced it in Google. Let's keep it as part of codelyzer.
@mgechev Okay, can we just update the following failure message?https://github.com/mgechev/codelyzer/blob/bc5a7ec5fd94e665423c77cb4c08fa98d2e2dfe9/src/templatesNoNegatedAsyncRule.ts#L68
Sure. How do you suggest to update it?