diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts
index fedee6c755..41a4a91bce 100644
--- a/packages/core/src/core_render3_private_export.ts
+++ b/packages/core/src/core_render3_private_export.ts
@@ -18,6 +18,7 @@ export {
injectTemplateRef as ɵinjectTemplateRef,
injectViewContainerRef as ɵinjectViewContainerRef,
injectChangeDetectorRef as ɵinjectChangeDetectorRef,
+ injectAttribute as ɵinjectAttribute,
InjectFlags as ɵInjectFlags,
PublicFeature as ɵPublicFeature,
NgOnChangesFeature as ɵNgOnChangesFeature,
diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts
index 27446d1503..120b7d00e2 100644
--- a/packages/core/src/render3/di.ts
+++ b/packages/core/src/render3/di.ts
@@ -18,7 +18,7 @@ import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_co
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_ViewRef} from '../linker/view_ref';
import {Type} from '../type';
-import {assertLessThan} from './assert';
+import {assertLessThan, assertNotNull} from './assert';
import {assertPreviousIsParent, getDirectiveInstance, getPreviousOrParentNode, getRenderer, renderEmbeddedTemplate} from './instructions';
import {ComponentTemplate, DirectiveDef} from './interfaces/definition';
import {LInjector} from './interfaces/injector';
@@ -234,6 +234,54 @@ export function injectChangeDetectorRef(): viewEngine_ChangeDetectorRef {
return getOrCreateChangeDetectorRef(getOrCreateNodeInjector(), null);
}
+/**
+ * Inject static attribute value into directive constructor.
+ *
+ * This method is used with `factory` functions which are generated as part of
+ * `defineDirective` or `defineComponent`. The method retrieves the static value
+ * of an attribute. (Dynamic attributes are not supported since they are not resolved
+ * at the time of injection and can change over time.)
+ *
+ * # Example
+ * Given:
+ * ```
+ * @Component(...)
+ * class MyComponent {
+ * constructor(@Attribute('title') title: string) { ... }
+ * }
+ * ```
+ * When instantiated with
+ * ```
+ *
+ * ```
+ *
+ * Then factory method generated is:
+ * ```
+ * MyComponent.ngComponentDef = defineComponent({
+ * factory: () => new MyComponent(injectAttribute('title'))
+ * ...
+ * })
+ * ```
+ *
+ * @experimental
+ */
+export function injectAttribute(attrName: string): string|undefined {
+ ngDevMode && assertPreviousIsParent();
+ const lElement = getPreviousOrParentNode() as LElementNode;
+ ngDevMode && assertNodeType(lElement, LNodeFlags.Element);
+ const tElement = lElement.tNode !;
+ ngDevMode && assertNotNull(tElement, 'expecting tNode');
+ const attrs = tElement.attrs;
+ if (attrs) {
+ for (let i = 0; i < attrs.length; i = i + 2) {
+ if (attrs[i] == attrName) {
+ return attrs[i + 1];
+ }
+ }
+ }
+ return undefined;
+}
+
/**
* Creates a ViewRef and stores it on the injector as ChangeDetectorRef (public alias).
* Or, if it already exists, retrieves the existing instance.
diff --git a/packages/core/src/render3/index.ts b/packages/core/src/render3/index.ts
index 6391cb5a9b..7d764f6f34 100644
--- a/packages/core/src/render3/index.ts
+++ b/packages/core/src/render3/index.ts
@@ -11,10 +11,11 @@ import {NgOnChangesFeature, PublicFeature, defineComponent, defineDirective, def
import {InjectFlags} from './di';
import {ComponentDef, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveType} from './interfaces/definition';
-export {InjectFlags, QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, inject, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from './di';
+export {InjectFlags, QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, inject, injectAttribute, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from './di';
export {CssSelector} from './interfaces/projection';
+
// Naming scheme:
// - Capital letters are for creating things: T(Text), E(Element), D(Directive), V(View),
// C(Container), L(Listener)
diff --git a/packages/core/test/render3/compiler_canonical/injection_spec.ts b/packages/core/test/render3/compiler_canonical/injection_spec.ts
index 463a4a7b60..5e69202be0 100644
--- a/packages/core/test/render3/compiler_canonical/injection_spec.ts
+++ b/packages/core/test/render3/compiler_canonical/injection_spec.ts
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
+import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
import * as $r3$ from '../../../src/core_render3_private_export';
import {renderComponent, toHtml} from '../render_util';
@@ -60,4 +60,49 @@ describe('injection', () => {
expect(toHtml(app)).toEqual('ViewRef');
});
+ it('should inject attributes', () => {
+ type $MyComp$ = MyComp;
+ type $MyApp$ = MyApp;
+
+ @Component({selector: 'my-comp', template: `{{ title }}`})
+ class MyComp {
+ constructor(@Attribute('title') public title: string|undefined) {}
+
+ // NORMATIVE
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyComp,
+ tag: 'my-comp',
+ factory: function MyComp_Factory() { return new MyComp($r3$.ɵinjectAttribute('title')); },
+ template: function MyComp_Template(ctx: $MyComp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵT(0);
+ }
+ $r3$.ɵt(0, $r3$.ɵb(ctx.title));
+ }
+ });
+ // /NORMATIVE
+ }
+
+ class MyApp {
+ static ngComponentDef = $r3$.ɵdefineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: function MyApp_Factory() { return new MyApp(); },
+ /** */
+ template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
+ if (cm) {
+ $r3$.ɵE(0, MyComp, e0_attrs);
+ $r3$.ɵe();
+ }
+ MyComp.ngComponentDef.h(1, 0);
+ $r3$.ɵr(1, 0);
+ }
+ });
+ }
+ const e0_attrs = ['title', 'WORKS'];
+ const app = renderComponent(MyApp);
+ // ChangeDetectorRef is the token, ViewRef is historically the constructor
+ expect(toHtml(app)).toEqual('WORKS');
+ });
+
});
\ No newline at end of file
diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts
index 97c79c2bb7..33b97de204 100644
--- a/packages/core/test/render3/di_spec.ts
+++ b/packages/core/test/render3/di_spec.ts
@@ -9,7 +9,7 @@
import {ChangeDetectorRef, ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
import {defineComponent} from '../../src/render3/definition';
-import {InjectFlags, bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector} from '../../src/render3/di';
+import {InjectFlags, bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector, injectAttribute} from '../../src/render3/di';
import {NgOnChangesFeature, PublicFeature, defineDirective, inject, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, createLNode, createLView, createTView, directiveRefresh, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, load, projection, projectionDef, text, textBinding} from '../../src/render3/instructions';
import {LInjector} from '../../src/render3/interfaces/injector';
@@ -465,6 +465,29 @@ describe('di', () => {
expect(dir !.cdr).toBe(dirSameInstance !.cdr);
});
+ it('should injectAttribute', () => {
+ let exist: string|undefined = 'wrong';
+ let nonExist: string|undefined = 'wrong';
+ class MyApp {
+ static ngComponentDef = defineComponent({
+ type: MyApp,
+ tag: 'my-app',
+ factory: () => new MyApp(),
+ template: function(ctx: MyApp, cm: boolean) {
+ if (cm) {
+ elementStart(0, 'div', ['exist', 'existValue', 'other', 'ignore']);
+ exist = injectAttribute('exist');
+ nonExist = injectAttribute('nonExist');
+ }
+ }
+ });
+ }
+
+ const app = renderComponent(MyApp);
+ expect(exist).toEqual('existValue');
+ expect(nonExist).toEqual(undefined);
+ });
+
});
describe('inject', () => {