Skip to content

Commit 331b16e

Browse files
crisbetodylhunn
authored andcommitted
feat(core): add API to inject attributes on the host node (#54604)
Angular has the `@Attribute` decorator that allows for attributes to be injected from the host node, but we don't have an equivalent for the `inject` function. These changes introduce the new `HostAttributeToken` class that can be used to inject attributes similarly to `@Attribute`. It can be used as follows: ```typescript import {HostAttributeToken, inject} from '@angular/core'; class MyDir { someAttr = inject(new HostAttributeToken('some-attr')); } ``` The new API works similarly to `@Attribute` with one key exception: it will throw a DI error when the attribute doesn't exist, instead of returning `null` like `@Attribute`. We made this change to align its behavior closer to other injection tokens. PR Close #54604
1 parent 4066e62 commit 331b16e

File tree

6 files changed

+396
-5
lines changed

6 files changed

+396
-5
lines changed

goldens/public-api/core/index.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,13 @@ export interface Host {
720720
// @public
721721
export const Host: HostDecorator;
722722

723+
// @public
724+
export class HostAttributeToken {
725+
constructor(attributeName: string);
726+
// (undocumented)
727+
toString(): string;
728+
}
729+
723730
// @public
724731
export interface HostBinding {
725732
hostPropertyName?: string;
@@ -789,6 +796,19 @@ export function inject<T>(token: ProviderToken<T>, options: InjectOptions & {
789796
// @public (undocumented)
790797
export function inject<T>(token: ProviderToken<T>, options: InjectOptions): T | null;
791798

799+
// @public (undocumented)
800+
export function inject(token: HostAttributeToken): string;
801+
802+
// @public (undocumented)
803+
export function inject(token: HostAttributeToken, options: {
804+
optional: true;
805+
}): string | null;
806+
807+
// @public (undocumented)
808+
export function inject(token: HostAttributeToken, options: {
809+
optional: false;
810+
}): string;
811+
792812
// @public
793813
export interface Injectable {
794814
providedIn?: Type<any> | 'root' | 'platform' | 'any' | null;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*!
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {ɵɵinjectAttribute} from '../render3/instructions/di_attr';
10+
11+
/**
12+
* Creates a token that can be used to inject static attributes of the host node.
13+
*
14+
* @usageNotes
15+
* ### Injecting an attribute that is known to exist
16+
* ```typescript
17+
* @Directive()
18+
* class MyDir {
19+
* attr: string = inject(new HostAttributeToken('some-attr'));
20+
* }
21+
* ```
22+
*
23+
* ### Optionally injecting an attribute
24+
* ```typescript
25+
* @Directive()
26+
* class MyDir {
27+
* attr: string | null = inject(new HostAttributeToken('some-attr'), {optional: true});
28+
* }
29+
* ```
30+
* @publicApi
31+
*/
32+
export class HostAttributeToken {
33+
constructor(private attributeName: string) {}
34+
35+
/** @internal */
36+
__NG_ELEMENT_ID__ = () => ɵɵinjectAttribute(this.attributeName);
37+
38+
toString(): string {
39+
return `HostAttributeToken ${this.attributeName}`;
40+
}
41+
}

packages/core/src/di/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,4 @@ export {InjectOptions} from './interface/injector';
2828
export {INJECTOR} from './injector_token';
2929
export {ClassProvider, ModuleWithProviders, ClassSansProvider, ImportedNgModuleProviders, ConstructorProvider, EnvironmentProviders, ConstructorSansProvider, ExistingProvider, ExistingSansProvider, FactoryProvider, FactorySansProvider, Provider, StaticClassProvider, StaticClassSansProvider, StaticProvider, TypeProvider, ValueProvider, ValueSansProvider} from './interface/provider';
3030
export {InjectionToken} from './injection_token';
31+
export {HostAttributeToken} from './host_attribute_token';

packages/core/src/di/injector_compatibility.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {getInjectImplementation, injectRootLimpMode} from './inject_switch';
1818
import type {Injector} from './injector';
1919
import {DecoratorFlags, InjectFlags, InjectOptions, InternalInjectFlags} from './interface/injector';
2020
import {ProviderToken} from './provider_token';
21+
import type {HostAttributeToken} from './host_attribute_token';
2122

2223

2324
const _THROW_IF_NOT_FOUND = {};
@@ -85,8 +86,14 @@ export function injectInjectorOnly<T>(token: ProviderToken<T>, flags = InjectFla
8586
*/
8687
export function ɵɵinject<T>(token: ProviderToken<T>): T;
8788
export function ɵɵinject<T>(token: ProviderToken<T>, flags?: InjectFlags): T|null;
88-
export function ɵɵinject<T>(token: ProviderToken<T>, flags = InjectFlags.Default): T|null {
89-
return (getInjectImplementation() || injectInjectorOnly)(resolveForwardRef(token), flags);
89+
export function ɵɵinject(token: HostAttributeToken): string;
90+
export function ɵɵinject(token: HostAttributeToken, flags?: InjectFlags): string|null;
91+
export function ɵɵinject<T>(
92+
token: ProviderToken<T>|HostAttributeToken, flags?: InjectFlags): string|null;
93+
export function ɵɵinject<T>(
94+
token: ProviderToken<T>|HostAttributeToken, flags = InjectFlags.Default): T|null {
95+
return (getInjectImplementation() || injectInjectorOnly)(
96+
resolveForwardRef(token as Type<T>), flags);
9097
}
9198

9299
/**
@@ -153,6 +160,30 @@ export function inject<T>(token: ProviderToken<T>, options: InjectOptions&{optio
153160
* @publicApi
154161
*/
155162
export function inject<T>(token: ProviderToken<T>, options: InjectOptions): T|null;
163+
/**
164+
* @param token A token that represents a static attribute on the host node that should be injected.
165+
* @returns Value of the attribute if it exists.
166+
* @throws If called outside of a supported context or the attribute does not exist.
167+
*
168+
* @publicApi
169+
*/
170+
export function inject(token: HostAttributeToken): string;
171+
/**
172+
* @param token A token that represents a static attribute on the host node that should be injected.
173+
* @returns Value of the attribute if it exists, otherwise `null`.
174+
* @throws If called outside of a supported context.
175+
*
176+
* @publicApi
177+
*/
178+
export function inject(token: HostAttributeToken, options: {optional: true}): string|null;
179+
/**
180+
* @param token A token that represents a static attribute on the host node that should be injected.
181+
* @returns Value of the attribute if it exists.
182+
* @throws If called outside of a supported context or the attribute does not exist.
183+
*
184+
* @publicApi
185+
*/
186+
export function inject(token: HostAttributeToken, options: {optional: false}): string;
156187
/**
157188
* Injects a token from the currently active injector.
158189
* `inject` is only supported in an [injection context](/guide/dependency-injection-context). It can
@@ -219,8 +250,11 @@ export function inject<T>(token: ProviderToken<T>, options: InjectOptions): T|nu
219250
* @publicApi
220251
*/
221252
export function inject<T>(
222-
token: ProviderToken<T>, flags: InjectFlags|InjectOptions = InjectFlags.Default): T|null {
223-
return ɵɵinject(token, convertToBitFlags(flags));
253+
token: ProviderToken<T>|HostAttributeToken,
254+
flags: InjectFlags|InjectOptions = InjectFlags.Default) {
255+
// The `as any` here _shouldn't_ be necessary, but without it JSCompiler
256+
// throws a disambiguation error due to the multiple signatures.
257+
return ɵɵinject(token as any, convertToBitFlags(flags));
224258
}
225259

226260
// Converts object-based DI flags (`InjectOptions`) to bit flags (`InjectFlags`).

0 commit comments

Comments
 (0)