From dc52fe0678d26fa011440f43908ede98624e1bd1 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 1 May 2026 10:43:33 +0200 Subject: [PATCH] fix(@schematics/angular): use service decorator in ng generate Updates the `service` schematic in `ng generate` to use the `@Service` decorator instead of `@Injectable`. There's also a new `--injectable` flag to get back the old behavior. --- ...name@dasherize__.__type@dasherize__.ts.template | 6 +++--- packages/schematics/angular/service/index_spec.ts | 14 +++++++++++++- packages/schematics/angular/service/schema.json | 5 +++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/schematics/angular/service/files/__name@dasherize__.__type@dasherize__.ts.template b/packages/schematics/angular/service/files/__name@dasherize__.__type@dasherize__.ts.template index de24346572c2..3b9a10da6ce4 100644 --- a/packages/schematics/angular/service/files/__name@dasherize__.__type@dasherize__.ts.template +++ b/packages/schematics/angular/service/files/__name@dasherize__.__type@dasherize__.ts.template @@ -1,8 +1,8 @@ -import { Injectable } from '@angular/core'; +import { <%= injectable ? 'Injectable' : 'Service' %> } from '@angular/core'; -@Injectable({ +<% if (injectable) { %>@Injectable({ providedIn: 'root', -}) +})<% } else { %>@Service()<% } %> export class <%= classifiedName %> { } diff --git a/packages/schematics/angular/service/index_spec.ts b/packages/schematics/angular/service/index_spec.ts index 56ae5edd2428..023b738b46c7 100644 --- a/packages/schematics/angular/service/index_spec.ts +++ b/packages/schematics/angular/service/index_spec.ts @@ -36,6 +36,7 @@ describe('Service Schematic', () => { skipPackageJson: false, }; let appTree: UnitTestTree; + beforeEach(async () => { appTree = await schematicRunner.runSchematic('workspace', workspaceOptions); appTree = await schematicRunner.runSchematic('application', appOptions, appTree); @@ -50,12 +51,23 @@ describe('Service Schematic', () => { expect(files).toContain('/projects/bar/src/app/foo/foo.ts'); }); - it('service should be tree-shakeable', async () => { + it('should use @Service decorator', async () => { const options = { ...defaultOptions }; const tree = await schematicRunner.runSchematic('service', options, appTree); const content = tree.readContent('/projects/bar/src/app/foo/foo.ts'); + expect(content).toMatch(/@Service\(\)/); + expect(content).toMatch(/import \{ Service \} from '@angular\/core'/); + }); + + it('should use @Injectable decorator when injectable flag is true', async () => { + const options = { ...defaultOptions, injectable: true }; + + const tree = await schematicRunner.runSchematic('service', options, appTree); + const content = tree.readContent('/projects/bar/src/app/foo/foo.ts'); + expect(content).toMatch(/@Injectable\(/); expect(content).toMatch(/providedIn: 'root',/); + expect(content).toMatch(/import \{ Injectable \} from '@angular\/core'/); }); it('should respect the skipTests flag', async () => { diff --git a/packages/schematics/angular/service/schema.json b/packages/schematics/angular/service/schema.json index 19afac150262..3954a0a032b8 100644 --- a/packages/schematics/angular/service/schema.json +++ b/packages/schematics/angular/service/schema.json @@ -48,6 +48,11 @@ "type": "boolean", "default": true, "description": "When true, the 'type' option will be appended to the generated class name. When false, only the file name will include the type." + }, + "injectable": { + "type": "boolean", + "default": false, + "description": "When true, generates an @Injectable instead of @Service." } }, "required": ["name", "project"]