Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ <h2>Chips example</h2>
<mat-form-field class="example-chip-list" appearance="fill">
<mat-label>Favorite Fruits</mat-label>
<mat-chip-grid #fruitChipList aria-label="Fruit selection">
<mat-chip *ngFor="let fruit of fruits" (removed)="remove(fruit)">
<mat-chip-row *ngFor="let fruit of fruits" (removed)="remove(fruit)">
{{fruit.name}}
<button matChipRemove>
<mat-icon>cancel</mat-icon>
</button>
</mat-chip>
</mat-chip-row>
<input placeholder="New fruit..."
[matChipInputFor]="fruitChipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,66 @@ describe('chips template migrator', () => {
it('should update standalone chips', async () => {
await runMigrationTest('<mat-chip></mat-chip>', '<mat-chip-option></mat-chip-option>');
});

it('should update mat-chip with an *ngFor', async () => {
await runMigrationTest(
`
<mat-chip-list>
<mat-chip *ngFor="let chip of chips">{{chip}}</mat-chip>
</mat-chip-list>
`,
`
<mat-chip-listbox>
<mat-chip-option *ngFor="let chip of chips">{{chip}}</mat-chip-option>
</mat-chip-listbox>
`,
);
});

it('should update a chip listbox with a nested ng-container', async () => {
await runMigrationTest(
`
<mat-chip-list>
<ng-container *ngFor="let category of categories">
<ng-container *ngIf="category === 'something'">
<mat-chip *ngFor="let chip of category.chips" [selectable]="false">{{chip}}</mat-chip>
</ng-container>
</ng-container>
</mat-chip-list>
`,
`
<mat-chip-listbox>
<ng-container *ngFor="let category of categories">
<ng-container *ngIf="category === 'something'">
<mat-chip-option *ngFor="let chip of category.chips" [selectable]="false">{{chip}}</mat-chip-option>
</ng-container>
</ng-container>
</mat-chip-listbox>
`,
);
});

it('should update a chip with an *ngIf', async () => {
await runMigrationTest(
'<mat-chip *ngIf="isShown"></mat-chip>',
'<mat-chip-option *ngIf="isShown"></mat-chip-option>',
);
});

it('should update a chip grid with an *ngFor', async () => {
await runMigrationTest(
`
<mat-chip-list #chipList>
<mat-chip *ngFor="let chip of chips">{{chip}}</mat-chip>
<input type="text" matInput [matChipInputFor]="chipList">
</mat-chip-list>
`,
`
<mat-chip-grid #chipList>
<mat-chip-row *ngFor="let chip of chips">{{chip}}</mat-chip-row>
<input type="text" matInput [matChipInputFor]="chipList">
</mat-chip-grid>
`,
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,49 @@ function runClearAttributeTest(html: string, result: string): void {
}

describe('#visitElements', () => {
describe('visitElements', () => {
it('should traverse elements with an *ngFor', () => {
const visitedElements: string[] = [];
const template = `
<parent>
<child *ngFor="let c of children">
<grandchild *ngFor="let g of c.children"></grandchild>
</child>
</parent>
`;

visitElements(parseTemplate(template).nodes, node => visitedElements.push(node.name));
expect(visitedElements).toEqual(['parent', 'child', 'grandchild']);
});

it('should traverse elements inside ng-container', () => {
const visitedElements: string[] = [];
const template = `
<ng-container>
<parent>
<ng-container>
<child>
<ng-container>
<grandchild></grandchild>
</ng-container>
</child>
</ng-container>
</parent>
</ng-container>
`;

visitElements(parseTemplate(template).nodes, node => visitedElements.push(node.name));
expect(visitedElements).toEqual([
'ng-container',
'parent',
'ng-container',
'child',
'ng-container',
'grandchild',
]);
});
});

describe('tag name replacements', () => {
it('should handle basic cases', async () => {
runTagNameDuplicationTest('<a></a>', '<aa></aa>');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
ParsedTemplate,
TmplAstElement,
TmplAstNode,
TmplAstTemplate,
parseTemplate as parseTemplateUsingCompiler,
} from '@angular/compiler';

Expand All @@ -32,9 +33,18 @@ export function visitElements(
nodes.reverse();
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node instanceof TmplAstElement) {
const isElement = node instanceof TmplAstElement;

if (isElement) {
preorderCallback(node);
}

// Descend both into elements and templates in order to cover cases like `*ngIf` and `*ngFor`.
if (isElement || node instanceof TmplAstTemplate) {
visitElements(node.children, preorderCallback, postorderCallback);
}

if (isElement) {
postorderCallback(node);
}
}
Expand All @@ -46,8 +56,8 @@ export function visitElements(
*
* For more details, see https://github.com/angular/angular/blob/4332897baa2226ef246ee054fdd5254e3c129109/packages/compiler-cli/src/ngtsc/annotations/component/src/resources.ts#L230.
*
* @param html text of the template to parse
* @param filePath URL to use for source mapping of the parsed template
* @param template text of the template to parse
* @param templateUrl URL to use for source mapping of the parsed template
* @returns the updated template html.
*/
export function parseTemplate(template: string, templateUrl: string = ''): ParsedTemplate {
Expand Down