Skip to content

Commit 419ffa2

Browse files
crisbetopkozlowski-opensource
authored andcommitted
fix(compiler): optimize track function that only passes $index (#55872)
Currently we optimize methods that pass both `$index` and the item into a method. We can take this a step further by also optimizing calls that only pass `$index` into the first parameter. PR Close #55872
1 parent 17d4b61 commit 419ffa2

File tree

5 files changed

+84
-1
lines changed

5 files changed

+84
-1
lines changed

packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/GOLDEN_PARTIAL.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,6 +1548,53 @@ export declare class MyApp {
15481548
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, false, never>;
15491549
}
15501550

1551+
/****************************************************************************************************
1552+
* PARTIAL FILE: for_template_track_method_only_index.js
1553+
****************************************************************************************************/
1554+
import { Component } from '@angular/core';
1555+
import * as i0 from "@angular/core";
1556+
export class MyApp {
1557+
constructor() {
1558+
this.message = 'hello';
1559+
this.items = [{ name: 'one' }, { name: 'two' }, { name: 'three' }];
1560+
}
1561+
trackFn(index) {
1562+
return index;
1563+
}
1564+
}
1565+
MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component });
1566+
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
1567+
<div>
1568+
{{message}}
1569+
@for (item of items; track trackFn($index)) {}
1570+
</div>
1571+
`, isInline: true });
1572+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{
1573+
type: Component,
1574+
args: [{
1575+
template: `
1576+
<div>
1577+
{{message}}
1578+
@for (item of items; track trackFn($index)) {}
1579+
</div>
1580+
`,
1581+
}]
1582+
}] });
1583+
1584+
/****************************************************************************************************
1585+
* PARTIAL FILE: for_template_track_method_only_index.d.ts
1586+
****************************************************************************************************/
1587+
import * as i0 from "@angular/core";
1588+
export declare class MyApp {
1589+
message: string;
1590+
items: {
1591+
name: string;
1592+
}[];
1593+
trackFn(index: number): number;
1594+
static ɵfac: i0.ɵɵFactoryDeclaration<MyApp, never>;
1595+
static ɵcmp: i0.ɵɵComponentDeclaration<MyApp, "ng-component", never, {}, {}, never, never, false, never>;
1596+
}
1597+
15511598
/****************************************************************************************************
15521599
* PARTIAL FILE: for_pure_track_reuse.js
15531600
****************************************************************************************************/

packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/TEST_CASES.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,21 @@
421421
}
422422
]
423423
},
424+
{
425+
"description": "should optimize tracking function that calls a method on the component only with $index from the root template",
426+
"inputFiles": ["for_template_track_method_only_index.ts"],
427+
"expectations": [
428+
{
429+
"files": [
430+
{
431+
"expected": "for_template_track_method_only_index_template.js",
432+
"generated": "for_template_track_method_only_index.js"
433+
}
434+
],
435+
"failureMessage": "Incorrect template"
436+
}
437+
]
438+
},
424439
{
425440
"description": "should reuse identical pure tracking functions",
426441
"inputFiles": ["for_pure_track_reuse.ts"],
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import {Component} from '@angular/core';
2+
3+
@Component({
4+
template: `
5+
<div>
6+
{{message}}
7+
@for (item of items; track trackFn($index)) {}
8+
</div>
9+
`,
10+
})
11+
export class MyApp {
12+
message = 'hello';
13+
items = [{name: 'one'}, {name: 'two'}, {name: 'three'}];
14+
15+
trackFn(index: number) {
16+
return index;
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
$r3$.ɵɵrepeaterCreate(2, MyApp_For_3_Template, 0, 0, null, null, ctx.trackFn, true);

packages/compiler/src/template/pipeline/src/phases/track_fn_optimization.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ function isTrackByFunctionCall(
7979
receiver: ir.ContextExpr;
8080
};
8181
} {
82-
if (!(expr instanceof o.InvokeFunctionExpr) || expr.args.length !== 2) {
82+
if (!(expr instanceof o.InvokeFunctionExpr) || expr.args.length === 0 || expr.args.length > 2) {
8383
return false;
8484
}
8585

@@ -95,6 +95,8 @@ function isTrackByFunctionCall(
9595
const [arg0, arg1] = expr.args;
9696
if (!(arg0 instanceof o.ReadVarExpr) || arg0.name !== '$index') {
9797
return false;
98+
} else if (expr.args.length === 1) {
99+
return true;
98100
}
99101
if (!(arg1 instanceof o.ReadVarExpr) || arg1.name !== '$item') {
100102
return false;

0 commit comments

Comments
 (0)