diff --git a/src/material/schematics/ng-generate/mdc-migration/rules/components/card/card-template.spec.ts b/src/material/schematics/ng-generate/mdc-migration/rules/components/card/card-template.spec.ts
new file mode 100644
index 000000000000..c6bec47daede
--- /dev/null
+++ b/src/material/schematics/ng-generate/mdc-migration/rules/components/card/card-template.spec.ts
@@ -0,0 +1,93 @@
+import {createTestApp, patchDevkitTreeToExposeTypeScript} from '@angular/cdk/schematics/testing';
+import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
+import {createNewTestRunner, migrateComponents, TEMPLATE_FILE} from '../test-setup-helper';
+
+describe('card template migrator', () => {
+ let runner: SchematicTestRunner;
+ let cliAppTree: UnitTestTree;
+
+ async function runMigrationTest(oldFileContent: string, newFileContent: string) {
+ cliAppTree.overwrite(TEMPLATE_FILE, oldFileContent);
+ const tree = await migrateComponents(['card'], runner, cliAppTree);
+ expect(tree.readContent(TEMPLATE_FILE)).toBe(newFileContent);
+ }
+
+ beforeEach(async () => {
+ runner = createNewTestRunner();
+ cliAppTree = patchDevkitTreeToExposeTypeScript(await createTestApp(runner));
+ });
+
+ it('should not update other elements', async () => {
+ await runMigrationTest('', '');
+ });
+
+ it('should update single', async () => {
+ await runMigrationTest('', '');
+ });
+
+ it('should update multiple same-line unnested', async () => {
+ await runMigrationTest(
+ '',
+ '',
+ );
+ });
+
+ it('should update multiple same-line nested', async () => {
+ await runMigrationTest(
+ '',
+ '',
+ );
+ });
+
+ it('should update multiple same-line nested and unnested', async () => {
+ await runMigrationTest(
+ '',
+ '',
+ );
+ });
+
+ it('should update multiple multi-line unnested', async () => {
+ await runMigrationTest(
+ `
+
+
+ `,
+ `
+
+
+ `,
+ );
+ });
+
+ it('should update multiple multi-line nested', async () => {
+ await runMigrationTest(
+ `
+
+
+
+ `,
+ `
+
+
+
+ `,
+ );
+ });
+
+ it('should update multiple multi-line nested and unnested', async () => {
+ await runMigrationTest(
+ `
+
+
+
+
+ `,
+ `
+
+
+
+
+ `,
+ );
+ });
+});
diff --git a/src/material/schematics/ng-generate/mdc-migration/rules/components/card/card-template.ts b/src/material/schematics/ng-generate/mdc-migration/rules/components/card/card-template.ts
new file mode 100644
index 000000000000..ca56b44f4211
--- /dev/null
+++ b/src/material/schematics/ng-generate/mdc-migration/rules/components/card/card-template.ts
@@ -0,0 +1,23 @@
+/**
+ * @license
+ * Copyright Google LLC All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {TmplAstElement} from '@angular/compiler';
+import {TemplateMigrator} from '../../template-migrator';
+import {addAttribute} from '../../tree-traversal';
+
+export class CardTemplateMigrator extends TemplateMigrator {
+ component = 'card';
+ tagName = 'mat-card';
+
+ override updateStartTag(template: string, node: TmplAstElement): string {
+ if (node.name !== this.tagName) {
+ return template;
+ }
+ return addAttribute(template, node, 'appearance', 'outlined');
+ }
+}
diff --git a/src/material/schematics/ng-generate/mdc-migration/rules/index.ts b/src/material/schematics/ng-generate/mdc-migration/rules/index.ts
index 82f5fd356ceb..fcd537482e4d 100644
--- a/src/material/schematics/ng-generate/mdc-migration/rules/index.ts
+++ b/src/material/schematics/ng-generate/mdc-migration/rules/index.ts
@@ -8,6 +8,7 @@
import {ButtonStylesMigrator} from './components/button/button-styles';
import {CardStylesMigrator} from './components/card/card-styles';
+import {CardTemplateMigrator} from './components/card/card-template';
import {CheckboxStylesMigrator} from './components/checkbox/checkbox-styles';
import {ChipsStylesMigrator} from './components/chips/chips-styles';
import {DialogStylesMigrator} from './components/dialog/dialog-styles';
@@ -36,6 +37,7 @@ export const MIGRATORS: ComponentMigrator[] = [
{
component: 'card',
styles: new CardStylesMigrator(),
+ template: new CardTemplateMigrator(),
},
{
component: 'checkbox',
diff --git a/src/material/schematics/ng-generate/mdc-migration/rules/template-migration.ts b/src/material/schematics/ng-generate/mdc-migration/rules/template-migration.ts
index d5598fda0e0a..dc09fde7ccb2 100644
--- a/src/material/schematics/ng-generate/mdc-migration/rules/template-migration.ts
+++ b/src/material/schematics/ng-generate/mdc-migration/rules/template-migration.ts
@@ -16,10 +16,21 @@ export class TemplateMigration extends Migration m.template).map(m => m.template!);
- visitElements(ast.nodes, node => {
- // TODO(wagnermaciel): implement the migration updates.
- });
+ visitElements(
+ ast.nodes,
+ node => {
+ migrators.forEach(m => {
+ template.content = m.updateEndTag(template.content, node);
+ });
+ },
+ node => {
+ migrators.forEach(m => {
+ template.content = m.updateStartTag(template.content, node);
+ });
+ },
+ );
this.fileSystem.overwrite(template.filePath, template.content);
}