From 6d084cb9fb999cd56deecc12a55f83a2d5e8ca63 Mon Sep 17 00:00:00 2001 From: MeAkib Date: Sun, 18 Oct 2026 20:17:43 +0600 Subject: [PATCH] fix(@schematics/angular): apply `viewEncapsulation` to generated application components The `viewEncapsulation` option of the application schematic had no effect. The root component template did not set `encapsulation`, the value was not added to the component schematic defaults, so components generated later used the default encapsulation, and the `defaultEncapsulation` option added to `bootstrapModule` in `main.ts` for NgModule applications is only used by the JIT compiler. Set `encapsulation` on the root component, add `viewEncapsulation` to the component schematic defaults in `angular.json`, and remove the ineffective `defaultEncapsulation` option from `main.ts`. Fixes #34388 --- .../src/app/app__suffix__.ts.template | 5 ++- .../files/module-files/src/main.ts.template | 7 +--- .../src/app/app__suffix__.ts.template | 5 ++- .../schematics/angular/application/index.ts | 6 ++- .../angular/application/index_spec.ts | 41 ++++++++++++++++--- 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.ts.template b/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.ts.template index ef9aebaa0e14..db9c4cfc0137 100644 --- a/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.ts.template +++ b/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.ts.template @@ -1,6 +1,7 @@ -import { Component, signal } from '@angular/core'; +import { Component, signal<% if (!!viewEncapsulation) { %>, ViewEncapsulation<% } %> } from '@angular/core'; -@Component({ +@Component({<% if (!!viewEncapsulation) { %> + encapsulation: ViewEncapsulation.<%= viewEncapsulation %>,<% } %> selector: '<%= selector %>', standalone: false,<% if (inlineStyle) { %> styles: [],<% } else { %> diff --git a/packages/schematics/angular/application/files/module-files/src/main.ts.template b/packages/schematics/angular/application/files/module-files/src/main.ts.template index 0fcb878b1e23..94cbfa256d61 100644 --- a/packages/schematics/angular/application/files/module-files/src/main.ts.template +++ b/packages/schematics/angular/application/files/module-files/src/main.ts.template @@ -1,8 +1,5 @@ -<% if (!!viewEncapsulation) { %>import { ViewEncapsulation } from '@angular/core'; -<% } %>import { platformBrowser } from '@angular/platform-browser'; +import { platformBrowser } from '@angular/platform-browser'; import { AppModule } from './app/app<%= typeSeparator %>module'; -platformBrowser().bootstrapModule(AppModule, { - <% if (!!viewEncapsulation) { %> defaultEncapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } %> -}) +platformBrowser().bootstrapModule(AppModule) .catch(err => console.error(err)); diff --git a/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.ts.template b/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.ts.template index dfdb04abcb76..2106166ea22f 100644 --- a/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.ts.template +++ b/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.ts.template @@ -1,7 +1,8 @@ -import { Component, signal } from '@angular/core';<% if (routing) { %> +import { Component, signal<% if (!!viewEncapsulation) { %>, ViewEncapsulation<% } %> } from '@angular/core';<% if (routing) { %> import { RouterOutlet } from '@angular/router';<% } %> -@Component({ +@Component({<% if (!!viewEncapsulation) { %> + encapsulation: ViewEncapsulation.<%= viewEncapsulation %>,<% } %> imports: [<% if (routing) { %>RouterOutlet<% } %>], selector: '<%= selector %>',<% if (inlineStyle) { %> styles: [],<% } else { %> diff --git a/packages/schematics/angular/application/index.ts b/packages/schematics/angular/application/index.ts index 2820634b1ba8..cac60c23177b 100644 --- a/packages/schematics/angular/application/index.ts +++ b/packages/schematics/angular/application/index.ts @@ -206,7 +206,8 @@ function addAppToWorkspaceFile(options: ApplicationOptions, appDir: string): Rul options.inlineTemplate || options.inlineStyle || options.minimal || - options.style !== Style.Css + options.style !== Style.Css || + options.viewEncapsulation ) { const componentSchematicsOptions: JsonObject = {}; if (options.inlineTemplate ?? options.minimal) { @@ -218,6 +219,9 @@ function addAppToWorkspaceFile(options: ApplicationOptions, appDir: string): Rul if (options.style && options.style !== Style.Css) { componentSchematicsOptions.style = options.style; } + if (options.viewEncapsulation) { + componentSchematicsOptions.viewEncapsulation = options.viewEncapsulation; + } schematics['@schematics/angular:component'] = componentSchematicsOptions; } diff --git a/packages/schematics/angular/application/index_spec.ts b/packages/schematics/angular/application/index_spec.ts index ad2ba04ff348..7f86e245553a 100644 --- a/packages/schematics/angular/application/index_spec.ts +++ b/packages/schematics/angular/application/index_spec.ts @@ -704,6 +704,35 @@ describe('Application Schematic', () => { expect(main).toContain('bootstrapApplication'); }); + it(`should set 'encapsulation' in the root component when 'viewEncapsulation' is provided`, async () => { + const options = { ...defaultOptions, viewEncapsulation: ViewEncapsulation.None }; + const tree = await schematicRunner.runSchematic('application', options, workspaceTree); + + const component = tree.readContent('/projects/foo/src/app/app.ts'); + expect(component).toContain( + `import { Component, signal, ViewEncapsulation } from '@angular/core';`, + ); + expect(component).toContain('encapsulation: ViewEncapsulation.None'); + }); + + it(`should add 'viewEncapsulation' to the component schematic defaults when provided`, async () => { + const options = { ...defaultOptions, viewEncapsulation: ViewEncapsulation.None }; + const tree = await schematicRunner.runSchematic('application', options, workspaceTree); + + const { projects } = JSON.parse(tree.readContent('/angular.json')); + expect(projects.foo.schematics['@schematics/angular:component'].viewEncapsulation).toBe('None'); + }); + + it(`should not set 'encapsulation' in the root component by default`, async () => { + const tree = await schematicRunner.runSchematic('application', defaultOptions, workspaceTree); + + expect(tree.readContent('/projects/foo/src/app/app.ts')).not.toContain('encapsulation'); + const { projects } = JSON.parse(tree.readContent('/angular.json')); + expect(projects.foo.schematics?.['@schematics/angular:component']?.viewEncapsulation).toBe( + undefined, + ); + }); + describe('standalone=false', () => { it('should add the provideZoneChangeDetection with event coalescing option by default with zone.js apps', async () => { const tree = await schematicRunner.runSchematic( @@ -720,7 +749,7 @@ describe('Application Schematic', () => { expect(content).toContain('provideZoneChangeDetection({ eventCoalescing: true })'); }); - it(`should set 'defaultEncapsulation' in main.ts when 'ViewEncapsulation' is provided`, async () => { + it(`should set 'encapsulation' in the root component when 'viewEncapsulation' is provided`, async () => { const tree = await schematicRunner.runSchematic( 'application', { @@ -731,10 +760,12 @@ describe('Application Schematic', () => { workspaceTree, ); - const path = '/projects/foo/src/main.ts'; - const content = tree.readContent(path); - expect(content).toContain('defaultEncapsulation: ViewEncapsulation.ShadowDom'); - expect(content).toContain(`import { ViewEncapsulation } from '@angular/core'`); + const component = tree.readContent('/projects/foo/src/app/app.ts'); + expect(component).toContain( + `import { Component, signal, ViewEncapsulation } from '@angular/core';`, + ); + expect(component).toContain('encapsulation: ViewEncapsulation.ShadowDom'); + expect(tree.readContent('/projects/foo/src/main.ts')).not.toContain('defaultEncapsulation'); }); it('should handle the routing flag', async () => {