Skip to content
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

fix(material/schematics): not migrating elements with template directives #25956

Merged
merged 1 commit into from Nov 11, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
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>
`,
);
});
});
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
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