Compare commits

...

11 Commits

Author SHA1 Message Date
dce1c7cf58 build: fix comment formatting to make closure happy 2018-10-25 16:38:52 -07:00
2c7386c961 feat(ivy): support injecting the injector (#26699)
PR Close #26699
2018-10-25 18:47:56 -04:00
d5cbcef0ea fix(core): ignore comment nodes under unsafe elements (#25879)
Comment nodes that are child nodes of unsafe elements are identified as text nodes. This results in the comment node being returned as an encoded string.
Add a check to ignore such comment nodes.

PR Close #25879
2018-10-25 11:20:19 -07:00
b0476f308b feat(ivy): support providers and viewProviders (#25803)
PR Close #25803
2018-10-25 12:58:40 -04:00
9dc52d9d04 feat(ivy): expose a series of debug console tools (#26705)
PR Close #26705
2018-10-24 20:30:51 -04:00
297dc2bc02 fix(ivy): ensure ngClass works with [class] bindings (#26559)
PR Close #26559
2018-10-24 20:27:12 -04:00
0cc9842bf6 test(upgrade): make e2e tests for upgrade docs examples less flaky (#26726)
PR Close #26726
2018-10-24 19:49:14 -04:00
54ea10288e test(elements): make e2e tests for elements docs examples even less flaky (#26726)
PR Close #26726
2018-10-24 19:49:14 -04:00
1880c9531f ci: only publish builds if relevant aio jobs pass (#26722)
Some of the `aio`-/`docs`-related jobs rely on the locally built Angular
packages. When these jobs fail, it could mean that there is an issue
with the Angular packages (e.g. an unintentional breaking change).

This commit ensures that the `publish_artifacts` job is not run, unless
those `aio`-/`docs`-related jobs pass.

(The `test_aio_tools` job also uses the locally built Angular packages,
but it does not exercise them in a meaningful way to be worth making it
a prerequisite for `publish_artifacts`.)

PR Close #26722
2018-10-24 19:48:41 -04:00
13a803d4f7 build(docs-infra): fix parameter type rendering (#26688)
Closes #24355

PR Close #26688
2018-10-24 19:48:10 -04:00
f6c2db818e fix(ivy): ensure styling pipes are allocated before used in bindings (#26593)
PR Close #26593
2018-10-24 18:42:59 -04:00
103 changed files with 4780 additions and 1866 deletions

View File

@ -423,6 +423,10 @@ workflows:
- test_ivy_jit
- test_ivy_aot
- integration_test
# Only publish if `aio`/`docs` tests using the locally built Angular packages pass
- test_aio_local
- test_docs_examples_0
- test_docs_examples_1
# Get the artifacts to publish from the build-packages-dist job
# since the publishing script expects the legacy outputs layout.
- build-packages-dist

View File

@ -13,6 +13,10 @@ describe('Elements', () => {
browser.wait(EC.elementToBeClickable(elem), 5000);
elem.click();
};
const waitForText = (elem: ElementFinder) => {
// Waiting for the element to have some text, makes the tests less flaky.
browser.wait(async () => /\S/.test(await elem.getText()), 5000);
}
beforeEach(() => browser.get(''));
@ -33,6 +37,8 @@ describe('Elements', () => {
messageInput.sendKeys('Angular rocks!');
click(popupComponentButton);
waitForText(popupComponent);
expect(popupComponent.getText()).toContain('Popup: Angular rocks!');
});
@ -62,6 +68,8 @@ describe('Elements', () => {
messageInput.sendKeys('Angular rocks!');
click(popupElementButton);
waitForText(popupElement);
expect(popupElement.getText()).toContain('Popup: Angular rocks!');
});

View File

@ -1,6 +1,6 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by, ElementFinder } from 'protractor';
import { browser, element, by, ElementArrayFinder, ElementFinder } from 'protractor';
// Angular E2E Testing Guide:
// https://docs.angularjs.org/guide/e2e-testing
@ -20,6 +20,12 @@ describe('PhoneCat Application', function() {
describe('View: Phone list', function() {
// Helpers
const waitForCount = (elems: ElementArrayFinder, count: number) => {
// Wait for the list to stabilize, which may take a while (e.g. due to animations).
browser.wait(() => elems.count().then(c => c === count), 5000);
};
beforeEach(function() {
browser.get('index.html#!/phones');
});
@ -28,13 +34,16 @@ describe('PhoneCat Application', function() {
let phoneList = element.all(by.repeater('phone in $ctrl.phones'));
let query = element(by.model('$ctrl.query'));
waitForCount(phoneList, 20);
expect(phoneList.count()).toBe(20);
query.sendKeys('nexus');
waitForCount(phoneList, 1);
expect(phoneList.count()).toBe(1);
query.clear();
query.sendKeys('motorola');
waitForCount(phoneList, 8);
expect(phoneList.count()).toBe(8);
});
@ -51,6 +60,7 @@ describe('PhoneCat Application', function() {
}
queryField.sendKeys('tablet'); // Let's narrow the dataset to make the assertions shorter
waitForCount(phoneNameColumn, 2);
expect(getNames()).toEqual([
'Motorola XOOM\u2122 with Wi-Fi',
@ -66,10 +76,16 @@ describe('PhoneCat Application', function() {
});
it('should render phone specific links', function() {
let phoneList = element.all(by.repeater('phone in $ctrl.phones'));
let query = element(by.model('$ctrl.query'));
query.sendKeys('nexus');
element.all(by.css('.phones li a')).first().click();
query.sendKeys('nexus');
waitForCount(phoneList, 1);
let nexusPhone = phoneList.first();
let detailLink = nexusPhone.all(by.css('a')).first()
detailLink.click();
expect(browser.getLocationAbsUrl()).toBe('/phones/nexus-s');
});

View File

@ -109,7 +109,7 @@
"cross-spawn": "^5.1.0",
"css-selector-parser": "^1.3.0",
"dgeni": "^0.4.7",
"dgeni-packages": "^0.26.9",
"dgeni-packages": "^0.26.12",
"entities": "^1.1.1",
"eslint": "^3.19.0",
"eslint-plugin-jasmine": "^2.2.0",
@ -163,4 +163,4 @@
"xregexp": "^4.0.0",
"yargs": "^7.0.2"
}
}
}

View File

@ -3085,10 +3085,10 @@ devtools-timeline-model@1.1.6:
chrome-devtools-frontend "1.0.401423"
resolve "1.1.7"
dgeni-packages@^0.26.9:
version "0.26.9"
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.26.9.tgz#64d0eb89046bcaec8417036c18002bdbd5062dbc"
integrity sha512-wvY/74ytYVdX/CMGs4jaP6PJUeUWcYyMjSCuYUVSbgEVU40SdE7lMnAlJuvTr/wTH3Zfx5BV/WTiVmzBwbXvow==
dgeni-packages@^0.26.12:
version "0.26.12"
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.26.12.tgz#6b8329b59a17213d30137b53a689d579cd6a9fb1"
integrity sha512-dYuRYnw+sURD82F6JJ+2erOKOlaReBymR9g2bjpws3GSIpBbX1p1kE9VYwmAaBoZRE2h+woK8RyDktZvkzETfg==
dependencies:
canonical-path "0.0.2"
catharsis "^0.8.1"

View File

@ -128,7 +128,7 @@ describe('Renderer', () => {
}));
expect(addDefinitionsSpy.calls.first().args[2])
.toEqual(
`A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""]], factory: function A_Factory(t) { return new (t || A)(); }, features: [ɵngcc0.ɵPublicFeature] });`);
`A.ngDirectiveDef = ɵngcc0.ɵdefineDirective({ type: A, selectors: [["", "a", ""]], factory: function A_Factory(t) { return new (t || A)(); } });`);
});
it('should call removeDecorators with the source code, a map of class decorators that have been analyzed',

View File

@ -118,6 +118,10 @@ export class ComponentDecoratorHandler implements
preserveWhitespaces = value;
}
const viewProviders: Expression|null = component.has('viewProviders') ?
new WrappedNodeExpr(component.get('viewProviders') !) :
null;
// Go through the root directories for this project, and select the one with the smallest
// relative path representation.
const filePath = node.getSourceFile().fileName;
@ -202,6 +206,7 @@ export class ComponentDecoratorHandler implements
directives: EMPTY_MAP,
wrapDirectivesInClosure: false, //
animations,
viewProviders
},
parsedTemplate: template.nodes,
},

View File

@ -146,6 +146,9 @@ export function extractDirectiveMetadata(
const host = extractHostBindings(directive, decoratedElements, reflector, checker, coreModule);
const providers: Expression|null =
directive.has('providers') ? new WrappedNodeExpr(directive.get('providers') !) : null;
// Determine if `ngOnChanges` is a lifecycle hook defined on the component.
const usesOnChanges = members.some(
member => !member.isStatic && member.kind === ClassMemberKind.Method &&
@ -176,7 +179,7 @@ export function extractDirectiveMetadata(
outputs: {...outputsFromMeta, ...outputsFromFields}, queries, selector,
type: new WrappedNodeExpr(clazz.name !),
typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0,
typeSourceSpan: null !, usesInheritance, exportAs,
typeSourceSpan: null !, usesInheritance, exportAs, providers
};
return {decoratedElements, decorator: directive, metadata};
}

View File

@ -64,18 +64,6 @@ export function getConstructorDependencies(
ErrorCode.PARAM_MISSING_TOKEN, param.nameNode,
`No suitable token for parameter ${param.name || idx} of class ${clazz.name!.text}`);
}
if (ts.isIdentifier(tokenExpr)) {
const importedSymbol = reflector.getImportOfIdentifier(tokenExpr);
if (importedSymbol !== null && importedSymbol.from === '@angular/core') {
switch (importedSymbol.name) {
case 'Injector':
resolved = R3ResolvedDependencyType.Injector;
break;
default:
// Leave as a Token or Attribute.
}
}
}
const token = new WrappedNodeExpr(tokenExpr);
useType.push({token, optional, self, skipSelf, host, resolved});
});

View File

@ -415,7 +415,6 @@ describe('compiler compliance', () => {
factory: function MyComponent_Factory(t){
return new (t || MyComponent)();
},
features: [$r3$.ɵPublicFeature],
consts: 1,
vars: 0,
template: function MyComponent_Template(rf,ctx){
@ -470,7 +469,6 @@ describe('compiler compliance', () => {
type: ChildComponent,
selectors: [["child"]],
factory: function ChildComponent_Factory(t) { return new (t || ChildComponent)(); },
features: [$r3$.ɵPublicFeature],
consts: 1,
vars: 0,
template: function ChildComponent_Template(rf, ctx) {
@ -485,8 +483,7 @@ describe('compiler compliance', () => {
SomeDirective.ngDirectiveDef = $r3$.ɵdefineDirective({
type: SomeDirective,
selectors: [["", "some-directive", ""]],
factory: function SomeDirective_Factory(t) {return new (t || SomeDirective)(); },
features: [$r3$.ɵPublicFeature]
factory: function SomeDirective_Factory(t) {return new (t || SomeDirective)(); }
});
`;
@ -498,7 +495,6 @@ describe('compiler compliance', () => {
type: MyComponent,
selectors: [["my-component"]],
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
features: [$r3$.ɵPublicFeature],
consts: 2,
vars: 0,
template: function MyComponent_Template(rf, ctx) {
@ -543,8 +539,7 @@ describe('compiler compliance', () => {
SomeDirective.ngDirectiveDef = $r3$.ɵdefineDirective({
type: SomeDirective,
selectors: [["div", "some-directive", "", 8, "foo", 3, "title", "", 9, "baz"]],
factory: function SomeDirective_Factory(t) {return new (t || SomeDirective)(); },
features: [$r3$.ɵPublicFeature]
factory: function SomeDirective_Factory(t) {return new (t || SomeDirective)(); }
});
`;
@ -553,8 +548,7 @@ describe('compiler compliance', () => {
OtherDirective.ngDirectiveDef = $r3$.ɵdefineDirective({
type: OtherDirective,
selectors: [["", 5, "span", "title", "", 9, "baz"]],
factory: function OtherDirective_Factory(t) {return new (t || OtherDirective)(); },
features: [$r3$.ɵPublicFeature]
factory: function OtherDirective_Factory(t) {return new (t || OtherDirective)(); }
});
`;
@ -590,8 +584,7 @@ describe('compiler compliance', () => {
hostBindings: function HostBindingDir_HostBindings(dirIndex, elIndex) {
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵload(dirIndex).dirId));
},
hostVars: 1,
features: [$r3$.ɵPublicFeature]
hostVars: 1
});
`;
@ -635,7 +628,6 @@ describe('compiler compliance', () => {
$r3$.ɵelementProperty(elIndex, "id", $r3$.ɵbind($r3$.ɵpureFunction1(1, $ff$, $r3$.ɵload(dirIndex).id)));
},
hostVars: 3,
features: [$r3$.ɵPublicFeature],
consts: 0,
vars: 0,
template: function HostBindingComp_Template(rf, ctx) {}
@ -679,7 +671,6 @@ describe('compiler compliance', () => {
$r3$.ɵdirectiveInject(ElementRef), $r3$.ɵdirectiveInject(ViewContainerRef),
$r3$.ɵdirectiveInject(ChangeDetectorRef));
},
features: [$r3$.ɵPublicFeature],
consts: 0,
vars: 0,
template: function MyComponent_Template(rf, ctx) {}
@ -720,8 +711,7 @@ describe('compiler compliance', () => {
IfDirective.ngDirectiveDef = $r3$.ɵdefineDirective({
type: IfDirective,
selectors: [["", "if", ""]],
factory: function IfDirective_Factory(t) { return new (t || IfDirective)($r3$.ɵdirectiveInject(TemplateRef)); },
features: [$r3$.ɵPublicFeature]
factory: function IfDirective_Factory(t) { return new (t || IfDirective)($r3$.ɵdirectiveInject(TemplateRef)); }
});`;
const MyComponentDefinition = `
const $c1$ = ["foo", ""];
@ -743,7 +733,6 @@ describe('compiler compliance', () => {
type: MyComponent,
selectors: [["my-component"]],
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
features: [$r3$.ɵPublicFeature],
consts: 3,
vars: 0,
template: function MyComponent_Template(rf, ctx) {
@ -806,7 +795,6 @@ describe('compiler compliance', () => {
type: MyApp,
selectors: [["my-app"]],
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
features: [$r3$.ɵPublicFeature],
consts: 1,
vars: 3,
template: function MyApp_Template(rf, ctx) {
@ -888,7 +876,6 @@ describe('compiler compliance', () => {
type: MyApp,
selectors: [["my-app"]],
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
features: [$r3$.ɵPublicFeature],
consts: 1,
vars: 11,
template: function MyApp_Template(rf, ctx) {
@ -952,7 +939,6 @@ describe('compiler compliance', () => {
type: MyApp,
selectors: [["my-app"]],
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
features: [$r3$.ɵPublicFeature],
consts: 1,
vars: 3,
template: function MyApp_Template(rf, ctx) {
@ -1020,7 +1006,6 @@ describe('compiler compliance', () => {
type: MyApp,
selectors: [["my-app"]],
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
features: [$r3$.ɵPublicFeature],
consts: 1,
vars: 8,
template: function MyApp_Template(rf, ctx) {
@ -1079,7 +1064,6 @@ describe('compiler compliance', () => {
type: SimpleComponent,
selectors: [["simple"]],
factory: function SimpleComponent_Factory(t) { return new (t || SimpleComponent)(); },
features: [$r3$.ɵPublicFeature],
consts: 2,
vars: 0,
template: function SimpleComponent_Template(rf, ctx) {
@ -1102,7 +1086,6 @@ describe('compiler compliance', () => {
type: ComplexComponent,
selectors: [["complex"]],
factory: function ComplexComponent_Factory(t) { return new (t || ComplexComponent)(); },
features: [$r3$.ɵPublicFeature],
consts: 4,
vars: 0,
template: function ComplexComponent_Template(rf, ctx) {
@ -1171,7 +1154,6 @@ describe('compiler compliance', () => {
type: ViewQueryComponent,
selectors: [["view-query-component"]],
factory: function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); },
features: [$r3$.ɵPublicFeature],
viewQuery: function ViewQueryComponent_Query(rf, ctx) {
if (rf & 1) {
$r3$.ɵquery(0, SomeDirective, true);
@ -1348,9 +1330,9 @@ describe('compiler compliance', () => {
factory: function ContentQueryComponent_Factory(t) {
return new (t || ContentQueryComponent)();
},
contentQueries: function ContentQueryComponent_ContentQueries() {
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, true));
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false));
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, true), dirIndex);
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false), dirIndex);
},
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
const instance = $r3$.ɵload(dirIndex);
@ -1358,7 +1340,6 @@ describe('compiler compliance', () => {
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList(queryStartIndex))) && ($instance$.someDir = $tmp$.first));
($r3$.ɵqueryRefresh(($tmp$ = $r3$.ɵloadQueryList((queryStartIndex + 1)))) && ($instance$.someDirList = $tmp$));
},
features: [$r3$.ɵPublicFeature],
consts: 2,
vars: 0,
template: function ContentQueryComponent_Template(rf, ctx) {
@ -1406,9 +1387,9 @@ describe('compiler compliance', () => {
ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({
contentQueries: function ContentQueryComponent_ContentQueries() {
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e0_attrs$, true));
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e1_attrs$, false));
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e0_attrs$, true), dirIndex);
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e1_attrs$, false), dirIndex);
},
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
const instance = $r3$.ɵload(dirIndex);
@ -1459,11 +1440,11 @@ describe('compiler compliance', () => {
ContentQueryComponent.ngComponentDef = $r3$.ɵdefineComponent({
contentQueries: function ContentQueryComponent_ContentQueries() {
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e0_attrs$ , true, TemplateRef));
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, true, ElementRef));
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e1_attrs$, false, ElementRef));
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false, TemplateRef));
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex) {
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e0_attrs$ , true, TemplateRef), dirIndex);
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, true, ElementRef), dirIndex);
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, $e1_attrs$, false, ElementRef), dirIndex);
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false, TemplateRef), dirIndex);
},
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(dirIndex, queryStartIndex) {
const instance = $r3$.ɵload(dirIndex);
@ -1552,7 +1533,6 @@ describe('compiler compliance', () => {
type: MyApp,
selectors: [["my-app"]],
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
features: [$r3$.ɵPublicFeature],
consts: 6,
vars: 17,
template: function MyApp_Template(rf, ctx) {
@ -1616,7 +1596,6 @@ describe('compiler compliance', () => {
type: MyApp,
selectors: [["my-app"]],
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
features: [$r3$.ɵPublicFeature],
consts: 6,
vars: 27,
template: function MyApp_Template(rf, ctx) {
@ -1671,7 +1650,6 @@ describe('compiler compliance', () => {
type: MyComponent,
selectors: [["my-component"]],
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
features: [$r3$.ɵPublicFeature],
consts: 3,
vars: 1,
template: function MyComponent_Template(rf, ctx) {
@ -1765,7 +1743,6 @@ describe('compiler compliance', () => {
type: MyComponent,
selectors: [["my-component"]],
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
features: [$r3$.ɵPublicFeature],
consts: 6,
vars: 1,
template: function MyComponent_Template(rf, ctx) {
@ -1911,7 +1888,7 @@ describe('compiler compliance', () => {
selectors: [["lifecycle-comp"]],
factory: function LifecycleComp_Factory(t) { return new (t || LifecycleComp)(); },
inputs: {nameMin: "name"},
features: [$r3$PublicFeature, $r3$NgOnChangesFeature],
features: [$r3$.ɵNgOnChangesFeature],
consts: 0,
vars: 0,
template: function LifecycleComp_Template(rf, ctx) {}
@ -1922,7 +1899,6 @@ describe('compiler compliance', () => {
type: SimpleLayout,
selectors: [["simple-layout"]],
factory: function SimpleLayout_Factory(t) { return new (t || SimpleLayout)(); },
features: [$r3$.ɵPublicFeature],
consts: 2,
vars: 2,
template: function SimpleLayout_Template(rf, ctx) {
@ -2032,7 +2008,7 @@ describe('compiler compliance', () => {
factory: function ForOfDirective_Factory(t) {
return new (t || ForOfDirective)($r3$.ɵdirectiveInject(ViewContainerRef), $r3$.ɵdirectiveInject(TemplateRef));
},
features: [$r3$PublicFeature, $r3$NgOnChangesFeature],
features: [$r3$.ɵNgOnChangesFeature],
inputs: {forOf: "forOf"}
});
`;
@ -2052,7 +2028,6 @@ describe('compiler compliance', () => {
type: MyComponent,
selectors: [["my-component"]],
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
features: [$r3$.ɵPublicFeature],
consts: 2,
vars: 1,
template: function MyComponent_Template(rf, ctx){
@ -2108,7 +2083,7 @@ describe('compiler compliance', () => {
factory: function ForOfDirective_Factory(t) {
return new (t || ForOfDirective)($r3$.ɵdirectiveInject(ViewContainerRef), $r3$.ɵdirectiveInject(TemplateRef));
},
features: [$r3$PublicFeature, $r3$NgOnChangesFeature],
features: [$r3$.ɵNgOnChangesFeature],
inputs: {forOf: "forOf"}
});
`;
@ -2131,7 +2106,6 @@ describe('compiler compliance', () => {
type: MyComponent,
selectors: [["my-component"]],
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
features: [$r3$.ɵPublicFeature],
consts: 2,
vars: 1,
template: function MyComponent_Template(rf, ctx) {
@ -2231,7 +2205,6 @@ describe('compiler compliance', () => {
type: MyComponent,
selectors: [["my-component"]],
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
features: [$r3$.ɵPublicFeature],
consts: 2,
vars: 1,
template: function MyComponent_Template(rf, ctx) {

View File

@ -42,7 +42,6 @@ describe('compiler compliance: directives', () => {
type: MyComponent,
selectors: [["my-component"]],
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
features: [$r3$.ɵPublicFeature],
consts: 1,
vars: 0,
template: function MyComponent_Template(rf, ctx) {
@ -88,7 +87,6 @@ describe('compiler compliance: directives', () => {
type: MyComponent,
selectors: [["my-component"]],
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
features: [$r3$.ɵPublicFeature],
consts: 1,
vars: 0,
template: function MyComponent_Template(rf, ctx) {

View File

@ -202,7 +202,6 @@ describe('compiler compliance: listen()', () => {
type: MyComponent,
selectors: [["my-component"]],
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
features: [$r3$.ɵPublicFeature],
consts: 4,
vars: 0,
template: function MyComponent_Template(rf, ctx) {

View File

@ -0,0 +1,152 @@
/**
* @license
* Copyright Google Inc. 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 {setup} from '@angular/compiler/test/aot/test_util';
import {compile, expectEmit} from './mock_compile';
describe('compiler compliance: providers', () => {
const angularFiles = setup({
compileAngular: false,
compileFakeCore: true,
compileAnimations: false,
});
it('should emit the ProvidersFeature feature when providers and viewProviders', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
abstract class Greeter { abstract greet(): string; }
class GreeterEN implements Greeter {
greet() { return 'Hi'; }
}
@Component({
selector: 'my-component',
template: '<div></div>',
providers: [GreeterEN, {provide: Greeter, useClass: GreeterEN}],
viewProviders: [GreeterEN]
})
export class MyComponent {
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
const result = compile(files, angularFiles);
expectEmit(
result.source,
'features: [i0.ɵProvidersFeature([GreeterEN, {provide: Greeter, useClass: GreeterEN}], [GreeterEN])],',
'Incorrect features');
});
it('should emit the ProvidersFeature feature when providers only', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
abstract class Greeter { abstract greet(): string; }
class GreeterEN implements Greeter {
greet() { return 'Hi'; }
}
@Component({
selector: 'my-component',
template: '<div></div>',
providers: [GreeterEN, {provide: Greeter, useClass: GreeterEN}]
})
export class MyComponent {
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
const result = compile(files, angularFiles);
expectEmit(
result.source,
'features: [i0.ɵProvidersFeature([GreeterEN, {provide: Greeter, useClass: GreeterEN}])],',
'Incorrect features');
});
it('should emit the ProvidersFeature feature when viewProviders only', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
abstract class Greeter { abstract greet(): string; }
class GreeterEN implements Greeter {
greet() { return 'Hi'; }
}
@Component({
selector: 'my-component',
template: '<div></div>',
viewProviders: [GreeterEN]
})
export class MyComponent {
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
const result = compile(files, angularFiles);
expectEmit(
result.source, 'features: [i0.ɵProvidersFeature([], [GreeterEN])],', 'Incorrect features');
});
it('should not emit the ProvidersFeature feature when no providers', () => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
abstract class Greeter { abstract greet(): string; }
class GreeterEN implements Greeter {
greet() { return 'Hi'; }
}
@Component({
selector: 'my-component',
template: '<div></div>'
})
export class MyComponent {
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
const result = compile(files, angularFiles);
expectEmit(
result.source, `
export class MyComponent {
}
MyComponent.ngComponentDef = i0.ɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); }, consts: 1, vars: 0, template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
i0.ɵelement(0, "div");
} } });`,
'Incorrect features');
});
});

View File

@ -128,7 +128,6 @@ describe('compiler compliance: styling', () => {
factory:function MyComponent_Factory(t){
return new (t || MyComponent)();
},
features: [$r3$.ɵPublicFeature],
consts: 0,
vars: 0,
template: function MyComponent_Template(rf, $ctx$) {
@ -170,7 +169,6 @@ describe('compiler compliance: styling', () => {
factory:function MyComponent_Factory(t){
return new (t || MyComponent)();
},
features: [$r3$.ɵPublicFeature],
consts: 0,
vars: 0,
template: function MyComponent_Template(rf, $ctx$) {
@ -308,7 +306,6 @@ describe('compiler compliance: styling', () => {
factory:function MyComponent_Factory(t){
return new (t || MyComponent)();
},
features: [$r3$.ɵPublicFeature],
consts: 1,
vars: 1,
template: function MyComponent_Template(rf, $ctx$) {
@ -367,7 +364,6 @@ describe('compiler compliance: styling', () => {
factory: function MyComponent_Factory(t) {
return new (t || MyComponent)();
},
features: [$r3$.ɵPublicFeature],
consts: 1,
vars: 0,
template: function MyComponent_Template(rf, ctx) {
@ -506,7 +502,6 @@ describe('compiler compliance: styling', () => {
factory:function MyComponent_Factory(t){
return new (t || MyComponent)();
},
features: [$r3$.ɵPublicFeature],
consts: 1,
vars: 1,
template: function MyComponent_Template(rf, $ctx$) {
@ -563,7 +558,6 @@ describe('compiler compliance: styling', () => {
factory:function MyComponent_Factory(t){
return new (t || MyComponent)();
},
features: [$r3$.ɵPublicFeature],
consts: 1,
vars: 2,
template: function MyComponent_Template(rf, $ctx$) {
@ -625,5 +619,46 @@ describe('compiler compliance: styling', () => {
expectEmit(result.source, template, 'Incorrect template');
});
it('should stamp out pipe definitions in the creation block if used by styling bindings',
() => {
const files = {
app: {
'spec.ts': `
import {Component, NgModule} from '@angular/core';
@Component({
selector: 'my-component',
template: \`<div [style]="myStyleExp | stylePipe" [class]="myClassExp | classPipe"></div>\`
})
export class MyComponent {
myStyleExp = [{color:'red'}, {color:'blue', duration:1000}]
myClassExp = 'foo bar apple';
}
@NgModule({declarations: [MyComponent]})
export class MyModule {}
`
}
};
const template = `
template: function MyComponent_Template(rf, $ctx$) {
if (rf & 1) {
$r3$.ɵelementStart(0, "div");
$r3$.ɵelementStyling(null, null, $r3$.ɵdefaultStyleSanitizer);
$r3$.ɵpipe(1, "classPipe");
$r3$.ɵpipe(2, "stylePipe");
$r3$.ɵelementEnd();
}
if (rf & 2) {
$r3$.ɵelementStylingMap(0, $r3$.ɵpipeBind1(1, 0, $ctx$.myClassExp), $r3$.ɵpipeBind1(2, 2, $ctx$.myStyleExp));
$r3$.ɵelementStylingApply(0);
}
}
`;
const result = compile(files, angularFiles);
expectEmit(result.source, template, 'Incorrect template');
});
});
});

View File

@ -380,7 +380,7 @@ describe('ngtsc behavioral tests', () => {
const jsContents = env.getContents('test.js');
expect(jsContents)
.toContain(
`factory: function FooCmp_Factory(t) { return new (t || FooCmp)(i0.ɵinjectAttribute("test"), i0.ɵdirectiveInject(ChangeDetectorRef), i0.ɵdirectiveInject(ElementRef), i0.ɵdirectiveInject(i0.INJECTOR), i0.ɵdirectiveInject(Renderer2), i0.ɵdirectiveInject(TemplateRef), i0.ɵdirectiveInject(ViewContainerRef)); }`);
`factory: function FooCmp_Factory(t) { return new (t || FooCmp)(i0.ɵinjectAttribute("test"), i0.ɵdirectiveInject(ChangeDetectorRef), i0.ɵdirectiveInject(ElementRef), i0.ɵdirectiveInject(Injector), i0.ɵdirectiveInject(Renderer2), i0.ɵdirectiveInject(TemplateRef), i0.ɵdirectiveInject(ViewContainerRef)); }`);
});
it('should generate queries for components', () => {

View File

@ -95,11 +95,6 @@ export enum R3ResolvedDependencyType {
* The token expression is a string representing the attribute name.
*/
Attribute = 1,
/**
* The dependency is for the `Injector` type itself.
*/
Injector = 2,
}
/**
@ -230,22 +225,14 @@ function compileInjectDependency(
dep: R3DependencyMetadata, injectFn: o.ExternalReference): o.Expression {
// Interpret the dependency according to its resolved type.
switch (dep.resolved) {
case R3ResolvedDependencyType.Token:
case R3ResolvedDependencyType.Injector: {
case R3ResolvedDependencyType.Token: {
// Build up the injection flags according to the metadata.
const flags = InjectFlags.Default | (dep.self ? InjectFlags.Self : 0) |
(dep.skipSelf ? InjectFlags.SkipSelf : 0) | (dep.host ? InjectFlags.Host : 0) |
(dep.optional ? InjectFlags.Optional : 0);
// Determine the token used for injection. In almost all cases this is the given token, but
// if the dependency is resolved to the `Injector` then the special `INJECTOR` token is used
// instead.
let token: o.Expression = dep.token;
if (dep.resolved === R3ResolvedDependencyType.Injector) {
token = o.importExpr(Identifiers.INJECTOR);
}
// Build up the arguments to the injectFn call.
const injectArgs = [token];
const injectArgs = [dep.token];
// If this dependency is optional or otherwise has non-default flags, then additional
// parameters describing how to inject the dependency must be passed to the inject function
// that's being used.
@ -280,12 +267,9 @@ export function dependenciesFromGlobalMetadata(
for (let dependency of type.diDeps) {
if (dependency.token) {
const tokenRef = tokenReference(dependency.token);
let resolved: R3ResolvedDependencyType = R3ResolvedDependencyType.Token;
if (tokenRef === injectorRef) {
resolved = R3ResolvedDependencyType.Injector;
} else if (dependency.isAttribute) {
resolved = R3ResolvedDependencyType.Attribute;
}
let resolved: R3ResolvedDependencyType = dependency.isAttribute ?
R3ResolvedDependencyType.Attribute :
R3ResolvedDependencyType.Token;
// In the case of most dependencies, the token will be a reference to a type. Sometimes,
// however, it can be a string, in the case of older Angular code or @Attribute injection.

View File

@ -175,7 +175,7 @@ export class Identifiers {
static InheritDefinitionFeature:
o.ExternalReference = {name: 'ɵInheritDefinitionFeature', moduleName: CORE};
static PublicFeature: o.ExternalReference = {name: 'ɵPublicFeature', moduleName: CORE};
static ProvidersFeature: o.ExternalReference = {name: 'ɵProvidersFeature', moduleName: CORE};
static listener: o.ExternalReference = {name: 'ɵlistener', moduleName: CORE};

View File

@ -103,6 +103,11 @@ export interface R3DirectiveMetadata {
* if any.
*/
exportAs: string|null;
/**
* The list of providers defined in the directive.
*/
providers: o.Expression|null;
}
/**
@ -180,6 +185,11 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
* A collection of animation triggers that will be used in the component template.
*/
animations: o.Expression|null;
/**
* The list of view providers defined in the component.
*/
viewProviders: o.Expression|null;
}
/**

View File

@ -82,11 +82,30 @@ function baseDirectiveFields(
// e.g 'outputs: {a: 'a'}`
definitionMap.set('outputs', conditionallyCreateMapObjectLiteral(meta.outputs));
if (meta.exportAs !== null) {
definitionMap.set('exportAs', o.literal(meta.exportAs));
}
return {definitionMap, statements: result.statements};
}
/**
* Add features to the definition map.
*/
function addFeatures(
definitionMap: DefinitionMap, meta: R3DirectiveMetadata | R3ComponentMetadata) {
// e.g. `features: [NgOnChangesFeature]`
const features: o.Expression[] = [];
// TODO: add `PublicFeature` so that directives get registered to the DI - make this configurable
features.push(o.importExpr(R3.PublicFeature));
const providers = meta.providers;
const viewProviders = (meta as R3ComponentMetadata).viewProviders;
if (providers || viewProviders) {
const args = [providers || new o.LiteralArrayExpr([])];
if (viewProviders) {
args.push(viewProviders);
}
features.push(o.importExpr(R3.ProvidersFeature).callFn(args));
}
if (meta.usesInheritance) {
features.push(o.importExpr(R3.InheritDefinitionFeature));
@ -97,11 +116,6 @@ function baseDirectiveFields(
if (features.length) {
definitionMap.set('features', o.literalArr(features));
}
if (meta.exportAs !== null) {
definitionMap.set('exportAs', o.literal(meta.exportAs));
}
return {definitionMap, statements: result.statements};
}
/**
@ -111,6 +125,7 @@ export function compileDirectiveFromMetadata(
meta: R3DirectiveMetadata, constantPool: ConstantPool,
bindingParser: BindingParser): R3DirectiveDef {
const {definitionMap, statements} = baseDirectiveFields(meta, constantPool, bindingParser);
addFeatures(definitionMap, meta);
const expression = o.importExpr(R3.defineDirective).callFn([definitionMap.toLiteralMap()]);
// On the type side, remove newlines from the selector as it will need to fit into a TypeScript
@ -164,6 +179,7 @@ export function compileComponentFromMetadata(
meta: R3ComponentMetadata, constantPool: ConstantPool,
bindingParser: BindingParser): R3ComponentDef {
const {definitionMap, statements} = baseDirectiveFields(meta, constantPool, bindingParser);
addFeatures(definitionMap, meta);
const selector = meta.selector && CssSelector.parse(meta.selector);
const firstSelector = selector && selector[0];
@ -320,6 +336,8 @@ export function compileComponentFromRender2(
encapsulation:
(summary.template && summary.template.encapsulation) || core.ViewEncapsulation.Emulated,
animations: null,
viewProviders:
component.viewProviders.length > 0 ? new o.WrappedNodeExpr(component.viewProviders) : null
};
const res = compileComponentFromMetadata(meta, outputCtx.constantPool, bindingParser);
@ -362,6 +380,7 @@ function directiveMetadataFromGlobalMetadata(
outputs: directive.outputs,
usesInheritance: false,
exportAs: null,
providers: directive.providers.length > 0 ? new o.WrappedNodeExpr(directive.providers) : null
};
}
@ -451,11 +470,15 @@ function createContentQueriesFunction(
if (meta.queries.length) {
const statements: o.Statement[] = meta.queries.map((query: R3QueryMetadata) => {
const queryDefinition = createQueryDefinition(query, constantPool, null);
return o.importExpr(R3.registerContentQuery).callFn([queryDefinition]).toStmt();
return o.importExpr(R3.registerContentQuery)
.callFn([queryDefinition, o.variable('dirIndex')])
.toStmt();
});
const typeName = meta.name;
const parameters = [new o.FnParam('dirIndex', o.NUMBER_TYPE)];
return o.fn(
[], statements, o.INFERRED_TYPE, null, typeName ? `${typeName}_ContentQueries` : null);
parameters, statements, o.INFERRED_TYPE, null,
typeName ? `${typeName}_ContentQueries` : null);
}
return null;

View File

@ -643,18 +643,23 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
const stylingInput = mapBasedStyleInput || mapBasedClassInput;
if (stylingInput) {
// these values must be outside of the update block so that they can
// be evaluted (the AST visit call) during creation time so that any
// pipes can be picked up in time before the template is built
const mapBasedClassValue =
mapBasedClassInput ? mapBasedClassInput.value.visit(this._valueConverter) : null;
const mapBasedStyleValue =
mapBasedStyleInput ? mapBasedStyleInput.value.visit(this._valueConverter) : null;
this.updateInstruction(stylingInput.sourceSpan, R3.elementStylingMap, () => {
const params: o.Expression[] = [indexLiteral];
if (mapBasedClassInput) {
const mapBasedClassValue = mapBasedClassInput.value.visit(this._valueConverter);
if (mapBasedClassValue) {
params.push(this.convertPropertyBinding(implicit, mapBasedClassValue, true));
} else if (mapBasedStyleInput) {
params.push(o.NULL_EXPR);
}
if (mapBasedStyleInput) {
const mapBasedStyleValue = mapBasedStyleInput.value.visit(this._valueConverter);
if (mapBasedStyleValue) {
params.push(this.convertPropertyBinding(implicit, mapBasedStyleValue, true));
}

View File

@ -14,7 +14,7 @@ export {isListLikeIterable as ɵisListLikeIterable} from './change_detection/cha
export {ChangeDetectorStatus as ɵChangeDetectorStatus, isDefaultChangeDetectionStrategy as ɵisDefaultChangeDetectionStrategy} from './change_detection/constants';
export {Console as ɵConsole} from './console';
export {InjectableDef as ɵInjectableDef, InjectorDef as ɵInjectorDef, getInjectableDef as ɵgetInjectableDef} from './di/defs';
export {inject as ɵinject, setCurrentInjector as ɵsetCurrentInjector} from './di/injector';
export {inject as ɵinject, setCurrentInjector as ɵsetCurrentInjector} from './di/injector_compatibility';
export {APP_ROOT as ɵAPP_ROOT} from './di/scope';
export {ivyEnabled as ɵivyEnabled} from './ivy_switch';
export {ComponentFactory as ɵComponentFactory} from './linker/component_factory';

View File

@ -25,7 +25,7 @@ export {
getFactoryOf as ɵgetFactoryOf,
getInheritedFactory as ɵgetInheritedFactory,
templateRefExtractor as ɵtemplateRefExtractor,
PublicFeature as ɵPublicFeature,
ProvidersFeature as ɵProvidersResolver,
InheritDefinitionFeature as ɵInheritDefinitionFeature,
NgOnChangesFeature as ɵNgOnChangesFeature,
NgModuleType as ɵNgModuleType,
@ -223,5 +223,11 @@ export {
SWITCH_RENDERER2_FACTORY__POST_R3__ as ɵSWITCH_RENDERER2_FACTORY__POST_R3__,
} from './render/api';
export {
publishGlobalUtil as ɵpublishGlobalUtil
} from './render3/publish_global_util';
export {
SWITCH_INJECTOR_FACTORY__POST_R3__ as ɵSWITCH_INJECTOR_FACTORY__POST_R3__,
} from './di/injector';
// clang-format on

View File

@ -16,7 +16,8 @@ export * from './di/metadata';
export {InjectableType, InjectorType, defineInjectable, defineInjector} from './di/defs';
export {forwardRef, resolveForwardRef, ForwardRefFn} from './di/forward_ref';
export {Injectable, InjectableDecorator, InjectableProvider} from './di/injectable';
export {inject, InjectFlags, INJECTOR, Injector} from './di/injector';
export {INJECTOR, Injector} from './di/injector';
export {inject, InjectFlags} from './di/injector_compatibility';
export {ReflectiveInjector} from './di/reflective_injector';
export {StaticProvider, ValueProvider, ConstructorSansProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ClassProvider} from './di/provider';
export {createInjector} from './di/r3_injector';

View File

@ -8,6 +8,7 @@
import {Type} from '../type';
import {stringify} from '../util';
import {getClosureSafeProperty} from '../util/property';
@ -22,6 +23,8 @@ import {stringify} from '../util';
*/
export interface ForwardRefFn { (): any; }
const __forward_ref__ = getClosureSafeProperty({__forward_ref__: getClosureSafeProperty});
/**
* Allows to refer to references which are not yet defined.
*
@ -53,10 +56,11 @@ export function forwardRef(forwardRefFn: ForwardRefFn): Type<any> {
* @see `forwardRef`
* @publicApi
*/
export function resolveForwardRef(type: any): any {
if (typeof type === 'function' && type.hasOwnProperty('__forward_ref__') &&
type.__forward_ref__ === forwardRef) {
return (<ForwardRefFn>type)();
export function resolveForwardRef<T>(type: T): T {
const fn: any = type;
if (typeof fn === 'function' && fn.hasOwnProperty(__forward_ref__) &&
fn.__forward_ref__ === forwardRef) {
return fn();
} else {
return type;
}

View File

@ -6,13 +6,16 @@
* found in the LICENSE file at https://angular.io/license
*/
import {injectInjector} from '../render3/di';
import {Type} from '../type';
import {stringify} from '../util';
import {noop} from '../util/noop';
import {getClosureSafeProperty} from '../util/property';
import {InjectableDef, defineInjectable, getInjectableDef} from './defs';
import {defineInjectable} from './defs';
import {resolveForwardRef} from './forward_ref';
import {InjectionToken} from './injection_token';
import {InjectFlags, inject} from './injector_compatibility';
import {Inject, Optional, Self, SkipSelf} from './metadata';
import {ConstructorProvider, ExistingProvider, FactoryProvider, StaticClassProvider, StaticProvider, ValueProvider} from './provider';
@ -104,8 +107,16 @@ export abstract class Injector {
providedIn: 'any' as any,
factory: () => inject(INJECTOR),
});
/** @internal */
static __NG_ELEMENT_ID__: () => Injector = () => SWITCH_INJECTOR_FACTORY();
}
export const SWITCH_INJECTOR_FACTORY__POST_R3__ = function() {
return injectInjector();
};
const SWITCH_INJECTOR_FACTORY__PRE_R3__ = noop;
const SWITCH_INJECTOR_FACTORY: typeof injectInjector = SWITCH_INJECTOR_FACTORY__PRE_R3__;
const IDENT = function<T>(value: T): T {
@ -336,7 +347,6 @@ function resolveToken(
return value;
}
function computeDeps(provider: StaticProvider): DependencyRecord[] {
let deps: DependencyRecord[] = EMPTY;
const providerDeps: any[] =
@ -396,107 +406,3 @@ function formatError(text: string, obj: any, source: string | null = null): stri
function staticError(text: string, obj: any): Error {
return new Error(formatError(text, obj));
}
/**
* Injection flags for DI.
*
* @publicApi
*/
export const enum InjectFlags {
Default = 0b0000,
/**
* Specifies that an injector should retrieve a dependency from any injector until reaching the
* host element of the current component. (Only used with Element Injector)
*/
Host = 0b0001,
/** Don't descend into ancestors of the node requesting injection. */
Self = 0b0010,
/** Skip the node that is requesting injection. */
SkipSelf = 0b0100,
/** Inject `defaultValue` instead if token not found. */
Optional = 0b1000,
}
/**
* Current injector value used by `inject`.
* - `undefined`: it is an error to call `inject`
* - `null`: `inject` can be called but there is no injector (limp-mode).
* - Injector instance: Use the injector for resolution.
*/
let _currentInjector: Injector|undefined|null = undefined;
export function setCurrentInjector(injector: Injector | null | undefined): Injector|undefined|null {
const former = _currentInjector;
_currentInjector = injector;
return former;
}
/**
* Injects a token from the currently active injector.
*
* This function must be used in the context of a factory function such as one defined for an
* `InjectionToken`, and will throw an error if not called from such a context.
*
* @usageNotes
* ### Example
*
* {@example core/di/ts/injector_spec.ts region='ShakeableInjectionToken'}
*
* Within such a factory function `inject` is utilized to request injection of a dependency, instead
* of providing an additional array of dependencies as was common to do with `useFactory` providers.
* `inject` is faster and more type-safe.
*
* @publicApi
*/
export function inject<T>(token: Type<T>| InjectionToken<T>): T;
export function inject<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T|null;
export function inject<T>(token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null {
if (_currentInjector === undefined) {
throw new Error(`inject() must be called from an injection context`);
} else if (_currentInjector === null) {
const injectableDef: InjectableDef<T>|null = getInjectableDef(token);
if (injectableDef && injectableDef.providedIn == 'root') {
return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
injectableDef.value;
}
if (flags & InjectFlags.Optional) return null;
throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`);
} else {
return _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
}
}
export function injectArgs(types: (Type<any>| InjectionToken<any>| any[])[]): any[] {
const args: any[] = [];
for (let i = 0; i < types.length; i++) {
const arg = types[i];
if (Array.isArray(arg)) {
if (arg.length === 0) {
throw new Error('Arguments array must have arguments.');
}
let type: Type<any>|undefined = undefined;
let flags: InjectFlags = InjectFlags.Default;
for (let j = 0; j < arg.length; j++) {
const meta = arg[j];
if (meta instanceof Optional || meta.ngMetadataName === 'Optional') {
flags |= InjectFlags.Optional;
} else if (meta instanceof SkipSelf || meta.ngMetadataName === 'SkipSelf') {
flags |= InjectFlags.SkipSelf;
} else if (meta instanceof Self || meta.ngMetadataName === 'Self') {
flags |= InjectFlags.Self;
} else if (meta instanceof Inject) {
type = meta.token;
} else {
type = meta;
}
}
args.push(inject(type !, flags));
} else {
args.push(inject(arg));
}
}
return args;
}

View File

@ -0,0 +1,165 @@
/**
* @license
* Copyright Google Inc. 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 {Type} from '../type';
import {stringify} from '../util';
import {InjectableDef, getInjectableDef} from './defs';
import {InjectionToken} from './injection_token';
import {Injector} from './injector';
import {Inject, Optional, Self, SkipSelf} from './metadata';
/**
* Injection flags for DI.
*
* @publicApi
*/
export const enum InjectFlags {
Default = 0b0000,
/**
* Specifies that an injector should retrieve a dependency from any injector until reaching the
* host element of the current component. (Only used with Element Injector)
*/
Host = 0b0001,
/** Don't descend into ancestors of the node requesting injection. */
Self = 0b0010,
/** Skip the node that is requesting injection. */
SkipSelf = 0b0100,
/** Inject `defaultValue` instead if token not found. */
Optional = 0b1000,
}
/**
* Current injector value used by `inject`.
* - `undefined`: it is an error to call `inject`
* - `null`: `inject` can be called but there is no injector (limp-mode).
* - Injector instance: Use the injector for resolution.
*/
let _currentInjector: Injector|undefined|null = undefined;
export function setCurrentInjector(injector: Injector | null | undefined): Injector|undefined|null {
const former = _currentInjector;
_currentInjector = injector;
return former;
}
/**
* Current implementation of inject.
*
* By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed
* to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this
* way for two reasons:
* 1. `Injector` should not depend on ivy logic.
* 2. To maintain tree shake-ability we don't want to bring in unnecessary code.
*/
let _injectImplementation: (<T>(token: Type<T>| InjectionToken<T>, flags: InjectFlags) => T | null)|
undefined;
/**
* Sets the current inject implementation.
*/
export function setInjectImplementation(
impl: (<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags) => T | null) | undefined):
(<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags) => T | null)|undefined {
const previous = _injectImplementation;
_injectImplementation = impl;
return previous;
}
export function injectInjectorOnly<T>(token: Type<T>| InjectionToken<T>): T;
export function injectInjectorOnly<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T|
null;
export function injectInjectorOnly<T>(
token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null {
if (_currentInjector === undefined) {
throw new Error(`inject() must be called from an injection context`);
} else if (_currentInjector === null) {
return injectRootLimpMode(token, undefined, flags);
} else {
return _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
}
}
/**
* Injects a token from the currently active injector.
*
* This function must be used in the context of a factory function such as one defined for an
* `InjectionToken`, and will throw an error if not called from such a context.
*
* @usageNotes
* ### Example
*
* {@example core/di/ts/injector_spec.ts region='ShakeableInjectionToken'}
*
* Within such a factory function `inject` is utilized to request injection of a dependency, instead
* of providing an additional array of dependencies as was common to do with `useFactory` providers.
* `inject` is faster and more type-safe.
*
* @publicApi
*/
export function inject<T>(token: Type<T>| InjectionToken<T>): T;
export function inject<T>(token: Type<T>| InjectionToken<T>, flags?: InjectFlags): T|null;
export function inject<T>(token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null {
return (_injectImplementation || injectInjectorOnly)(token, flags);
}
/**
* Injects `root` tokens in limp mode.
*
* If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to
* `"root"`. This is known as the limp mode injection. In such case the value is stored in the
* `InjectableDef`.
*/
export function injectRootLimpMode<T>(
token: Type<T>| InjectionToken<T>, notFoundValue: T | undefined, flags: InjectFlags): T|null {
const injectableDef: InjectableDef<T>|null = getInjectableDef(token);
if (injectableDef && injectableDef.providedIn == 'root') {
return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
injectableDef.value;
}
if (flags & InjectFlags.Optional) return null;
if (notFoundValue !== undefined) return notFoundValue;
throw new Error(`Injector: NOT_FOUND [${stringify(token)}]`);
}
export function injectArgs(types: (Type<any>| InjectionToken<any>| any[])[]): any[] {
const args: any[] = [];
for (let i = 0; i < types.length; i++) {
const arg = types[i];
if (Array.isArray(arg)) {
if (arg.length === 0) {
throw new Error('Arguments array must have arguments.');
}
let type: Type<any>|undefined = undefined;
let flags: InjectFlags = InjectFlags.Default;
for (let j = 0; j < arg.length; j++) {
const meta = arg[j];
if (meta instanceof Optional || meta.ngMetadataName === 'Optional') {
flags |= InjectFlags.Optional;
} else if (meta instanceof SkipSelf || meta.ngMetadataName === 'SkipSelf') {
flags |= InjectFlags.SkipSelf;
} else if (meta instanceof Self || meta.ngMetadataName === 'Self') {
flags |= InjectFlags.Self;
} else if (meta instanceof Inject) {
type = meta.token;
} else {
type = meta;
}
}
args.push(inject(type !, flags));
} else {
args.push(inject(arg));
}
}
return args;
}

View File

@ -13,7 +13,8 @@ import {stringify} from '../util';
import {InjectableDef, InjectableType, InjectorType, InjectorTypeWithProviders, getInjectableDef, getInjectorDef} from './defs';
import {resolveForwardRef} from './forward_ref';
import {InjectionToken} from './injection_token';
import {INJECTOR, InjectFlags, Injector, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE, inject, injectArgs, setCurrentInjector} from './injector';
import {INJECTOR, Injector, NullInjector, THROW_IF_NOT_FOUND, USE_VALUE} from './injector';
import {InjectFlags, inject, injectArgs, setCurrentInjector} from './injector_compatibility';
import {ClassProvider, ConstructorProvider, ExistingProvider, FactoryProvider, Provider, StaticClassProvider, StaticProvider, TypeProvider, ValueProvider} from './provider';
import {APP_ROOT} from './scope';
@ -165,7 +166,7 @@ export class R3Injector {
if (def && this.injectableDefInScope(def)) {
// Found an ngInjectableDef and it's scoped to this injector. Pretend as if it was here
// all along.
record = injectableDefRecord(token);
record = makeRecord(injectableDefFactory(token), NOT_YET);
this.records.set(token, record);
}
}
@ -328,7 +329,7 @@ export class R3Injector {
}
}
function injectableDefRecord(token: Type<any>| InjectionToken<any>): Record<any> {
function injectableDefFactory(token: Type<any>| InjectionToken<any>): () => any {
const injectableDef = getInjectableDef(token as InjectableType<any>);
if (injectableDef === null) {
if (token instanceof InjectionToken) {
@ -336,21 +337,34 @@ function injectableDefRecord(token: Type<any>| InjectionToken<any>): Record<any>
}
// TODO(alxhub): there should probably be a strict mode which throws here instead of assuming a
// no-args constructor.
return makeRecord(() => new (token as Type<any>)());
return () => new (token as Type<any>)();
}
return makeRecord(injectableDef.factory);
return injectableDef.factory;
}
function providerToRecord(provider: SingleProvider): Record<any> {
let factory: (() => any)|undefined = providerToFactory(provider);
if (isValueProvider(provider)) {
return makeRecord(undefined, provider.useValue);
} else {
return makeRecord(factory, NOT_YET);
}
}
/**
* Converts a `SingleProvider` into a factory function.
*
* @param provider provider to convert to factory
*/
export function providerToFactory(provider: SingleProvider): () => any {
let token = resolveForwardRef(provider);
let value: any = NOT_YET;
let factory: (() => any)|undefined = undefined;
if (isTypeProvider(provider)) {
return injectableDefRecord(provider);
return injectableDefFactory(provider);
} else {
token = resolveForwardRef(provider.provide);
if (isValueProvider(provider)) {
value = provider.useValue;
factory = () => provider.useValue;
} else if (isExistingProvider(provider)) {
factory = () => inject(provider.useExisting);
} else if (isFactoryProvider(provider)) {
@ -360,11 +374,11 @@ function providerToRecord(provider: SingleProvider): Record<any> {
if (hasDeps(provider)) {
factory = () => new (classRef)(...injectArgs(provider.deps));
} else {
return injectableDefRecord(classRef);
return injectableDefFactory(classRef);
}
}
}
return makeRecord(factory, value);
return factory;
}
function makeRecord<T>(
@ -392,7 +406,7 @@ function isFactoryProvider(value: SingleProvider): value is FactoryProvider {
return !!(value as FactoryProvider).useFactory;
}
function isTypeProvider(value: SingleProvider): value is TypeProvider {
export function isTypeProvider(value: SingleProvider): value is TypeProvider {
return typeof value === 'function';
}

View File

@ -10,7 +10,7 @@ import {ReflectionCapabilities} from '../reflection/reflection_capabilities';
import {Type} from '../type';
import {getClosureSafeProperty} from '../util/property';
import {inject, injectArgs} from './injector';
import {inject, injectArgs} from './injector_compatibility';
import {ClassSansProvider, ConstructorSansProvider, ExistingSansProvider, FactorySansProvider, StaticClassSansProvider, ValueProvider, ValueSansProvider} from './provider';
const USE_VALUE =

View File

@ -20,7 +20,7 @@
*/
export {InjectableDef as ɵInjectableDef, InjectorDef as ɵInjectorDef, defineInjectable, defineInjector} from './di/defs';
export {inject} from './di/injector';
export {inject} from './di/injector_compatibility';
export {NgModuleDef as ɵNgModuleDef, NgModuleDefWithMeta as ɵNgModuleDefWithMeta} from './metadata/ng_module';
export {defineNgModule as ɵdefineNgModule} from './render3/definition';
export {NgModuleFactory as ɵNgModuleFactory} from './render3/ng_module_ref';

View File

@ -15,17 +15,19 @@ import {Sanitizer} from '../sanitization/security';
import {assertComponentType, assertDefined} from './assert';
import {getComponentViewByInstance} from './context_discovery';
import {getComponentDef} from './definition';
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
import {queueInitHooks, queueLifecycleHooks} from './hooks';
import {CLEAN_PROMISE, baseDirectiveCreate, createLViewData, createNodeAtIndex, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, getOrCreateTView, leaveView, locateHostElement, prefillHostVars, resetComponentState, setHostBindings} from './instructions';
import {CLEAN_PROMISE, createLViewData, createNodeAtIndex, createTView, detectChangesInternal, executeInitAndContentHooks, getOrCreateTView, initNodeFlags, instantiateRootComponent, locateHostElement, prefillHostVars, setHostBindings} from './instructions';
import {ComponentDef, ComponentType} from './interfaces/definition';
import {TElementNode, TNodeFlags, TNodeType} from './interfaces/node';
import {PlayerHandler} from './interfaces/player';
import {RElement, RNode, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {CONTEXT, HEADER_OFFSET, HOST, HOST_NODE, INJECTOR, LViewData, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
import {publishDefaultGlobalUtils} from './publish_global_util';
import {enterView, leaveView, resetComponentState} from './state';
import {getRootView, readElementValue, readPatchedLViewData, stringify} from './util';
// Root component will always have an element index of 0 and an injector size of 1
const ROOT_EXPANDO_INSTRUCTIONS = [0, 1];
@ -106,6 +108,7 @@ export function renderComponent<T>(
Type<T>/* Type as workaround for: Microsoft/TypeScript/issues/4881 */
,
opts: CreateComponentOptions = {}): T {
ngDevMode && publishDefaultGlobalUtils();
ngDevMode && assertComponentType(componentType);
const rendererFactory = opts.rendererFactory || domRendererFactory3;
const sanitizer = opts.sanitizer || null;
@ -129,13 +132,12 @@ export function renderComponent<T>(
let component: T;
try {
if (rendererFactory.begin) rendererFactory.begin();
const componentView =
createRootComponentView(hostRNode, componentDef, rootView, renderer, sanitizer);
component = createRootComponent(
hostRNode, componentView, componentDef, rootView, rootContext, opts.hostFeatures || null);
executeInitAndContentHooks();
executeInitAndContentHooks(rootView);
detectChangesInternal(componentView, component);
} finally {
leaveView(oldView);
@ -170,9 +172,9 @@ export function createRootComponentView(
if (tView.firstTemplatePass) {
tView.expandoInstructions = ROOT_EXPANDO_INSTRUCTIONS.slice();
if (def.diPublic) def.diPublic(def);
tNode.flags =
rootView.length << TNodeFlags.DirectiveStartingIndexShift | TNodeFlags.isComponent;
diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), rootView, def.type);
tNode.flags = TNodeFlags.isComponent;
initNodeFlags(tNode, rootView.length, 1);
}
// Store component view at node index, with node as the HOST
@ -188,16 +190,17 @@ export function createRootComponentView(
export function createRootComponent<T>(
hostRNode: RNode | null, componentView: LViewData, componentDef: ComponentDef<T>,
rootView: LViewData, rootContext: RootContext, hostFeatures: HostFeature[] | null): any {
const tView = rootView[TVIEW];
// Create directive instance with factory() and store at next index in viewData
const component =
baseDirectiveCreate(rootView.length, componentDef.factory() as T, componentDef, hostRNode);
const component = instantiateRootComponent(tView, rootView, componentDef);
rootContext.components.push(component);
componentView[CONTEXT] = component;
hostFeatures && hostFeatures.forEach((feature) => feature(component, componentDef));
if (rootView[TVIEW].firstTemplatePass) prefillHostVars(componentDef.hostVars);
setHostBindings();
if (tView.firstTemplatePass) prefillHostVars(tView, rootView, componentDef.hostVars);
setHostBindings(tView, rootView);
return component;
}

View File

@ -8,7 +8,8 @@
import {ChangeDetectorRef as ViewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {InjectionToken} from '../di/injection_token';
import {Injector, inject} from '../di/injector';
import {Injector} from '../di/injector';
import {inject} from '../di/injector_compatibility';
import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
import {ElementRef as viewEngine_ElementRef} from '../linker/element_ref';
@ -19,11 +20,12 @@ import {Type} from '../type';
import {assertComponentType, assertDefined} from './assert';
import {LifecycleHooksFeature, createRootComponent, createRootComponentView, createRootContext} from './component';
import {getComponentDef} from './definition';
import {adjustBlueprintForNewNode, createLViewData, createNodeAtIndex, createTView, createViewNode, elementCreate, enterView, locateHostElement, renderEmbeddedTemplate} from './instructions';
import {createLViewData, createNodeAtIndex, createTView, createViewNode, elementCreate, locateHostElement, renderEmbeddedTemplate} from './instructions';
import {ComponentDef, RenderFlags} from './interfaces/definition';
import {TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
import {FLAGS, HEADER_OFFSET, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
import {enterView} from './state';
import {getTNode} from './util';
import {createElementRef} from './view_engine_compatibility';
import {RootViewRef, ViewRef} from './view_ref';
@ -114,9 +116,6 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
elementCreate(this.selector, rendererFactory.createRenderer(null, this.componentDef)) :
locateHostElement(rendererFactory, rootSelectorOrNode);
// The first index of the first selector is the tag name.
const componentTag = this.componentDef.selectors ![0] ![0] as string;
const rootFlags = this.componentDef.onPush ? LViewFlags.Dirty | LViewFlags.IsRoot :
LViewFlags.CheckAlways | LViewFlags.IsRoot;
const rootContext: RootContext = ngModule && !isInternalRootView ?
@ -145,15 +144,25 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
// projection instruction. This is needed to support the reprojection of these nodes.
if (projectableNodes) {
let index = 0;
const tView = rootView[TVIEW];
const projection: TNode[] = tElementNode.projection = [];
for (let i = 0; i < projectableNodes.length; i++) {
const nodeList = projectableNodes[i];
let firstTNode: TNode|null = null;
let previousTNode: TNode|null = null;
for (let j = 0; j < nodeList.length; j++) {
adjustBlueprintForNewNode(rootView);
if (tView.firstTemplatePass) {
// For dynamically created components such as ComponentRef, we create a new TView for
// each insert. This is not ideal since we should be sharing the TViews.
// Also the logic here should be shared with `component.ts`'s `renderComponent`
// method.
tView.expandoStartIndex++;
tView.blueprint.splice(++index + HEADER_OFFSET, 0, null);
tView.data.splice(index + HEADER_OFFSET, 0, null);
rootView.splice(index + HEADER_OFFSET, 0, null);
}
const tNode =
createNodeAtIndex(++index, TNodeType.Element, nodeList[j] as RElement, null, null);
createNodeAtIndex(index, TNodeType.Element, nodeList[j] as RElement, null, null);
previousTNode ? (previousTNode.next = tNode) : (firstTNode = tNode);
previousTNode = tNode;
}

View File

@ -54,7 +54,7 @@ export function defineComponent<T>(componentDefinition: {
/**
* Factory method used to create an instance of directive.
*/
factory: () => T;
factory: (t: Type<T>| null) => T;
/**
* The number of nodes, local refs, and pipes in this component template.
@ -154,7 +154,7 @@ export function defineComponent<T>(componentDefinition: {
/**
* Function to create instances of content queries associated with a given directive.
*/
contentQueries?: (() => void);
contentQueries?: ((dirIndex: number) => void);
/** Refreshes content queries associated with directives in a given view */
contentQueriesRefresh?: ((directiveIndex: number, queryIndex: number) => void);
@ -210,7 +210,7 @@ export function defineComponent<T>(componentDefinition: {
/**
* A list of optional features to apply.
*
* See: {@link NgOnChangesFeature}, {@link PublicFeature}
* See: {@link NgOnChangesFeature}, {@link ProvidersFeature}
*/
features?: ComponentDefFeature[];
@ -238,17 +238,6 @@ export function defineComponent<T>(componentDefinition: {
*/
changeDetection?: ChangeDetectionStrategy;
/**
* Defines the set of injectable objects that are visible to a Directive and its light DOM
* children.
*/
providers?: Provider[];
/**
* Defines the set of injectable objects that are visible to its view DOM children.
*/
viewProviders?: Provider[];
/**
* Registry of directives and components that may be found in this component's view.
*
@ -270,7 +259,7 @@ export function defineComponent<T>(componentDefinition: {
const declaredInputs: {[key: string]: string} = {} as any;
const def: Mutable<ComponentDef<any>, keyof ComponentDef<any>> = {
type: type,
diPublic: null,
providersResolver: null,
consts: componentDefinition.consts,
vars: componentDefinition.vars,
hostVars: componentDefinition.hostVars || 0,
@ -301,8 +290,6 @@ export function defineComponent<T>(componentDefinition: {
// TODO(misko): convert ViewEncapsulation into const enum so that it can be used directly in the
// next line. Also `None` should be 0 not 2.
encapsulation: componentDefinition.encapsulation || ViewEncapsulation.Emulated,
providers: EMPTY_ARRAY,
viewProviders: EMPTY_ARRAY,
id: 'c',
styles: componentDefinition.styles || EMPTY_ARRAY,
_: null as never,
@ -525,7 +512,7 @@ export const defineDirective = defineComponent as any as<T>(directiveDefinition:
/**
* Factory method used to create an instance of directive.
*/
factory: () => T;
factory: (t: Type<T>| null) => T;
/**
* Static attributes to set on host element.
@ -595,7 +582,7 @@ export const defineDirective = defineComponent as any as<T>(directiveDefinition:
/**
* A list of optional features to apply.
*
* See: {@link NgOnChangesFeature}, {@link PublicFeature}, {@link InheritDefinitionFeature}
* See: {@link NgOnChangesFeature}, {@link ProvidersFeature}, {@link InheritDefinitionFeature}
*/
features?: DirectiveDefFeature[];
@ -615,7 +602,7 @@ export const defineDirective = defineComponent as any as<T>(directiveDefinition:
/**
* Function to create instances of content queries associated with a given directive.
*/
contentQueries?: (() => void);
contentQueries?: ((directiveIndex: number) => void);
/** Refreshes content queries associated with directives in a given view */
contentQueriesRefresh?: ((directiveIndex: number, queryIndex: number) => void);
@ -650,7 +637,7 @@ export function definePipe<T>(pipeDef: {
type: Type<T>,
/** A factory for creating a pipe instance. */
factory: () => T,
factory: (t: Type<T>| null) => T,
/** Whether the pipe is pure. */
pure?: boolean

View File

@ -6,23 +6,68 @@
* found in the LICENSE file at https://angular.io/license
*/
// We are temporarily importing the existing viewEngine_from core so we can be sure we are
// correctly implementing its interfaces for backwards compatibility.
import {getInjectableDef, getInjectorDef} from '../di/defs';
import {InjectionToken} from '../di/injection_token';
import {InjectFlags, Injector, inject, setCurrentInjector} from '../di/injector';
import {Injector} from '../di/injector';
import {InjectFlags, injectRootLimpMode, setInjectImplementation} from '../di/injector_compatibility';
import {Type} from '../type';
import {assertDefined} from './assert';
import {assertDefined, assertEqual} from './assert';
import {getComponentDef, getDirectiveDef, getPipeDef} from './definition';
import {NG_ELEMENT_ID} from './fields';
import {_getViewData, getPreviousOrParentTNode, resolveDirective, setEnvironment} from './instructions';
import {DirectiveDef} from './interfaces/definition';
import {InjectorLocationFlags, PARENT_INJECTOR, TNODE,} from './interfaces/injector';
import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
import {NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags, TNODE, isFactory} from './interfaces/injector';
import {AttributeMarker, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType} from './interfaces/node';
import {DECLARATION_VIEW, HOST_NODE, INJECTOR, LViewData, TData, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes} from './node_assert';
import {getPreviousOrParentTNode, getViewData, setTNodeAndViewData} from './state';
import {getParentInjectorIndex, getParentInjectorView, getParentInjectorViewOffset, hasParentInjector, isComponent, stringify} from './util';
/**
* Defines if the call to `inject` should include `viewProviders` in its resolution.
*
* This is set to true when we try to instantiate a component. This value is reset in
* `getNodeInjectable` to a value which matches the declaration location of the token about to be
* instantiated. This is done so that if we are injecting a token which was declared outside of
* `viewProviders` we don't accidentally pull `viewProviders` in.
*
* Example:
*
* ```
* @Injectable()
* class MyService {
* constructor(public value: String) {}
* }
*
* @Component({
* providers: [
* MyService,
* {provide: String, value: 'providers' }
* ]
* viewProviders: [
* {provide: String, value: 'viewProviders'}
* ]
* })
* class MyComponent {
* constructor(myService: MyService, value: String) {
* // We expect that Component can see into `viewProviders`.
* expect(value).toEqual('viewProviders');
* // `MyService` was not declared in `viewProviders` hence it can't see it.
* expect(myService.value).toEqual('providers');
* }
* }
*
* ```
*/
let includeViewProviders = false;
function setIncludeViewProviders(v: boolean): boolean {
const oldValue = includeViewProviders;
includeViewProviders = v;
return oldValue;
}
/**
* The number of slots in each bloom filter (used by DI). The larger this number, the fewer
@ -43,46 +88,40 @@ let nextNgElementId = 0;
* @param tView The TView for the injector's bloom filters
* @param type The directive token to register
*/
export function bloomAdd(injectorIndex: number, tView: TView, type: Type<any>): void {
if (tView.firstTemplatePass) {
let id: number|undefined = (type as any)[NG_ELEMENT_ID];
export function bloomAdd(
injectorIndex: number, tView: TView, type: Type<any>| InjectionToken<any>): void {
ngDevMode && assertEqual(tView.firstTemplatePass, true, 'expected firstTemplatePass to be true');
let id: number|undefined = (type as any)[NG_ELEMENT_ID];
// Set a unique ID on the directive type, so if something tries to inject the directive,
// we can easily retrieve the ID and hash it into the bloom bit that should be checked.
if (id == null) {
id = (type as any)[NG_ELEMENT_ID] = nextNgElementId++;
}
// We only have BLOOM_SIZE (256) slots in our bloom filter (8 buckets * 32 bits each),
// so all unique IDs must be modulo-ed into a number from 0 - 255 to fit into the filter.
const bloomBit = id & BLOOM_MASK;
// Create a mask that targets the specific bit associated with the directive.
// JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
// to bit positions 0 - 31 in a 32 bit integer.
const mask = 1 << bloomBit;
// Use the raw bloomBit number to determine which bloom filter bucket we should check
// e.g: bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127], etc
const b7 = bloomBit & 0x80;
const b6 = bloomBit & 0x40;
const b5 = bloomBit & 0x20;
const tData = tView.data as number[];
if (b7) {
b6 ? (b5 ? (tData[injectorIndex + 7] |= mask) : (tData[injectorIndex + 6] |= mask)) :
(b5 ? (tData[injectorIndex + 5] |= mask) : (tData[injectorIndex + 4] |= mask));
} else {
b6 ? (b5 ? (tData[injectorIndex + 3] |= mask) : (tData[injectorIndex + 2] |= mask)) :
(b5 ? (tData[injectorIndex + 1] |= mask) : (tData[injectorIndex] |= mask));
}
// Set a unique ID on the directive type, so if something tries to inject the directive,
// we can easily retrieve the ID and hash it into the bloom bit that should be checked.
if (id == null) {
id = (type as any)[NG_ELEMENT_ID] = nextNgElementId++;
}
}
export function getOrCreateNodeInjector(): number {
return getOrCreateNodeInjectorForNode(
getPreviousOrParentTNode() as TElementNode | TElementContainerNode | TContainerNode,
_getViewData());
// We only have BLOOM_SIZE (256) slots in our bloom filter (8 buckets * 32 bits each),
// so all unique IDs must be modulo-ed into a number from 0 - 255 to fit into the filter.
const bloomBit = id & BLOOM_MASK;
// Create a mask that targets the specific bit associated with the directive.
// JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
// to bit positions 0 - 31 in a 32 bit integer.
const mask = 1 << bloomBit;
// Use the raw bloomBit number to determine which bloom filter bucket we should check
// e.g: bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127], etc
const b7 = bloomBit & 0x80;
const b6 = bloomBit & 0x40;
const b5 = bloomBit & 0x20;
const tData = tView.data as number[];
if (b7) {
b6 ? (b5 ? (tData[injectorIndex + 7] |= mask) : (tData[injectorIndex + 6] |= mask)) :
(b5 ? (tData[injectorIndex + 5] |= mask) : (tData[injectorIndex + 4] |= mask));
} else {
b6 ? (b5 ? (tData[injectorIndex + 3] |= mask) : (tData[injectorIndex + 2] |= mask)) :
(b5 ? (tData[injectorIndex + 1] |= mask) : (tData[injectorIndex] |= mask));
}
}
/**
@ -102,26 +141,29 @@ export function getOrCreateNodeInjectorForNode(
const tView = hostView[TVIEW];
if (tView.firstTemplatePass) {
tNode.injectorIndex = hostView.length;
setUpBloom(tView.data, tNode); // foundation for node bloom
setUpBloom(hostView, null); // foundation for cumulative bloom
setUpBloom(tView.blueprint, null);
insertBloom(tView.data, tNode); // foundation for node bloom
insertBloom(hostView, null); // foundation for cumulative bloom
insertBloom(tView.blueprint, null);
ngDevMode && assertEqual(
tNode.flags === 0 || tNode.flags === TNodeFlags.isComponent, true,
'expected tNode.flags to not be initialized');
}
const parentLoc = getParentInjectorLocation(tNode, hostView);
const parentIndex = parentLoc & InjectorLocationFlags.InjectorIndexMask;
const parentIndex = getParentInjectorIndex(parentLoc);
const parentView: LViewData = getParentInjectorView(parentLoc, hostView);
const parentData = parentView[TVIEW].data as any;
const injectorIndex = tNode.injectorIndex;
// If a parent injector can't be found, its location is set to -1.
// In that case, we don't need to set up a cumulative bloom
if (parentLoc !== -1) {
for (let i = 0; i < PARENT_INJECTOR; i++) {
const bloomIndex = parentIndex + i;
// Creates a cumulative bloom filter that merges the parent's bloom filter
// and its own cumulative bloom (which contains tokens for all ancestors)
hostView[injectorIndex + i] = parentView[bloomIndex] | parentData[bloomIndex];
if (hasParentInjector(parentLoc)) {
const parentData = parentView[TVIEW].data as any;
// Creates a cumulative bloom filter that merges the parent's bloom filter
// and its own cumulative bloom (which contains tokens for all ancestors)
for (let i = 0; i < 8; i++) {
hostView[injectorIndex + i] = parentView[parentIndex + i] | parentData[parentIndex + i];
}
}
@ -129,10 +171,11 @@ export function getOrCreateNodeInjectorForNode(
return injectorIndex;
}
function setUpBloom(arr: any[], footer: TNode | null) {
function insertBloom(arr: any[], footer: TNode | null): void {
arr.push(0, 0, 0, 0, 0, 0, 0, 0, footer);
}
export function getInjectorIndex(tNode: TNode, hostView: LViewData): number {
if (tNode.injectorIndex === -1 ||
// If the injector index is the same as its parent's injector index, then the index has been
@ -150,10 +193,12 @@ export function getInjectorIndex(tNode: TNode, hostView: LViewData): number {
/**
* Finds the index of the parent injector, with a view offset if applicable. Used to set the
* parent injector initially.
*
* Returns a combination of number of `ViewData` we have to go up and index in that `Viewdata`
*/
export function getParentInjectorLocation(tNode: TNode, view: LViewData): number {
export function getParentInjectorLocation(tNode: TNode, view: LViewData): RelativeInjectorLocation {
if (tNode.parent && tNode.parent.injectorIndex !== -1) {
return tNode.parent.injectorIndex; // view offset is 0
return tNode.parent.injectorIndex as any; // view offset is 0
}
// For most cases, the parent injector index can be found on the host node (e.g. for component
@ -167,81 +212,20 @@ export function getParentInjectorLocation(tNode: TNode, view: LViewData): number
viewOffset++;
}
return hostTNode ?
hostTNode.injectorIndex | (viewOffset << InjectorLocationFlags.ViewOffsetShift) :
-1;
hostTNode.injectorIndex | (viewOffset << RelativeInjectorLocationFlags.ViewOffsetShift) :
-1 as any;
}
/**
* Unwraps a parent injector location number to find the view offset from the current injector,
* then walks up the declaration view tree until the view is found that contains the parent
* injector.
*
* @param location The location of the parent injector, which contains the view offset
* @param startView The LViewData instance from which to start walking up the view tree
* @returns The LViewData instance that contains the parent injector
*/
export function getParentInjectorView(location: number, startView: LViewData): LViewData {
let viewOffset = location >> InjectorLocationFlags.ViewOffsetShift;
let parentView = startView;
// For most cases, the parent injector can be found on the host node (e.g. for component
// or container), but we must keep the loop here to support the rarer case of deeply nested
// <ng-template> tags or inline views, where the parent injector might live many views
// above the child injector.
while (viewOffset > 0) {
parentView = parentView[DECLARATION_VIEW] !;
viewOffset--;
}
return parentView;
}
/**
* Makes a directive public to the DI system by adding it to an injector's bloom filter.
* Makes a type or an injection token public to the DI system by adding it to an
* injector's bloom filter.
*
* @param di The node injector in which a directive will be added
* @param def The definition of the directive to be made public
* @param token The type or the injection token to be made public
*/
export function diPublicInInjector(
injectorIndex: number, view: LViewData, def: DirectiveDef<any>): void {
bloomAdd(injectorIndex, view[TVIEW], def.type);
}
/**
* Makes a directive public to the DI system by adding it to an injector's bloom filter.
*
* @param def The definition of the directive to be made public
*/
export function diPublic(def: DirectiveDef<any>): void {
diPublicInInjector(getOrCreateNodeInjector(), _getViewData(), def);
}
/**
* Returns the value associated to the given token from the injectors.
*
* `directiveInject` is intended to be used for directive, component and pipe factories.
* All other injection use `inject` which does not walk the node injector tree.
*
* Usage example (in factory function):
*
* class SomeDirective {
* constructor(directive: DirectiveA) {}
*
* static ngDirectiveDef = defineDirective({
* type: SomeDirective,
* factory: () => new SomeDirective(directiveInject(DirectiveA))
* });
* }
*
* @param token the type or token to inject
* @param flags Injection flags
* @returns the value from the injector or `null` when not found
*/
export function directiveInject<T>(token: Type<T>| InjectionToken<T>): T;
export function directiveInject<T>(token: Type<T>| InjectionToken<T>, flags: InjectFlags): T;
export function directiveInject<T>(
token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null {
const hostTNode =
getPreviousOrParentTNode() as TElementNode | TContainerNode | TElementContainerNode;
return getOrCreateInjectable<T>(hostTNode, _getViewData(), token, flags);
injectorIndex: number, view: LViewData, token: InjectionToken<any>| Type<any>): void {
bloomAdd(injectorIndex, view[TVIEW], token);
}
/**
@ -275,8 +259,7 @@ export function directiveInject<T>(
*
* @publicApi
*/
export function injectAttribute(attrNameToInject: string): string|undefined {
const tNode = getPreviousOrParentTNode();
export function injectAttributeImpl(tNode: TNode, attrNameToInject: string): string|undefined {
ngDevMode && assertNodeOfPossibleTypes(
tNode, TNodeType.Container, TNodeType.Element, TNodeType.ElementContainer);
ngDevMode && assertDefined(tNode, 'expecting tNode');
@ -295,7 +278,7 @@ export function injectAttribute(attrNameToInject: string): string|undefined {
/**
* Returns the value associated to the given token from the injectors.
* Returns the value associated to the given token from the NodeInjectors => ModuleInjector.
*
* Look for the injector providing the token by walking up the node injector tree and then
* the module injector tree.
@ -306,133 +289,169 @@ export function injectAttribute(attrNameToInject: string): string|undefined {
* @returns the value from the injector or `null` when not found
*/
export function getOrCreateInjectable<T>(
hostTNode: TElementNode | TContainerNode | TElementContainerNode, hostView: LViewData,
token: Type<T>| InjectionToken<T>, flags: InjectFlags = InjectFlags.Default): T|null {
tNode: TElementNode | TContainerNode | TElementContainerNode, lViewData: LViewData,
token: Type<T>| InjectionToken<T>, flags: InjectFlags = InjectFlags.Default,
notFoundValue?: any): T|null {
const bloomHash = bloomHashBitOrFactory(token);
// If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
// so just call the factory function to create it.
if (typeof bloomHash === 'function') return bloomHash();
if (typeof bloomHash === 'function') {
const savePreviousOrParentTNode = getPreviousOrParentTNode();
const saveViewData = getViewData();
setTNodeAndViewData(tNode, lViewData);
try {
return bloomHash();
} finally {
setTNodeAndViewData(savePreviousOrParentTNode, saveViewData);
}
} else if (typeof bloomHash == 'number') {
// If the token has a bloom hash, then it is a token which could be in NodeInjector.
// If the token has a bloom hash, then it is a directive that is public to the injection system
// (diPublic) otherwise fall back to the module injector.
if (bloomHash != null) {
const startInjectorIndex = getInjectorIndex(hostTNode, hostView);
// A reference to the previous injector TView that was found while climbing the element injector
// tree. This is used to know if viewProviders can be accessed on the current injector.
let previousTView: TView|null = null;
let injectorIndex = getInjectorIndex(tNode, lViewData);
let parentLocation: RelativeInjectorLocation = NO_PARENT_INJECTOR;
let injectorIndex = startInjectorIndex;
let injectorView = hostView;
let parentLocation: number = -1;
// If we should skip this injector, start by searching the parent injector.
if (flags & InjectFlags.SkipSelf) {
parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lViewData) :
lViewData[injectorIndex + PARENT_INJECTOR];
// If we should skip this injector or if an injector doesn't exist on this node (e.g. all
// directives on this node are private), start by searching the parent injector.
if (flags & InjectFlags.SkipSelf || injectorIndex === -1) {
parentLocation = injectorIndex === -1 ? getParentInjectorLocation(hostTNode, hostView) :
injectorView[injectorIndex + PARENT_INJECTOR];
if (shouldNotSearchParent(flags, parentLocation)) {
if (!shouldSearchParent(flags, parentLocation)) {
injectorIndex = -1;
} else {
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
injectorView = getParentInjectorView(parentLocation, injectorView);
previousTView = lViewData[TVIEW];
injectorIndex = getParentInjectorIndex(parentLocation);
lViewData = getParentInjectorView(parentLocation, lViewData);
}
}
// Traverse up the injector tree until we find a potential match or until we know there
// *isn't* a match.
while (injectorIndex !== -1) {
// Traverse up the injector tree until we find a potential match or until we know there
// *isn't* a match. Outer loop is necessary in case we get a false positive injector.
while (injectorIndex !== -1) {
// Check the current injector. If it matches, stop searching for an injector.
if (injectorHasToken(bloomHash, injectorIndex, injectorView[TVIEW].data)) {
break;
}
parentLocation = lViewData[injectorIndex + PARENT_INJECTOR];
parentLocation = injectorView[injectorIndex + PARENT_INJECTOR];
if (shouldNotSearchParent(flags, parentLocation)) {
injectorIndex = -1;
break;
}
// If the ancestor bloom filter value has the bit corresponding to the directive, traverse
// up to find the specific injector. If the ancestor bloom filter does not have the bit, we
// can abort.
if (injectorHasToken(bloomHash, injectorIndex, injectorView)) {
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
injectorView = getParentInjectorView(parentLocation, injectorView);
} else {
injectorIndex = -1;
break;
// Check the current injector. If it matches, see if it contains token.
const tView = lViewData[TVIEW];
if (bloomHasToken(bloomHash, injectorIndex, tView.data)) {
// At this point, we have an injector which *may* contain the token, so we step through
// the providers and directives associated with the injector's corresponding node to get
// the instance.
const instance: T|null =
searchTokensOnInjector<T>(injectorIndex, lViewData, token, previousTView);
if (instance !== NOT_FOUND) {
return instance;
}
}
// If no injector is found, we *know* that there is no ancestor injector that contains the
// token, so we abort.
if (injectorIndex === -1) {
break;
if (shouldSearchParent(flags, parentLocation) &&
bloomHasToken(bloomHash, injectorIndex, lViewData)) {
// The def wasn't found anywhere on this node, so it was a false positive.
// Traverse up the tree and continue searching.
previousTView = tView;
injectorIndex = getParentInjectorIndex(parentLocation);
lViewData = getParentInjectorView(parentLocation, lViewData);
} else {
// If we should not search parent OR If the ancestor bloom filter value does not have the
// bit corresponding to the directive we can give up on traversing up to find the specific
// injector.
injectorIndex = -1;
}
// At this point, we have an injector which *may* contain the token, so we step through the
// directives associated with the injector's corresponding node to get the directive instance.
let instance: T|null;
if (instance = searchDirectivesOnInjector<T>(injectorIndex, injectorView, token)) {
return instance;
}
// If we *didn't* find the directive for the token and we are searching the current node's
// injector, it's possible the directive is on this node and hasn't been created yet.
if (injectorIndex === startInjectorIndex && hostView === injectorView &&
(instance = searchMatchesQueuedForCreation<T>(token, injectorView[TVIEW]))) {
return instance;
}
// The def wasn't found anywhere on this node, so it was a false positive.
// Traverse up the tree and continue searching.
injectorIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
injectorView = getParentInjectorView(parentLocation, injectorView);
}
}
const moduleInjector = hostView[INJECTOR];
const formerInjector = setCurrentInjector(moduleInjector);
try {
return inject(token, flags);
} finally {
setCurrentInjector(formerInjector);
}
}
function searchMatchesQueuedForCreation<T>(token: any, hostTView: TView): T|null {
const matches = hostTView.currentMatches;
if (matches) {
for (let i = 0; i < matches.length; i += 2) {
const def = matches[i] as DirectiveDef<any>;
if (def.type === token) {
return resolveDirective(def, i + 1, matches);
}
if ((flags & (InjectFlags.Self | InjectFlags.Host)) === 0) {
const moduleInjector = lViewData[INJECTOR];
if (moduleInjector) {
return moduleInjector.get(token, notFoundValue, flags & InjectFlags.Optional);
} else {
return injectRootLimpMode(token, notFoundValue, flags & InjectFlags.Optional);
}
}
return null;
if (flags & InjectFlags.Optional) {
return notFoundValue;
} else {
throw new Error(`NodeInjector: NOT_FOUND [${stringify(token)}]`);
}
}
function searchDirectivesOnInjector<T>(
injectorIndex: number, injectorView: LViewData, token: Type<T>| InjectionToken<T>) {
const tNode = injectorView[TVIEW].data[injectorIndex + TNODE] as TNode;
const NOT_FOUND = {};
function searchTokensOnInjector<T>(
injectorIndex: number, injectorView: LViewData, token: Type<T>| InjectionToken<T>,
previousTView: TView | null) {
const currentTView = injectorView[TVIEW];
const tNode = currentTView.data[injectorIndex + TNODE] as TNode;
const nodeFlags = tNode.flags;
const count = nodeFlags & TNodeFlags.DirectiveCountMask;
if (count !== 0) {
const start = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
const end = start + count;
const defs = injectorView[TVIEW].data;
for (let i = start; i < end; i++) {
// Get the definition for the directive at this index and, if it is injectable (diPublic),
// and matches the given token, return the directive instance.
const directiveDef = defs[i] as DirectiveDef<any>;
if (directiveDef.type === token && directiveDef.diPublic) {
return injectorView[i];
}
const nodeProviderIndexes = tNode.providerIndexes;
const tInjectables = currentTView.data;
// First, we step through providers
let canAccessViewProviders = false;
// We need to determine if view providers can be accessed by the starting element.
// It happens in 2 cases:
// 1) On the initial element injector , if we are instantiating a token which can see the
// viewProviders of the component of that element. Such token are:
// - the component itself (but not other directives)
// - viewProviders tokens of the component (but not providers tokens)
// 2) Upper in the element injector tree, if the starting element is actually in the view of
// the current element. To determine this, we track the transition of view during the climb,
// and check the host node of the current view to identify component views.
if (previousTView == null && isComponent(tNode) && includeViewProviders ||
previousTView != null && previousTView != currentTView &&
(currentTView.node == null || currentTView.node !.type === TNodeType.Element)) {
canAccessViewProviders = true;
}
const startInjectables = nodeProviderIndexes & TNodeProviderIndexes.ProvidersStartIndexMask;
const startDirectives = nodeFlags >> TNodeFlags.DirectiveStartingIndexShift;
const cptViewProvidersCount =
nodeProviderIndexes >> TNodeProviderIndexes.CptViewProvidersCountShift;
const startingIndex =
canAccessViewProviders ? startInjectables : startInjectables + cptViewProvidersCount;
const directiveCount = nodeFlags & TNodeFlags.DirectiveCountMask;
for (let i = startingIndex; i < startDirectives + directiveCount; i++) {
const providerTokenOrDef = tInjectables[i] as InjectionToken<any>| Type<any>| DirectiveDef<any>;
if (i < startDirectives && token === providerTokenOrDef ||
i >= startDirectives && (providerTokenOrDef as DirectiveDef<any>).type === token) {
return getNodeInjectable(tInjectables, injectorView, i, tNode as TElementNode);
}
}
return null;
return NOT_FOUND;
}
/**
* Retrieve or instantiate the injectable from the `lData` at particular `index`.
*
* This function checks to see if the value has already been instantiated and if so returns the
* cached `injectable`. Otherwise if it detects that the value is still a factory it
* instantiates the `injectable` and caches the value.
*/
export function getNodeInjectable(
tData: TData, lData: LViewData, index: number, tNode: TElementNode): any {
let value = lData[index];
if (isFactory(value)) {
const factory: NodeInjectorFactory = value;
if (factory.resolving) {
throw new Error(`Circular dep for ${stringify(tData[index])}`);
}
const previousIncludeViewProviders = setIncludeViewProviders(factory.canSeeViewProviders);
factory.resolving = true;
let previousInjectImplementation;
if (factory.injectImpl) {
previousInjectImplementation = setInjectImplementation(factory.injectImpl);
}
const savePreviousOrParentTNode = getPreviousOrParentTNode();
const saveViewData = getViewData();
setTNodeAndViewData(tNode, lData);
try {
value = lData[index] = factory.factory(null, tData, lData, tNode);
} finally {
if (factory.injectImpl) setInjectImplementation(previousInjectImplementation);
setIncludeViewProviders(previousIncludeViewProviders);
factory.resolving = false;
setTNodeAndViewData(savePreviousOrParentTNode, saveViewData);
}
}
return value;
}
/**
@ -452,7 +471,7 @@ export function bloomHashBitOrFactory(token: Type<any>| InjectionToken<any>): nu
return typeof tokenId === 'number' ? tokenId & BLOOM_MASK : tokenId;
}
export function injectorHasToken(
export function bloomHasToken(
bloomHash: number, injectorIndex: number, injectorView: LViewData | TData) {
// Create a mask that targets the specific bit associated with the directive we're looking for.
// JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
@ -481,9 +500,16 @@ export function injectorHasToken(
}
/** Returns true if flags prevent parent injector from being searched for tokens */
function shouldNotSearchParent(flags: InjectFlags, parentLocation: number): boolean|number {
return flags & InjectFlags.Self ||
(flags & InjectFlags.Host && (parentLocation >> InjectorLocationFlags.ViewOffsetShift) > 0);
function shouldSearchParent(flags: InjectFlags, parentLocation: RelativeInjectorLocation): boolean|
number {
return !(
flags & InjectFlags.Self ||
(flags & InjectFlags.Host && getParentInjectorViewOffset(parentLocation) > 0));
}
export function injectInjector() {
const tNode = getPreviousOrParentTNode() as TElementNode | TContainerNode | TElementContainerNode;
return new NodeInjector(tNode, getViewData());
}
export class NodeInjector implements Injector {
@ -496,11 +522,12 @@ export class NodeInjector implements Injector {
}
get(token: any): any {
setEnvironment(this._tNode, this._hostView);
setTNodeAndViewData(this._tNode, this._hostView);
return getOrCreateInjectable(this._tNode, this._hostView, token);
}
}
export function getFactoryOf<T>(type: Type<any>): ((type?: Type<T>) => T)|null {
export function getFactoryOf<T>(type: Type<any>): ((type: Type<T>| null) => T)|null {
const typeAny = type as any;
const def = getComponentDef<T>(typeAny) || getDirectiveDef<T>(typeAny) ||
getPipeDef<T>(typeAny) || getInjectableDef<T>(typeAny) || getInjectorDef<T>(typeAny);

View File

@ -0,0 +1,260 @@
/**
* @license
* Copyright Google Inc. 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 {resolveForwardRef} from '../di/forward_ref';
import {Provider} from '../di/provider';
import {isTypeProvider, providerToFactory} from '../di/r3_injector';
import {DirectiveDef} from '.';
import {diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode} from './di';
import {directiveInject} from './instructions';
import {NodeInjectorFactory} from './interfaces/injector';
import {TContainerNode, TElementContainerNode, TElementNode, TNodeFlags, TNodeProviderIndexes} from './interfaces/node';
import {LViewData, TData, TVIEW, TView} from './interfaces/view';
import {getPreviousOrParentTNode, getViewData} from './state';
import {isComponentDef} from './util';
/**
* Resolves the providers which are defined in the DirectiveDef.
*
* When inserting the tokens and the factories in their respective arrays, we can assume that
* this method is called first for the component (if any), and then for other directives on the same
* node.
* As a consequence,the providers are always processed in that order:
* 1) The view providers of the component
* 2) The providers of the component
* 3) The providers of the other directives
* This matches the structure of the injectables arrays of a view (for each node).
* So the tokens and the factories can be pushed at the end of the arrays, except
* in one case for multi providers.
*
* @param def the directive definition
* @param providers: Array of `providers`.
* @param viewProviders: Array of `viewProviders`.
*/
export function providersResolver<T>(
def: DirectiveDef<T>, providers: Provider[], viewProviders: Provider[]): void {
const viewData = getViewData();
const tView: TView = viewData[TVIEW];
if (tView.firstTemplatePass) {
const isComponent = isComponentDef(def);
// The list of view providers is processed first, and the flags are updated
resolveProvider(viewProviders, tView.data, tView.blueprint, isComponent, true);
// Then, the list of providers is processed, and the flags are updated
resolveProvider(providers, tView.data, tView.blueprint, isComponent, false);
}
}
/**
* Resolves a provider and publishes it to the DI system.
*/
function resolveProvider(
provider: Provider, tInjectables: TData, lInjectablesBlueprint: NodeInjectorFactory[],
isComponent: boolean, isViewProvider: boolean): void {
provider = resolveForwardRef(provider);
if (Array.isArray(provider)) {
// Recursively call `resolveProvider`
// Recursion is OK in this case because this code will not be in hot-path once we implement
// cloning of the initial state.
for (let i = 0; i < provider.length; i++) {
resolveProvider(
provider[i], tInjectables, lInjectablesBlueprint, isComponent, isViewProvider);
}
} else {
const viewData = getViewData();
let token: any = isTypeProvider(provider) ? provider : resolveForwardRef(provider.provide);
let providerFactory: () => any = providerToFactory(provider);
const previousOrParentTNode = getPreviousOrParentTNode();
const beginIndex =
previousOrParentTNode.providerIndexes & TNodeProviderIndexes.ProvidersStartIndexMask;
const endIndex = previousOrParentTNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
const cptViewProvidersCount =
previousOrParentTNode.providerIndexes >> TNodeProviderIndexes.CptViewProvidersCountShift;
if (isTypeProvider(provider) || !provider.multi) {
// Single provider case: the factory is created and pushed immediately
const factory = new NodeInjectorFactory(providerFactory, isViewProvider, directiveInject);
const existingFactoryIndex = indexOf(
token, tInjectables, isViewProvider ? beginIndex : beginIndex + cptViewProvidersCount,
endIndex);
if (existingFactoryIndex == -1) {
diPublicInInjector(
getOrCreateNodeInjectorForNode(
previousOrParentTNode as TElementNode | TContainerNode | TElementContainerNode,
viewData),
viewData, token);
tInjectables.push(token);
previousOrParentTNode.flags += 1 << TNodeFlags.DirectiveStartingIndexShift;
if (isViewProvider) {
previousOrParentTNode.providerIndexes +=
TNodeProviderIndexes.CptViewProvidersCountShifter;
}
lInjectablesBlueprint.push(factory);
viewData.push(factory);
} else {
lInjectablesBlueprint[existingFactoryIndex] = factory;
viewData[existingFactoryIndex] = factory;
}
} else {
// Multi provider case:
// We create a multi factory which is going to aggregate all the values.
// Since the output of such a factory depends on content or view injection,
// we create two of them, which are linked together.
//
// The first one (for view providers) is always in the first block of the injectables array,
// and the second one (for providers) is always in the second block.
// This is important because view providers have higher priority. When a multi token
// is being looked up, the view providers should be found first.
// Note that it is not possible to have a multi factory in the third block (directive block).
//
// The algorithm to process multi providers is as follows:
// 1) If the multi provider comes from the `viewProviders` of the component:
// a) If the special view providers factory doesn't exist, it is created and pushed.
// b) Else, the multi provider is added to the existing multi factory.
// 2) If the multi provider comes from the `providers` of the component or of another
// directive:
// a) If the multi factory doesn't exist, it is created and provider pushed into it.
// It is also linked to the multi factory for view providers, if it exists.
// b) Else, the multi provider is added to the existing multi factory.
const existingProvidersFactoryIndex =
indexOf(token, tInjectables, beginIndex + cptViewProvidersCount, endIndex);
const existingViewProvidersFactoryIndex =
indexOf(token, tInjectables, beginIndex, beginIndex + cptViewProvidersCount);
const doesProvidersFactoryExist = existingProvidersFactoryIndex >= 0 &&
lInjectablesBlueprint[existingProvidersFactoryIndex];
const doesViewProvidersFactoryExist = existingViewProvidersFactoryIndex >= 0 &&
lInjectablesBlueprint[existingViewProvidersFactoryIndex];
if (isViewProvider && !doesViewProvidersFactoryExist ||
!isViewProvider && !doesProvidersFactoryExist) {
// Cases 1.a and 2.a
diPublicInInjector(
getOrCreateNodeInjectorForNode(
previousOrParentTNode as TElementNode | TContainerNode | TElementContainerNode,
viewData),
viewData, token);
const factory = multiFactory(
isViewProvider ? multiViewProvidersFactoryResolver : multiProvidersFactoryResolver,
lInjectablesBlueprint.length, isViewProvider, isComponent, providerFactory);
if (!isViewProvider && doesViewProvidersFactoryExist) {
lInjectablesBlueprint[existingViewProvidersFactoryIndex].providerFactory = factory;
}
tInjectables.push(token);
previousOrParentTNode.flags += 1 << TNodeFlags.DirectiveStartingIndexShift;
if (isViewProvider) {
previousOrParentTNode.providerIndexes +=
TNodeProviderIndexes.CptViewProvidersCountShifter;
}
lInjectablesBlueprint.push(factory);
viewData.push(factory);
} else {
// Cases 1.b and 2.b
multiFactoryAdd(
lInjectablesBlueprint ![isViewProvider ? existingViewProvidersFactoryIndex : existingProvidersFactoryIndex],
providerFactory, !isViewProvider && isComponent);
}
if (!isViewProvider && isComponent && doesViewProvidersFactoryExist) {
lInjectablesBlueprint[existingViewProvidersFactoryIndex].componentProviders !++;
}
}
}
}
/**
* Add a factory in a multi factory.
*/
function multiFactoryAdd(
multiFactory: NodeInjectorFactory, factory: () => any, isComponentProvider: boolean): void {
multiFactory.multi !.push(factory);
if (isComponentProvider) {
multiFactory.componentProviders !++;
}
}
/**
* Returns the index of item in the array, but only in the begin to end range.
*/
function indexOf(item: any, arr: any[], begin: number, end: number) {
for (let i = begin; i < end; i++) {
if (arr[i] === item) return i;
}
return -1;
}
/**
* Use this with `multi` `providers`.
*/
function multiProvidersFactoryResolver(
this: NodeInjectorFactory, _: null, tData: TData, lData: LViewData,
tNode: TElementNode): any[] {
return multiResolve(this.multi !, []);
}
/**
* Use this with `multi` `viewProviders`.
*
* This factory knows how to concatenate itself with the existing `multi` `providers`.
*/
function multiViewProvidersFactoryResolver(
this: NodeInjectorFactory, _: null, tData: TData, lData: LViewData,
tNode: TElementNode): any[] {
const factories = this.multi !;
let result: any[];
if (this.providerFactory) {
const componentCount = this.providerFactory.componentProviders !;
const multiProviders = getNodeInjectable(tData, lData, this.providerFactory !.index !, tNode);
// Copy the section of the array which contains `multi` `providers` from the component
result = multiProviders.slice(0, componentCount);
// Insert the `viewProvider` instances.
multiResolve(factories, result);
// Copy the section of the array which contains `multi` `providers` from other directives
for (let i = componentCount; i < multiProviders.length; i++) {
result.push(multiProviders[i]);
}
} else {
result = [];
// Insert the `viewProvider` instances.
multiResolve(factories, result);
}
return result;
}
/**
* Maps an array of factories into an array of values.
*/
function multiResolve(factories: Array<() => any>, result: any[]): any[] {
for (let i = 0; i < factories.length; i++) {
const factory = factories[i] !as() => null;
result.push(factory());
}
return result;
}
/**
* Creates a multi factory.
*/
function multiFactory(
factoryFn:
(this: NodeInjectorFactory, _: null, tData: TData, lData: LViewData, tNode: TElementNode) =>
any,
index: number, isViewProvider: boolean, isComponent: boolean,
f: () => any): NodeInjectorFactory {
const factory = new NodeInjectorFactory(factoryFn, isViewProvider, directiveInject);
factory.multi = [];
factory.index = index;
factory.componentProviders = 0;
multiFactoryAdd(factory, f, isComponent && !isViewProvider);
return factory;
}

View File

@ -9,11 +9,12 @@ import {Injector} from '../di/injector';
import {assertDefined} from './assert';
import {discoverDirectives, discoverLocalRefs, getContext, isComponentInstance} from './context_discovery';
import {NodeInjector} from './di';
import {LContext} from './interfaces/context';
import {TElementNode, TNode, TNodeFlags} from './interfaces/node';
import {CONTEXT, FLAGS, LViewData, LViewFlags, PARENT, RootContext, TVIEW} from './interfaces/view';
import {getComponentViewByIndex, readPatchedLViewData} from './util';
import {NodeInjector} from './view_engine_compatibility';
/**
@ -37,14 +38,14 @@ export function getComponent<T = {}>(target: {}): T|null {
const context = loadContext(target) !;
if (context.component === undefined) {
let lViewData = context.lViewData;
let lViewData: LViewData|null = context.lViewData;
while (lViewData) {
const ctx = lViewData ![CONTEXT] !as{};
if (ctx && isComponentInstance(ctx)) {
context.component = ctx;
break;
}
lViewData = lViewData ![PARENT] !;
lViewData = lViewData[FLAGS] & LViewFlags.IsRoot ? null : lViewData ![PARENT] !;
}
if (context.component === undefined) {
context.component = null;

View File

@ -102,9 +102,9 @@ export function InheritDefinitionFeature(definition: DirectiveDef<any>| Componen
const superContentQueries = superDef.contentQueries;
if (superContentQueries) {
if (prevContentQueries) {
definition.contentQueries = () => {
superContentQueries();
prevContentQueries();
definition.contentQueries = (dirIndex: number) => {
superContentQueries(dirIndex);
prevContentQueries(dirIndex);
};
} else {
definition.contentQueries = superContentQueries;

View File

@ -0,0 +1,45 @@
/**
* @license
* Copyright Google Inc. 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 {Provider} from '../../di/provider';
import {providersResolver} from '../di_setup';
import {DirectiveDef} from '../interfaces/definition';
/**
* This feature resolves the providers of a directive (or component),
* and publish them into the DI system, making it visible to others for injection.
*
* For example:
* class ComponentWithProviders {
* constructor(private greeter: GreeterDE) {}
*
* static ngComponentDef = defineComponent({
* type: ComponentWithProviders,
* selectors: [['component-with-providers']],
* factory: () => new ComponentWithProviders(directiveInject(GreeterDE as any)),
* consts: 1,
* vars: 1,
* template: function(fs: RenderFlags, ctx: ComponentWithProviders) {
* if (fs & RenderFlags.Create) {
* text(0);
* }
* if (fs & RenderFlags.Update) {
* textBinding(0, bind(ctx.greeter.greet()));
* }
* },
* features: [ProvidersFeature([GreeterDE])]
* });
* }
*
* @param definition
*/
export function ProvidersFeature<T>(providers: Provider[], viewProviders: Provider[] = []) {
return (definition: DirectiveDef<T>) => {
definition.providersResolver = (def: DirectiveDef<T>) =>
providersResolver(def, providers, viewProviders);
};
}

View File

@ -1,19 +0,0 @@
/**
* @license
* Copyright Google Inc. 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 {diPublic} from '../di';
import {DirectiveDef} from '../interfaces/definition';
/**
* This feature publishes the directive (or component) into the DI system, making it visible to
* others for injection.
*
* @param definition
*/
export function PublicFeature<T>(definition: DirectiveDef<T>) {
definition.diPublic = diPublic;
}

View File

@ -6,14 +6,17 @@
* found in the LICENSE file at https://angular.io/license
*/
import {NO_CHANGE} from '../../src/render3/tokens';
import {assertEqual, assertLessThan} from './assert';
import {NO_CHANGE, _getViewData, adjustBlueprintForNewNode, bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, createNodeAtIndex, getRenderer, load, resetComponentState} from './instructions';
import {adjustBlueprintForNewNode, bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, createNodeAtIndex, load} from './instructions';
import {LContainer, NATIVE, RENDER_PARENT} from './interfaces/container';
import {TElementNode, TNode, TNodeType} from './interfaces/node';
import {RComment, RElement} from './interfaces/renderer';
import {StylingContext} from './interfaces/styling';
import {BINDING_INDEX, HEADER_OFFSET, HOST_NODE, TVIEW} from './interfaces/view';
import {appendChild, createTextNode, removeChild} from './node_manipulation';
import {getRenderer, getViewData, resetComponentState} from './state';
import {getNativeByIndex, getNativeByTNode, getTNode, isLContainer, stringify} from './util';
@ -254,7 +257,7 @@ function appendI18nNode(tNode: TNode, parentTNode: TNode, previousTNode: TNode):
ngDevMode.rendererMoveNode++;
}
const viewData = _getViewData();
const viewData = getViewData();
// On first pass, re-organize node tree to put this node in the correct position.
const firstTemplatePass = viewData[TVIEW].firstTemplatePass;
@ -309,7 +312,7 @@ export function i18nEnd(): void {
* @param instructions The list of instructions to apply on the current view.
*/
export function i18nApply(startIndex: number, instructions: I18nInstruction[]): void {
const viewData = _getViewData();
const viewData = getViewData();
if (ngDevMode) {
assertEqual(
viewData[BINDING_INDEX], viewData[TVIEW].bindingStartIndex,
@ -409,7 +412,7 @@ export function i18nExpMapping(
* @returns The concatenated string when any of the arguments changes, `NO_CHANGE` otherwise.
*/
export function i18nInterpolation1(instructions: I18nExpInstruction[], v0: any): string|NO_CHANGE {
const different = bindingUpdated(_getViewData()[BINDING_INDEX]++, v0);
const different = bindingUpdated(getViewData()[BINDING_INDEX]++, v0);
if (!different) {
return NO_CHANGE;
@ -440,7 +443,7 @@ export function i18nInterpolation1(instructions: I18nExpInstruction[], v0: any):
*/
export function i18nInterpolation2(instructions: I18nExpInstruction[], v0: any, v1: any): string|
NO_CHANGE {
const viewData = _getViewData();
const viewData = getViewData();
const different = bindingUpdated2(viewData[BINDING_INDEX], v0, v1);
viewData[BINDING_INDEX] += 2;
@ -480,7 +483,7 @@ export function i18nInterpolation2(instructions: I18nExpInstruction[], v0: any,
*/
export function i18nInterpolation3(
instructions: I18nExpInstruction[], v0: any, v1: any, v2: any): string|NO_CHANGE {
const viewData = _getViewData();
const viewData = getViewData();
const different = bindingUpdated3(viewData[BINDING_INDEX], v0, v1, v2);
viewData[BINDING_INDEX] += 3;
@ -522,7 +525,7 @@ export function i18nInterpolation3(
*/
export function i18nInterpolation4(
instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any): string|NO_CHANGE {
const viewData = _getViewData();
const viewData = getViewData();
const different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3);
viewData[BINDING_INDEX] += 4;
@ -566,7 +569,7 @@ export function i18nInterpolation4(
export function i18nInterpolation5(
instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any): string|
NO_CHANGE {
const viewData = _getViewData();
const viewData = getViewData();
let different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3);
different = bindingUpdated(viewData[BINDING_INDEX] + 4, v4) || different;
viewData[BINDING_INDEX] += 5;
@ -613,7 +616,7 @@ export function i18nInterpolation5(
i18nInterpolation6(
instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any, v5: any):
string|NO_CHANGE {
const viewData = _getViewData();
const viewData = getViewData();
let different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3);
different = bindingUpdated2(viewData[BINDING_INDEX] + 4, v4, v5) || different;
viewData[BINDING_INDEX] += 6;
@ -661,7 +664,7 @@ i18nInterpolation6(
export function i18nInterpolation7(
instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any, v5: any,
v6: any): string|NO_CHANGE {
const viewData = _getViewData();
const viewData = getViewData();
let different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3);
different = bindingUpdated3(viewData[BINDING_INDEX] + 4, v4, v5, v6) || different;
viewData[BINDING_INDEX] += 7;
@ -710,7 +713,7 @@ export function i18nInterpolation7(
export function i18nInterpolation8(
instructions: I18nExpInstruction[], v0: any, v1: any, v2: any, v3: any, v4: any, v5: any,
v6: any, v7: any): string|NO_CHANGE {
const viewData = _getViewData();
const viewData = getViewData();
let different = bindingUpdated4(viewData[BINDING_INDEX], v0, v1, v2, v3);
different = bindingUpdated4(viewData[BINDING_INDEX] + 4, v4, v5, v6, v7) || different;
viewData[BINDING_INDEX] += 8;
@ -751,7 +754,7 @@ export function i18nInterpolation8(
*/
export function i18nInterpolationV(instructions: I18nExpInstruction[], values: any[]): string|
NO_CHANGE {
const viewData = _getViewData();
const viewData = getViewData();
let different = false;
for (let i = 0; i < values.length; i++) {
// Check if bindings have changed

View File

@ -5,24 +5,20 @@
* 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 {LifecycleHooksFeature, getHostElement, getRenderedText, renderComponent, whenRendered} from './component';
import {defineBase, defineComponent, defineDirective, defineNgModule, definePipe} from './definition';
import {InheritDefinitionFeature} from './features/inherit_definition_feature';
import {NgOnChangesFeature} from './features/ng_onchanges_feature';
import {PublicFeature} from './features/public_feature';
import {ProvidersFeature} from './features/providers_feature';
import {BaseDef, ComponentDef, ComponentDefWithMeta, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveDefWithMeta, DirectiveType, PipeDef, PipeDefWithMeta} from './interfaces/definition';
export {ComponentFactory, ComponentFactoryResolver, ComponentRef, WRAP_RENDERER_FACTORY2, injectComponentFactoryResolver} from './component_ref';
export {directiveInject, getFactoryOf, getInheritedFactory, injectAttribute} from './di';
export {getFactoryOf, getInheritedFactory} from './di';
export {RenderFlags} from './interfaces/definition';
export {CssSelectorList} from './interfaces/projection';
// clang-format off
export {
NO_CHANGE,
bind,
interpolation1,
interpolation2,
@ -55,9 +51,6 @@ export {
elementStyleProp,
elementStylingApply,
getCurrentView,
restoreView,
listener,
store,
load,
@ -66,9 +59,6 @@ export {
namespaceMathML,
namespaceSVG,
enableBindings,
disableBindings,
projection,
projectionDef,
@ -83,8 +73,19 @@ export {
detectChanges,
markDirty,
tick,
directiveInject,
injectAttribute,
} from './instructions';
export {
getCurrentView,
restoreView,
enableBindings,
disableBindings,
} from './state';
export {
i18nAttribute,
i18nExp,
@ -161,7 +162,7 @@ export {
DirectiveType,
NgOnChangesFeature,
InheritDefinitionFeature,
PublicFeature,
ProvidersFeature,
PipeDef,
PipeDefWithMeta,
LifecycleHooksFeature,
@ -175,3 +176,5 @@ export {
renderComponent,
whenRendered,
};
export {NO_CHANGE} from './tokens';

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Provider, ViewEncapsulation} from '../../core';
import {ViewEncapsulation} from '../../core';
import {Type} from '../../type';
import {CssSelectorList} from './projection';
@ -112,11 +112,11 @@ export interface DirectiveDef<T> extends BaseDef<T> {
/** Token representing the directive. Used by DI. */
type: Type<T>;
/** Function that makes a directive public to the DI system. */
diPublic: ((def: DirectiveDef<T>) => void)|null;
/** Function that resolves providers and publishes them into the DI system. */
providersResolver: ((def: DirectiveDef<T>) => void)|null;
/** The selectors that will be used to match nodes to this directive. */
selectors: CssSelectorList;
readonly selectors: CssSelectorList;
/**
* Name under which the directive is exported (for use with local references in template)
@ -126,12 +126,12 @@ export interface DirectiveDef<T> extends BaseDef<T> {
/**
* Factory function used to create a new directive instance.
*/
factory(): T;
factory: (t: Type<T>|null) => T;
/**
* Function to create instances of content queries associated with a given directive.
*/
contentQueries: (() => void)|null;
contentQueries: ((directiveIndex: number) => void)|null;
/** Refreshes content queries associated with directives in a given view */
contentQueriesRefresh: ((directiveIndex: number, queryIndex: number) => void)|null;
@ -142,7 +142,7 @@ export interface DirectiveDef<T> extends BaseDef<T> {
* Used to calculate the length of the LViewData array for the *parent* component
* of this directive/component.
*/
hostVars: number;
readonly hostVars: number;
/** Refreshes host bindings on the associated directive. */
hostBindings: HostBindingsFunction|null;
@ -153,7 +153,7 @@ export interface DirectiveDef<T> extends BaseDef<T> {
* Even indices: attribute name
* Odd indices: attribute value
*/
attributes: string[]|null;
readonly attributes: string[]|null;
/* The following are lifecycle hooks for this component */
onInit: (() => void)|null;
@ -167,7 +167,7 @@ export interface DirectiveDef<T> extends BaseDef<T> {
/**
* The features applied to this directive
*/
features: DirectiveDefFeature[]|null;
readonly features: DirectiveDefFeature[]|null;
}
export type ComponentDefWithMeta<
@ -245,18 +245,7 @@ export interface ComponentDef<T> extends DirectiveDef<T> {
readonly onPush: boolean;
/**
* Defines the set of injectable providers that are visible to a Directive and its content DOM
* children.
*/
readonly providers: Provider[]|null;
/**
* Defines the set of injectable providers that are visible to a Directive and its view DOM
* children only.
*/
readonly viewProviders: Provider[]|null;
/**
* Registry of directives and components that may be found in this view.
*
* The property is either an array of `DirectiveDef`s or a function which returns the array of
@ -297,12 +286,12 @@ export interface PipeDef<T> {
*
* Used to resolve pipe in templates.
*/
name: string;
readonly name: string;
/**
* Factory function used to create a new pipe instance.
*/
factory: () => T;
factory: (t: Type<T>|null) => T;
/**
* Whether or not the pipe is pure.
@ -310,7 +299,7 @@ export interface PipeDef<T> {
* Pure pipes result only depends on the pipe input and not on internal
* state of the pipe.
*/
pure: boolean;
readonly pure: boolean;
/* The following are lifecycle hooks for this pipe */
onDestroy: (() => void)|null;

View File

@ -6,18 +6,32 @@
* found in the LICENSE file at https://angular.io/license
*/
import {TContainerNode, TElementContainerNode, TElementNode,} from './node';
import {InjectionToken} from '../../di/injection_token';
import {InjectFlags} from '../../di/injector_compatibility';
import {Type} from '../../type';
import {TElementNode} from './node';
import {LViewData, TData} from './view';
export const TNODE = 8;
export const PARENT_INJECTOR = 8;
export const INJECTOR_SIZE = 9;
export const enum InjectorLocationFlags {
/**
* Represents a relative location of parent injector.
*
* The interfaces encodes number of parents `LViewData`s to traverse and index in the `LViewData`
* pointing to the parent injector.
*/
export interface RelativeInjectorLocation { __brand__: 'RelativeInjectorLocationFlags'; }
export const enum RelativeInjectorLocationFlags {
InjectorIndexMask = 0b111111111111111,
ViewOffsetShift = 15
ViewOffsetShift = 15,
NO_PARENT = -1,
}
export const NO_PARENT_INJECTOR: RelativeInjectorLocation = -1 as any;
/**
* Each injector is saved in 9 contiguous slots in `LViewData` and 9 contiguous slots in
* `TView.data`. This allows us to store information about the current node's tokens (which
@ -98,6 +112,140 @@ export const enum InjectorLocationFlags {
* }
*/
/**
* Factory for creating instances of injectors in the NodeInjector.
*
* This factory is complicated by the fact that it can resolve `multi` factories as well.
*
* NOTE: Some of the fields are optional which means that this class has two hidden classes.
* - One without `multi` support (most common)
* - One with `multi` values, (rare).
*
* Since VMs can cache up to 4 inline hidden classes this is OK.
*
* - Single factory: Only `resolving` and `factory` is defined.
* - `providers` factory: `componentProviders` is a number and `index = -1`.
* - `viewProviders` factory: `componentProviders` is a number and `index` points to `providers`.
*/
export class NodeInjectorFactory {
/**
* The inject implementation to be activated when using the factory.
*/
injectImpl: null|(<T>(token: Type<T>|InjectionToken<T>, flags: InjectFlags) => T);
/**
* Marker set to true during factory invocation to see if we get into recursive loop.
* Recursive loop causes an error to be displayed.
*/
resolving = false;
/**
* Marks that the token can see other Tokens declared in `viewProviders` on the same node.
*/
canSeeViewProviders: boolean;
/**
* An array of factories to use in case of `multi` provider.
*/
multi?: Array<() => any>;
/**
* Number of `multi`-providers which belong to the component.
*
* This is needed because when multiple components and directives declare the `multi` provider
* they have to be concatenated in the correct order.
*
* Example:
*
* If we have a component and directive active an a single element as declared here
* ```
* component:
* provides: [ {provide: String, useValue: 'component', multi: true} ],
* viewProvides: [ {provide: String, useValue: 'componentView', multi: true} ],
*
* directive:
* provides: [ {provide: String, useValue: 'directive', multi: true} ],
* ```
*
* Then the expected results are:
*
* ```
* providers: ['component', 'directive']
* viewProviders: ['component', 'componentView', 'directive']
* ```
*
* The way to think about it is that the `viewProviders` have been inserted after the component
* but before the directives, which is why we need to know how many `multi`s have been declared by
* the component.
*/
componentProviders?: number;
/**
* Current index of the Factory in the `data`. Needed for `viewProviders` and `providers` merging.
* See `providerFactory`.
*/
index?: number;
/**
* Because the same `multi` provider can be declared in `provides` and `viewProvides` it is
* possible for `viewProvides` to shadow the `provides`. For this reason we store the
* `provideFactory` of the `providers` so that `providers` can be extended with `viewProviders`.
*
* Example:
*
* Given:
* ```
* provides: [ {provide: String, useValue: 'all', multi: true} ],
* viewProvides: [ {provide: String, useValue: 'viewOnly', multi: true} ],
* ```
*
* We have to return `['all']` in case of content injection, but `['all', 'viewOnly']` in case
* of view injection. We further have to make sure that the shared instances (in our case
* `all`) are the exact same instance in both the content as well as the view injection. (We
* have to make sure that we don't double instantiate.) For this reason the `viewProvides`
* `Factory` has a pointer to the shadowed `provides` factory so that it can instantiate the
* `providers` (`['all']`) and then extend it with `viewProviders` (`['all'] + ['viewOnly'] =
* ['all', 'viewOnly']`).
*/
providerFactory?: NodeInjectorFactory|null;
constructor(
/**
* Factory to invoke in order to create a new instance.
*/
public factory:
(this: NodeInjectorFactory, _: null,
/**
* array where injectables tokens are stored. This is used in
* case of an error reporting to produce friendlier errors.
*/
tData: TData,
/**
* array where existing instances of injectables are stored. This is used in case
* of multi shadow is needed. See `multi` field documentation.
*/
lData: LViewData,
/**
* The TNode of the same element injector.
*/
tNode: TElementNode) => any,
/**
* Set to `true` if the token is declared in `viewProviders` (or if it is component).
*/
isViewProvider: boolean,
injectImplementation: null|(<T>(token: Type<T>|InjectionToken<T>, flags: InjectFlags) => T)) {
this.canSeeViewProviders = isViewProvider;
this.injectImpl = injectImplementation;
}
}
const FactoryPrototype = NodeInjectorFactory.prototype;
export function isFactory(obj: any): obj is NodeInjectorFactory {
// See: https://jsperf.com/instanceof-vs-getprototypeof
return obj != null && typeof obj == 'object' && Object.getPrototypeOf(obj) == FactoryPrototype;
}
// Note: This hack is necessary so we don't erroneously get a circular dependency
// failure based on types.
export const unusedValueExportToPlacateAjd = 1;

View File

@ -39,10 +39,25 @@ export const enum TNodeFlags {
/** This bit is set if the node has any content queries */
hasContentQuery = 0b00000000000000000100000000000000,
/** This bit is set if the node has any directives that contain [class properties */
hasClassInput = 0b00000000000000001000000000000000,
/** The index of the first directive on this node is encoded on the most significant bits */
DirectiveStartingIndexShift = 15,
DirectiveStartingIndexShift = 16,
}
/**
* Corresponds to the TNode.providerIndexes property.
*/
export const enum TNodeProviderIndexes {
/** The index of the first provider on this node is encoded on the least significant bits */
ProvidersStartIndexMask = 0b00000000000000001111111111111111,
/** The count of view providers from the component on this node is encoded on the 16 most
significant bits */
CptViewProvidersCountShift = 16,
CptViewProvidersCountShifter = 0b00000000000000010000000000000000,
}
/**
* A set of marker values to be used in the attributes arrays. Those markers indicate that some
* items are not regular attributes and the processing should be adapted accordingly.
@ -122,6 +137,14 @@ export interface TNode {
*/
flags: TNodeFlags;
/**
* This number stores two values using its bits:
*
* - the index of the first provider on that node (first 16 bits)
* - the count of view providers from the component on this node (last 16 bits)
*/
providerIndexes: TNodeProviderIndexes;
/** The tag name associated with this node. */
tagName: string|null;

View File

@ -156,7 +156,7 @@ export interface StylingContext extends Array<InitialStyles|{[key: string]: any}
* The last class value that was interpreted by elementStylingMap. This is cached
* So that the algorithm can exit early incase the value has not changed.
*/
[StylingIndex.PreviousMultiClassValue]: {[key: string]: any}|string|null;
[StylingIndex.PreviousOrCachedMultiClassValue]: {[key: string]: any}|string|null;
/**
* The last style value that was interpreted by elementStylingMap. This is cached
@ -181,18 +181,21 @@ export interface InitialStyles extends Array<string|null|boolean> { [0]: null; }
*/
export const enum StylingFlags {
// Implies no configurations
None = 0b0000,
None = 0b00000,
// Whether or not the entry or context itself is dirty
Dirty = 0b0001,
Dirty = 0b00001,
// Whether or not this is a class-based assignment
Class = 0b0010,
Class = 0b00010,
// Whether or not a sanitizer was applied to this property
Sanitize = 0b0100,
Sanitize = 0b00100,
// Whether or not any player builders within need to produce new players
PlayerBuildersDirty = 0b1000,
PlayerBuildersDirty = 0b01000,
// If NgClass is present (or some other class handler) then it will handle the map expressions and
// initial classes
OnlyProcessSingleClasses = 0b10000,
// The max amount of bits used to represent these configuration values
BitCountSize = 4,
// There are only three bits here
BitCountSize = 5,
// There are only five bits here
BitMask = 0b1111
}
@ -211,8 +214,9 @@ export const enum StylingIndex {
// Position of where the initial styles are stored in the styling context
// This index must align with HOST, see interfaces/view.ts
ElementPosition = 5,
// Position of where the last string-based CSS class value was stored
PreviousMultiClassValue = 6,
// Position of where the last string-based CSS class value was stored (or a cached version of the
// initial styles when a [class] directive is present)
PreviousOrCachedMultiClassValue = 6,
// Position of where the last string-based CSS class value was stored
PreviousMultiStyleValue = 7,
// Location of single (prop) value entries are stored within the context

View File

@ -6,9 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {InjectionToken} from '../../di/injection_token';
import {Injector} from '../../di/injector';
import {QueryList} from '../../linker';
import {Sanitizer} from '../../sanitization/security';
import {Type} from '../../type';
import {LContainer} from './container';
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefList, HostBindingsFunction, PipeDef, PipeDefList} from './definition';
@ -335,24 +337,6 @@ export interface TView {
*/
firstChild: TNode|null;
/**
* Selector matches for a node are temporarily cached on the TView so the
* DI system can eagerly instantiate directives on the same node if they are
* created out of order. They are overwritten after each node.
*
* <div dirA dirB></div>
*
* e.g. DirA injects DirB, but DirA is created first. DI should instantiate
* DirB when it finds that it's on the same node, but not yet created.
*
* Even indices: Directive defs
* Odd indices:
* - Null if the associated directive hasn't been instantiated yet
* - Directive index, if associated directive has been created
* - String, temporary 'CIRCULAR' token set while dependencies are being resolved
*/
currentMatches: CurrentMatchesList|null;
/**
* Set of instructions used to process host bindings efficiently.
*
@ -549,10 +533,9 @@ export type HookData = (number | (() => void))[];
*
* Injector bloom filters are also stored here.
*/
export type TData = (TNode | PipeDef<any>| DirectiveDef<any>| ComponentDef<any>| number | null)[];
/** Type for TView.currentMatches */
export type CurrentMatchesList = [DirectiveDef<any>, (string | number | null)];
export type TData =
(TNode | PipeDef<any>| DirectiveDef<any>| ComponentDef<any>| number | Type<any>|
InjectionToken<any>| null)[];
// Note: This hack is necessary so we don't erroneously get a circular dependency
// failure based on types.

View File

@ -80,6 +80,8 @@ export function compileComponent(type: Type<any>, metadata: Component): void {
wrapDirectivesInClosure: false,
styles: metadata.styles || [],
encapsulation: metadata.encapsulation || ViewEncapsulation.Emulated, animations,
viewProviders: metadata.viewProviders ? new WrappedNodeExpr(metadata.viewProviders) :
null
},
constantPool, makeBindingParser());
const preStatements = [...constantPool.statements, ...res.statements];
@ -180,6 +182,7 @@ function directiveMetadata(type: Type<any>, metadata: Directive): R3DirectiveMet
typeSourceSpan: null !,
usesInheritance: !extendsDirectlyFromObject(type),
exportAs: metadata.exportAs || null,
providers: metadata.providers ? new WrappedNodeExpr(metadata.providers) : null
};
}

View File

@ -7,7 +7,7 @@
*/
import {defineInjectable, defineInjector,} from '../../di/defs';
import {inject} from '../../di/injector';
import {inject} from '../../di/injector_compatibility';
import * as r3 from '../index';
import * as sanitization from '../../sanitization/sanitization';
@ -32,7 +32,7 @@ export const angularCoreEnv: {[name: string]: Function} = {
'ɵinjectAttribute': r3.injectAttribute,
'ɵtemplateRefExtractor': r3.templateRefExtractor,
'ɵNgOnChangesFeature': r3.NgOnChangesFeature,
'ɵPublicFeature': r3.PublicFeature,
'ɵProvidersFeature': r3.ProvidersFeature,
'ɵInheritDefinitionFeature': r3.InheritDefinitionFeature,
'ɵelementAttribute': r3.elementAttribute,
'ɵbind': r3.bind,

View File

@ -42,11 +42,7 @@ function reflectDependency(dep: any | any[]): R3DependencyMetadata {
};
function setTokenAndResolvedType(token: any): void {
if (token === Injector) {
meta.resolved = R3ResolvedDependencyType.Injector;
} else {
meta.resolved = R3ResolvedDependencyType.Token;
}
meta.resolved = R3ResolvedDependencyType.Token;
meta.token = new WrappedNodeExpr(token);
}

View File

@ -8,10 +8,11 @@
import {PipeTransform} from '../change_detection/pipe_transform';
import {getTView, load, store} from './instructions';
import {load, store} from './instructions';
import {PipeDef, PipeDefList} from './interfaces/definition';
import {HEADER_OFFSET} from './interfaces/view';
import {pureFunction1, pureFunction2, pureFunction3, pureFunction4, pureFunctionV} from './pure_function';
import {getTView} from './state';
/**
* Create a pipe.
@ -36,7 +37,7 @@ export function pipe(index: number, pipeName: string): any {
pipeDef = tView.data[adjustedIndex] as PipeDef<any>;
}
const pipeInstance = pipeDef.factory();
const pipeInstance = pipeDef.factory(null);
store(index, pipeInstance);
return pipeInstance;
}

View File

@ -0,0 +1,64 @@
/**
* @license
* Copyright Google Inc. 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 {global} from '../util';
import {getComponent, getDirectives, getHostComponent, getInjector, getRootComponents} from './discovery_utils';
/**
* This file introduces series of globally accessible debug tools
* to allow for the Angular debugging story to function.
*
* To see this in action run the following command:
*
* bazel run --define=compile=local
* //packages/core/test/bundling/todo:devserver
*
* Then load `localhost:5432` and start using the console tools.
*/
/**
* This value reflects the property on the window where the dev
* tools are patched (window.ngDev).
* */
export const GLOBAL_PUBLISH_EXPANDO_KEY = 'ngDev';
/*
* Publishes a collection of default debug tools onto `window._ng_`.
*
* These functions are available globally when Angular is in development
* mode and are automatically stripped away from prod mode is on.
*/
let _published = false;
export function publishDefaultGlobalUtils() {
if (!_published) {
_published = true;
publishGlobalUtil('getComponent', getComponent);
publishGlobalUtil('getHostComponent', getHostComponent);
publishGlobalUtil('getInjector', getInjector);
publishGlobalUtil('getRootComponents', getRootComponents);
publishGlobalUtil('getDirectives', getDirectives);
}
}
export declare type GlobalDevModeContainer = {
[GLOBAL_PUBLISH_EXPANDO_KEY]: {[fnName: string]: Function};
};
/**
* Publishes the given function to `window.ngDevMode` so that it can be
* used from the browser console when an application is not in production.
*/
export function publishGlobalUtil(name: string, fn: Function): void {
const w = global as any as GlobalDevModeContainer;
if (w) {
let container = w[GLOBAL_PUBLISH_EXPANDO_KEY];
if (!container) {
container = w[GLOBAL_PUBLISH_EXPANDO_KEY] = {};
}
container[name] = fn;
}
}

View File

@ -6,7 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {bindingUpdated, bindingUpdated2, bindingUpdated4, updateBinding, getBinding, getCreationMode, bindingUpdated3, getBindingRoot, getTView,} from './instructions';
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4, getBinding, updateBinding} from './instructions';
import {getBindingRoot, getCreationMode} from './state';
/**
* Bindings for pure functions are stored after regular bindings.

View File

@ -19,12 +19,13 @@ import {getSymbolIterator} from '../util';
import {assertDefined, assertEqual} from './assert';
import {NG_ELEMENT_ID} from './fields';
import {_getViewData, assertPreviousIsParent, getOrCreateCurrentQueries, store, storeCleanupWithContext} from './instructions';
import {store, storeCleanupWithContext} from './instructions';
import {DirectiveDef, unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
import {LQueries, unusedValueExportToPlacateAjd as unused4} from './interfaces/query';
import {LViewData, TVIEW} from './interfaces/view';
import {assertPreviousIsParent, getOrCreateCurrentQueries, getViewData} from './state';
import {flatten, isContentQueryHost} from './util';
import {createElementRef, createTemplateRef} from './view_engine_compatibility';
@ -259,7 +260,7 @@ function getIdxOfMatchingDirective(tNode: TNode, currentView: LViewData, type: T
const end = start + count;
for (let i = start; i < end; i++) {
const def = defs[i] as DirectiveDef<any>;
if (def.type === type && def.diPublic) {
if (def.type === type) {
return i;
}
}
@ -293,7 +294,7 @@ function queryReadByTNodeType(tNode: TNode, currentView: LViewData): any {
function add(
query: LQuery<any>| null, tNode: TElementNode | TContainerNode | TElementContainerNode) {
const currentView = _getViewData();
const currentView = getViewData();
while (query) {
const predicate = query.predicate;

View File

@ -0,0 +1,432 @@
/**
* @license
* Copyright Google Inc. 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 {Sanitizer} from '../sanitization/security';
import {assertDefined, assertEqual} from './assert';
import {executeHooks} from './hooks';
import {TElementNode, TNode, TNodeFlags, TViewNode} from './interfaces/node';
import {LQueries} from './interfaces/query';
import {Renderer3, RendererFactory3} from './interfaces/renderer';
import {BINDING_INDEX, CLEANUP, CONTEXT, DECLARATION_VIEW, FLAGS, HOST_NODE, LViewData, LViewFlags, OpaqueViewState, QUERIES, RENDERER, SANITIZER, TVIEW, TView} from './interfaces/view';
import {assertDataInRangeInternal, isContentQueryHost} from './util';
/**
* This property gets set before entering a template.
*
* This renderer can be one of two varieties of Renderer3:
*
* - ObjectedOrientedRenderer3
*
* This is the native browser API style, e.g. operations are methods on individual objects
* like HTMLElement. With this style, no additional code is needed as a facade (reducing payload
* size).
*
* - ProceduralRenderer3
*
* In non-native browser environments (e.g. platforms such as web-workers), this is the facade
* that enables element manipulation. This also facilitates backwards compatibility with
* Renderer2.
*/
let renderer: Renderer3;
export function getRenderer(): Renderer3 {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return renderer;
}
export function setRenderer(r: Renderer3): void {
renderer = r;
}
let rendererFactory: RendererFactory3;
export function getRendererFactory(): RendererFactory3 {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return rendererFactory;
}
export function setRendererFactory(factory: RendererFactory3): void {
rendererFactory = factory;
}
export function getCurrentSanitizer(): Sanitizer|null {
return viewData && viewData[SANITIZER];
}
/**
* Store the element depth count. This is used to identify the root elements of the template
* so that we can than attach `LViewData` to only those elements.
*/
let elementDepthCount !: number;
export function getElementDepthCount() {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return elementDepthCount;
}
export function increaseElementDepthCount() {
elementDepthCount++;
}
export function decreaseElementDepthCount() {
elementDepthCount--;
}
/**
* Stores whether directives should be matched to elements.
*
* When template contains `ngNonBindable` than we need to prevent the runtime form matching
* directives on children of that element.
*
* Example:
* ```
* <my-comp my-directive>
* Should match component / directive.
* </my-comp>
* <div ngNonBindable>
* <my-comp my-directive>
* Should not match component / directive because we are in ngNonBindable.
* </my-comp>
* </div>
* ```
*/
let bindingsEnabled !: boolean;
export function getBindingsEnabled(): boolean {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return bindingsEnabled;
}
/**
* Enables directive matching on elements.
*
* * Example:
* ```
* <my-comp my-directive>
* Should match component / directive.
* </my-comp>
* <div ngNonBindable>
* <!-- disabledBindings() -->
* <my-comp my-directive>
* Should not match component / directive because we are in ngNonBindable.
* </my-comp>
* <!-- enableBindings() -->
* </div>
* ```
*/
export function enableBindings(): void {
bindingsEnabled = true;
}
/**
* Disables directive matching on element.
*
* * Example:
* ```
* <my-comp my-directive>
* Should match component / directive.
* </my-comp>
* <div ngNonBindable>
* <!-- disabledBindings() -->
* <my-comp my-directive>
* Should not match component / directive because we are in ngNonBindable.
* </my-comp>
* <!-- enableBindings() -->
* </div>
* ```
*/
export function disableBindings(): void {
bindingsEnabled = false;
}
/**
* Returns the current OpaqueViewState instance.
*
* Used in conjunction with the restoreView() instruction to save a snapshot
* of the current view and restore it when listeners are invoked. This allows
* walking the declaration view tree in listeners to get vars from parent views.
*/
export function getCurrentView(): OpaqueViewState {
return viewData as any as OpaqueViewState;
}
/**
* Restores `contextViewData` to the given OpaqueViewState instance.
*
* Used in conjunction with the getCurrentView() instruction to save a snapshot
* of the current view and restore it when listeners are invoked. This allows
* walking the declaration view tree in listeners to get vars from parent views.
*
* @param viewToRestore The OpaqueViewState instance to restore.
*/
export function restoreView(viewToRestore: OpaqueViewState) {
contextViewData = viewToRestore as any as LViewData;
}
/** Used to set the parent property when nodes are created and track query results. */
let previousOrParentTNode: TNode;
export function getPreviousOrParentTNode(): TNode {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return previousOrParentTNode;
}
export function setPreviousOrParentTNode(tNode: TNode) {
previousOrParentTNode = tNode;
}
export function setTNodeAndViewData(tNode: TNode, view: LViewData) {
previousOrParentTNode = tNode;
viewData = view;
}
/**
* If `isParent` is:
* - `true`: then `previousOrParentTNode` points to a parent node.
* - `false`: then `previousOrParentTNode` points to previous node (sibling).
*/
let isParent: boolean;
export function getIsParent(): boolean {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return isParent;
}
export function setIsParent(value: boolean): void {
isParent = value;
}
let tView: TView;
export function getTView(): TView {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return tView;
}
let currentQueries: LQueries|null;
export function getCurrentQueries(): LQueries|null {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return currentQueries;
}
export function setCurrentQueries(queries: LQueries | null): void {
currentQueries = queries;
}
/**
* Query instructions can ask for "current queries" in 2 different cases:
* - when creating view queries (at the root of a component view, before any node is created - in
* this case currentQueries points to view queries)
* - when creating content queries (i.e. this previousOrParentTNode points to a node on which we
* create content queries).
*/
export function getOrCreateCurrentQueries(
QueryType: {new (parent: null, shallow: null, deep: null): LQueries}): LQueries {
// if this is the first content query on a node, any existing LQueries needs to be cloned
// in subsequent template passes, the cloning occurs before directive instantiation.
if (previousOrParentTNode && previousOrParentTNode !== viewData[HOST_NODE] &&
!isContentQueryHost(previousOrParentTNode)) {
currentQueries && (currentQueries = currentQueries.clone());
previousOrParentTNode.flags |= TNodeFlags.hasContentQuery;
}
return currentQueries || (currentQueries = new QueryType(null, null, null));
}
/**
* This property gets set before entering a template.
*/
let creationMode: boolean;
export function getCreationMode(): boolean {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return creationMode;
}
/**
* State of the current view being processed.
*
* An array of nodes (text, element, container, etc), pipes, their bindings, and
* any local variables that need to be stored between invocations.
*/
let viewData: LViewData;
/**
* Internal function that returns the current LViewData instance.
*
* The getCurrentView() instruction should be used for anything public.
*/
export function getViewData(): LViewData {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return viewData;
}
/**
* The last viewData retrieved by nextContext().
* Allows building nextContext() and reference() calls.
*
* e.g. const inner = x().$implicit; const outer = x().$implicit;
*/
let contextViewData: LViewData = null !;
export function getContextViewData(): LViewData {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return contextViewData;
}
export function getCleanup(view: LViewData): any[] {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return view[CLEANUP] || (view[CLEANUP] = []);
}
export function getTViewCleanup(view: LViewData): any[] {
return view[TVIEW].cleanup || (view[TVIEW].cleanup = []);
}
/**
* In this mode, any changes in bindings will throw an ExpressionChangedAfterChecked error.
*
* Necessary to support ChangeDetectorRef.checkNoChanges().
*/
let checkNoChangesMode = false;
export function getCheckNoChangesMode(): boolean {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return checkNoChangesMode;
}
export function setCheckNoChangesMode(mode: boolean): void {
checkNoChangesMode = mode;
}
/** Whether or not this is the first time the current view has been processed. */
let firstTemplatePass = true;
export function getFirstTemplatePass(): boolean {
return firstTemplatePass;
}
export function setFirstTemplatePass(value: boolean): void {
firstTemplatePass = value;
}
/**
* The root index from which pure function instructions should calculate their binding
* indices. In component views, this is TView.bindingStartIndex. In a host binding
* context, this is the TView.expandoStartIndex + any dirs/hostVars before the given dir.
*/
let bindingRootIndex: number = -1;
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
export function getBindingRoot() {
return bindingRootIndex;
}
export function setBindingRoot(value: number) {
bindingRootIndex = value;
}
/**
* Swap the current state with a new state.
*
* For performance reasons we store the state in the top level of the module.
* This way we minimize the number of properties to read. Whenever a new view
* is entered we have to store the state for later, and when the view is
* exited the state has to be restored
*
* @param newView New state to become active
* @param host Element to which the View is a child of
* @returns the previous state;
*/
export function enterView(
newView: LViewData, hostTNode: TElementNode | TViewNode | null): LViewData {
const oldView: LViewData = viewData;
tView = newView && newView[TVIEW];
creationMode = newView && (newView[FLAGS] & LViewFlags.CreationMode) === LViewFlags.CreationMode;
firstTemplatePass = newView && tView.firstTemplatePass;
bindingRootIndex = newView && tView.bindingStartIndex;
renderer = newView && newView[RENDERER];
previousOrParentTNode = hostTNode !;
isParent = true;
viewData = contextViewData = newView;
oldView && (oldView[QUERIES] = currentQueries);
currentQueries = newView && newView[QUERIES];
return oldView;
}
export function nextContextImpl<T = any>(level: number = 1): T {
contextViewData = walkUpViews(level, contextViewData !);
return contextViewData[CONTEXT] as T;
}
function walkUpViews(nestingLevel: number, currentView: LViewData): LViewData {
while (nestingLevel > 0) {
ngDevMode && assertDefined(
currentView[DECLARATION_VIEW],
'Declaration view should be defined if nesting level is greater than 0.');
currentView = currentView[DECLARATION_VIEW] !;
nestingLevel--;
}
return currentView;
}
/**
* Resets the application state.
*/
export function resetComponentState() {
isParent = false;
previousOrParentTNode = null !;
elementDepthCount = 0;
bindingsEnabled = true;
}
/**
* Used in lieu of enterView to make it clear when we are exiting a child view. This makes
* the direction of traversal (up or down the view tree) a bit clearer.
*
* @param newView New state to become active
* @param creationOnly An optional boolean to indicate that the view was processed in creation mode
* only, i.e. the first update will be done later. Only possible for dynamically created views.
*/
export function leaveView(newView: LViewData, creationOnly?: boolean): void {
if (!creationOnly) {
if (!checkNoChangesMode) {
executeHooks(viewData, tView.viewHooks, tView.viewCheckHooks, creationMode);
}
// Views are clean and in update mode after being checked, so these bits are cleared
viewData[FLAGS] &= ~(LViewFlags.CreationMode | LViewFlags.Dirty);
}
viewData[FLAGS] |= LViewFlags.RunInit;
viewData[BINDING_INDEX] = tView.bindingStartIndex;
enterView(newView, null);
}
export function assertPreviousIsParent() {
assertEqual(isParent, true, 'previousOrParentTNode should be a parent');
}
export function assertHasParent() {
assertDefined(previousOrParentTNode.parent, 'previousOrParentTNode should have a parent');
}
export function assertDataInRange(index: number, arr?: any[]) {
if (arr == null) arr = viewData;
assertDataInRangeInternal(index, arr || viewData);
}
export function assertDataNext(index: number, arr?: any[]) {
if (arr == null) arr = viewData;
assertEqual(
arr.length, index, `index ${index} expected to be at the end of arr (length ${arr.length})`);
}

View File

@ -11,6 +11,7 @@ import {BindingStore, BindingType, Player, PlayerBuilder, PlayerFactory, PlayerI
import {Renderer3, RendererStyleFlags3, isProceduralRenderer} from '../interfaces/renderer';
import {InitialStyles, StylingContext, StylingFlags, StylingIndex} from '../interfaces/styling';
import {LViewData, RootContext} from '../interfaces/view';
import {NO_CHANGE} from '../tokens';
import {getRootContext} from '../util';
import {BoundPlayerFactory} from './player_factory';
@ -45,7 +46,7 @@ const EMPTY_OBJ: {[key: string]: any} = {};
export function createStylingContextTemplate(
initialClassDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
initialStyleDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
styleSanitizer?: StyleSanitizeFn | null): StylingContext {
styleSanitizer?: StyleSanitizeFn | null, onlyProcessSingleClasses?: boolean): StylingContext {
const initialStylingValues: InitialStyles = [null];
const context: StylingContext =
createEmptyStylingContext(null, styleSanitizer, initialStylingValues);
@ -80,6 +81,7 @@ export function createStylingContextTemplate(
// make where the class offsets begin
context[StylingIndex.ClassOffsetPosition] = totalStyleDeclarations;
const initialStaticClasses: string[]|null = onlyProcessSingleClasses ? [] : null;
if (initialClassDeclarations) {
let hasPassedDeclarations = false;
for (let i = 0; i < initialClassDeclarations.length; i++) {
@ -93,6 +95,7 @@ export function createStylingContextTemplate(
const value = initialClassDeclarations[++i] as boolean;
initialStylingValues.push(value);
classesLookup[className] = initialStylingValues.length - 1;
initialStaticClasses && initialStaticClasses.push(className);
} else {
classesLookup[className] = 0;
}
@ -143,9 +146,15 @@ export function createStylingContextTemplate(
// there is no initial value flag for the master index since it doesn't
// reference an initial style value
setFlag(context, StylingIndex.MasterFlagPosition, pointers(0, 0, multiStart));
const masterFlag = pointers(0, 0, multiStart) |
(onlyProcessSingleClasses ? StylingFlags.OnlyProcessSingleClasses : 0);
setFlag(context, StylingIndex.MasterFlagPosition, masterFlag);
setContextDirty(context, initialStylingValues.length > 1);
if (initialStaticClasses) {
context[StylingIndex.PreviousOrCachedMultiClassValue] = initialStaticClasses.join(' ');
}
return context;
}
@ -164,8 +173,8 @@ export function createStylingContextTemplate(
*/
export function updateStylingMap(
context: StylingContext, classesInput: {[key: string]: any} | string |
BoundPlayerFactory<null|string|{[key: string]: any}>| null,
stylesInput?: {[key: string]: any} | BoundPlayerFactory<null|{[key: string]: any}>|
BoundPlayerFactory<null|string|{[key: string]: any}>| NO_CHANGE | null,
stylesInput?: {[key: string]: any} | BoundPlayerFactory<null|{[key: string]: any}>| NO_CHANGE |
null): void {
stylesInput = stylesInput || null;
@ -181,13 +190,14 @@ export function updateStylingMap(
(classesInput as BoundPlayerFactory<{[key: string]: any}|string>) !.value :
classesInput;
const stylesValue = stylesPlayerBuilder ? stylesInput !.value : stylesInput;
// early exit (this is what's done to avoid using ctx.bind() to cache the value)
const ignoreAllClassUpdates = classesValue === context[StylingIndex.PreviousMultiClassValue];
const ignoreAllStyleUpdates = stylesValue === context[StylingIndex.PreviousMultiStyleValue];
const ignoreAllClassUpdates = limitToSingleClasses(context) || classesValue === NO_CHANGE ||
classesValue === context[StylingIndex.PreviousOrCachedMultiClassValue];
const ignoreAllStyleUpdates =
stylesValue === NO_CHANGE || stylesValue === context[StylingIndex.PreviousMultiStyleValue];
if (ignoreAllClassUpdates && ignoreAllStyleUpdates) return;
context[StylingIndex.PreviousMultiClassValue] = classesValue;
context[StylingIndex.PreviousOrCachedMultiClassValue] = classesValue;
context[StylingIndex.PreviousMultiStyleValue] = stylesValue;
let classNames: string[] = EMPTY_ARR;
@ -478,6 +488,8 @@ export function renderStyleAndClassBindings(
const native = context[StylingIndex.ElementPosition] !;
const multiStartIndex = getMultiStartIndex(context);
const styleSanitizer = getStyleSanitizer(context);
const onlySingleClasses = limitToSingleClasses(context);
for (let i = StylingIndex.SingleStylesStartPosition; i < context.length;
i += StylingIndex.Size) {
// there is no point in rendering styles that have not changed on screen
@ -488,6 +500,7 @@ export function renderStyleAndClassBindings(
const playerBuilder = getPlayerBuilder(context, i);
const isClassBased = flag & StylingFlags.Class ? true : false;
const isInSingleRegion = i < multiStartIndex;
const readInitialValue = !isClassBased || !onlySingleClasses;
let valueToApply: string|boolean|null = value;
@ -506,7 +519,7 @@ export function renderStyleAndClassBindings(
// note that this should always be a falsy check since `false` is used
// for both class and style comparisons (styles can't be false and false
// classes are turned off and should therefore defer to their initial values)
if (!valueExists(valueToApply, isClassBased)) {
if (!valueExists(valueToApply, isClassBased) && readInitialValue) {
valueToApply = getInitialValue(context, flag);
}
@ -765,6 +778,10 @@ export function isContextDirty(context: StylingContext): boolean {
return isDirty(context, StylingIndex.MasterFlagPosition);
}
export function limitToSingleClasses(context: StylingContext) {
return context[StylingIndex.MasterFlagPosition] & StylingFlags.OnlyProcessSingleClasses;
}
export function setContextDirty(context: StylingContext, isDirtyYes: boolean): void {
setDirty(context, StylingIndex.MasterFlagPosition, isDirtyYes);
}

View File

@ -0,0 +1,15 @@
/**
* @license
* Copyright Google Inc. 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
*/
export interface NO_CHANGE {
// This is a brand that ensures that this type can never match anything else
brand: 'NO_CHANGE';
}
/** A special value which designates that a value has not changed. */
export const NO_CHANGE = {} as NO_CHANGE;

View File

@ -11,10 +11,13 @@ import {devModeEqual} from '../change_detection/change_detection_util';
import {assertDefined, assertLessThan} from './assert';
import {ACTIVE_INDEX, LContainer} from './interfaces/container';
import {LContext, MONKEY_PATCH_KEY_NAME} from './interfaces/context';
import {TNode, TNodeFlags} from './interfaces/node';
import {ComponentDef, DirectiveDef} from './interfaces/definition';
import {NO_PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags} from './interfaces/injector';
import {TContainerNode, TElementNode, TNode, TNodeFlags} from './interfaces/node';
import {RComment, RElement, RText} from './interfaces/renderer';
import {StylingContext} from './interfaces/styling';
import {CONTEXT, FLAGS, HEADER_OFFSET, HOST, LViewData, LViewFlags, PARENT, RootContext, TData, TVIEW} from './interfaces/view';
import {CONTEXT, DECLARATION_VIEW, FLAGS, HEADER_OFFSET, HOST, HOST_NODE, LViewData, LViewFlags, PARENT, RootContext, TData, TVIEW} from './interfaces/view';
/**
@ -122,6 +125,10 @@ export function isComponent(tNode: TNode): boolean {
return (tNode.flags & TNodeFlags.isComponent) === TNodeFlags.isComponent;
}
export function isComponentDef<T>(def: DirectiveDef<T>): def is ComponentDef<T> {
return (def as ComponentDef<T>).template !== null;
}
export function isLContainer(value: RElement | RComment | LContainer | StylingContext): boolean {
// Styling contexts are also arrays, but their first index contains an element node
return Array.isArray(value) && typeof value[ACTIVE_INDEX] === 'number';
@ -161,3 +168,72 @@ export function readPatchedLViewData(target: any): LViewData|null {
}
return null;
}
export function hasParentInjector(parentLocation: RelativeInjectorLocation): boolean {
return parentLocation !== NO_PARENT_INJECTOR;
}
export function getParentInjectorIndex(parentLocation: RelativeInjectorLocation): number {
return (parentLocation as any as number) & RelativeInjectorLocationFlags.InjectorIndexMask;
}
export function getParentInjectorViewOffset(parentLocation: RelativeInjectorLocation): number {
return (parentLocation as any as number) >> RelativeInjectorLocationFlags.ViewOffsetShift;
}
/**
* Unwraps a parent injector location number to find the view offset from the current injector,
* then walks up the declaration view tree until the view is found that contains the parent
* injector.
*
* @param location The location of the parent injector, which contains the view offset
* @param startView The LViewData instance from which to start walking up the view tree
* @returns The LViewData instance that contains the parent injector
*/
export function getParentInjectorView(
location: RelativeInjectorLocation, startView: LViewData): LViewData {
let viewOffset = getParentInjectorViewOffset(location);
let parentView = startView;
// For most cases, the parent injector can be found on the host node (e.g. for component
// or container), but we must keep the loop here to support the rarer case of deeply nested
// <ng-template> tags or inline views, where the parent injector might live many views
// above the child injector.
while (viewOffset > 0) {
parentView = parentView[DECLARATION_VIEW] !;
viewOffset--;
}
return parentView;
}
/**
* Unwraps a parent injector location number to find the view offset from the current injector,
* then walks up the declaration view tree until the TNode of the parent injector is found.
*
* @param location The location of the parent injector, which contains the view offset
* @param startView The LViewData instance from which to start walking up the view tree
* @param startTNode The TNode instance of the starting element
* @returns The TNode of the parent injector
*/
export function getParentInjectorTNode(
location: RelativeInjectorLocation, startView: LViewData, startTNode: TNode): TElementNode|
TContainerNode|null {
if (startTNode.parent && startTNode.parent.injectorIndex !== -1) {
// view offset is 0
const injectorIndex = startTNode.parent.injectorIndex;
let parentTNode = startTNode.parent;
while (parentTNode.parent != null && injectorIndex == parentTNode.injectorIndex) {
parentTNode = parentTNode.parent;
}
return parentTNode;
}
let viewOffset = getParentInjectorViewOffset(location);
let parentView = startView;
let parentTNode = startView[HOST_NODE] as TElementNode;
while (viewOffset > 0) {
parentView = parentView[DECLARATION_VIEW] !;
parentTNode = parentView[HOST_NODE] as TElementNode;
viewOffset--;
}
return parentTNode;
}

View File

@ -17,18 +17,18 @@ import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, ViewRef as viewEngine_Vie
import {Renderer2} from '../render/api';
import {assertDefined, assertGreaterThan, assertLessThan} from './assert';
import {NodeInjector, getParentInjectorLocation, getParentInjectorView} from './di';
import {_getViewData, addToViewTree, createEmbeddedViewAndNode, createLContainer, getPreviousOrParentTNode, getRenderer, renderEmbeddedTemplate} from './instructions';
import {ACTIVE_INDEX, LContainer, NATIVE, RENDER_PARENT, VIEWS} from './interfaces/container';
import {getOrCreateInjectable, getParentInjectorLocation} from './di';
import {addToViewTree, createEmbeddedViewAndNode, createLContainer, renderEmbeddedTemplate} from './instructions';
import {ACTIVE_INDEX, LContainer, NATIVE, VIEWS} from './interfaces/container';
import {RenderFlags} from './interfaces/definition';
import {InjectorLocationFlags} from './interfaces/injector';
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode} from './interfaces/node';
import {LQueries} from './interfaces/query';
import {RComment, RElement, Renderer3, isProceduralRenderer} from './interfaces/renderer';
import {CONTEXT, HOST_NODE, LViewData, QUERIES, RENDERER, TVIEW, TView} from './interfaces/view';
import {CONTEXT, HOST_NODE, LViewData, QUERIES, RENDERER, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {addRemoveViewFromContainer, appendChild, detachView, findComponentView, getBeforeNodeForView, getRenderParent, insertView, removeView} from './node_manipulation';
import {getComponentViewByIndex, getNativeByTNode, isComponent, isLContainer} from './util';
import {getPreviousOrParentTNode, getRenderer, getViewData} from './state';
import {getComponentViewByIndex, getNativeByTNode, getParentInjectorTNode, getParentInjectorView, hasParentInjector, isComponent, isLContainer} from './util';
import {ViewRef} from './view_ref';
@ -40,7 +40,7 @@ import {ViewRef} from './view_ref';
*/
export function injectElementRef(ElementRefToken: typeof ViewEngine_ElementRef):
ViewEngine_ElementRef {
return createElementRef(ElementRefToken, getPreviousOrParentTNode(), _getViewData());
return createElementRef(ElementRefToken, getPreviousOrParentTNode(), getViewData());
}
let R3ElementRef: {new (native: RElement | RComment): ViewEngine_ElementRef};
@ -79,7 +79,7 @@ export function injectTemplateRef<T>(
TemplateRefToken: typeof ViewEngine_TemplateRef,
ElementRefToken: typeof ViewEngine_ElementRef): ViewEngine_TemplateRef<T> {
return createTemplateRef<T>(
TemplateRefToken, ElementRefToken, getPreviousOrParentTNode(), _getViewData());
TemplateRefToken, ElementRefToken, getPreviousOrParentTNode(), getViewData());
}
/**
@ -147,9 +147,18 @@ export function injectViewContainerRef(
ElementRefToken: typeof ViewEngine_ElementRef): ViewEngine_ViewContainerRef {
const previousTNode =
getPreviousOrParentTNode() as TElementNode | TElementContainerNode | TContainerNode;
return createContainerRef(ViewContainerRefToken, ElementRefToken, previousTNode, _getViewData());
return createContainerRef(ViewContainerRefToken, ElementRefToken, previousTNode, getViewData());
}
export class NodeInjector implements Injector {
constructor(
private _tNode: TElementNode|TContainerNode|TElementContainerNode,
private _hostView: LViewData) {}
get(token: any, notFoundValue?: any): any {
return getOrCreateInjectable(this._tNode, this._hostView, token, notFoundValue);
}
}
/**
* Creates a ViewContainerRef and stores it on the injector.
@ -187,11 +196,11 @@ export function createContainerRef(
get parentInjector(): Injector {
const parentLocation = getParentInjectorLocation(this._hostTNode, this._hostView);
const parentView = getParentInjectorView(parentLocation, this._hostView);
const parentIndex = parentLocation & InjectorLocationFlags.InjectorIndexMask;
const parentTNode = parentView[TVIEW].data[parentIndex] as TElementNode | TContainerNode;
const parentTNode = getParentInjectorTNode(parentLocation, this._hostView, this._hostTNode);
return parentLocation === -1 ? new NullInjector() :
new NodeInjector(parentTNode, parentView);
return !hasParentInjector(parentLocation) || parentTNode == null ?
new NullInjector() :
new NodeInjector(parentTNode, parentView);
}
clear(): void {
@ -310,7 +319,7 @@ export function createContainerRef(
/** Returns a ChangeDetectorRef (a.k.a. a ViewRef) */
export function injectChangeDetectorRef(): ViewEngine_ChangeDetectorRef {
return createViewRef(getPreviousOrParentTNode(), _getViewData(), null);
return createViewRef(getPreviousOrParentTNode(), getViewData(), null);
}
/**
@ -345,5 +354,5 @@ function getOrCreateRenderer2(view: LViewData): Renderer2 {
/** Returns a Renderer2 (or throws when application was bootstrapped with Renderer3) */
export function injectRenderer2(): Renderer2 {
return getOrCreateRenderer2(_getViewData());
return getOrCreateRenderer2(getViewData());
}

View File

@ -11,10 +11,11 @@ import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detec
import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref';
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
import {checkNoChanges, checkNoChangesInRootView, detectChanges, detectChangesInRootView, getRendererFactory, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
import {checkNoChanges, checkNoChangesInRootView, detectChanges, detectChangesInRootView, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
import {TViewNode} from './interfaces/node';
import {FLAGS, LViewData, LViewFlags, PARENT} from './interfaces/view';
import {destroyLView} from './node_manipulation';
import {getRendererFactory} from './state';
// Needed due to tsickle downleveling where multiple `implements` with classes creates

View File

@ -98,16 +98,17 @@ class SanitizingHtmlSerializer {
// However this code never accesses properties off of `document` before deleting its contents
// again, so it shouldn't be vulnerable to DOM clobbering.
let current: Node = el.firstChild !;
let elementValid = true;
while (current) {
if (current.nodeType === Node.ELEMENT_NODE) {
this.startElement(current as Element);
elementValid = this.startElement(current as Element);
} else if (current.nodeType === Node.TEXT_NODE) {
this.chars(current.nodeValue !);
} else {
// Strip non-element, non-text nodes.
this.sanitizedSomething = true;
}
if (current.firstChild) {
if (elementValid && current.firstChild) {
current = current.firstChild !;
continue;
}
@ -130,11 +131,19 @@ class SanitizingHtmlSerializer {
return this.buf.join('');
}
private startElement(element: Element) {
/**
* Outputs only valid Elements.
*
* Invalid elements are skipped.
*
* @param element element to sanitize
* Returns true if the element is valid.
*/
private startElement(element: Element): boolean {
const tagName = element.nodeName.toLowerCase();
if (!VALID_ELEMENTS.hasOwnProperty(tagName)) {
this.sanitizedSomething = true;
return;
return false;
}
this.buf.push('<');
this.buf.push(tagName);
@ -154,6 +163,7 @@ class SanitizingHtmlSerializer {
this.buf.push(' ', attrName, '="', encodeEntities(value), '"');
}
this.buf.push('>');
return true;
}
private endElement(current: Element) {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {getCurrentSanitizer} from '../render3/instructions';
import {getCurrentSanitizer} from '../render3/state';
import {stringify} from '../render3/util';
import {BypassType, allowSanitizationBypass} from './bypass';

View File

@ -8,7 +8,8 @@
import {InjectableDef, getInjectableDef} from '../di/defs';
import {resolveForwardRef} from '../di/forward_ref';
import {INJECTOR, InjectFlags, Injector, setCurrentInjector} from '../di/injector';
import {INJECTOR, Injector} from '../di/injector';
import {setCurrentInjector} from '../di/injector_compatibility';
import {APP_ROOT} from '../di/scope';
import {NgModuleRef} from '../linker/ng_module_factory';
import {stringify} from '../util';

View File

@ -8,7 +8,8 @@
import {ApplicationRef} from '../application_ref';
import {ChangeDetectorRef} from '../change_detection/change_detection';
import {InjectFlags, Injector} from '../di/injector';
import {Injector} from '../di/injector';
import {InjectFlags} from '../di/injector_compatibility';
import {ComponentFactory, ComponentRef} from '../linker/component_factory';
import {ComponentFactoryBoundToModule, ComponentFactoryResolver} from '../linker/component_factory_resolver';
import {ElementRef} from '../linker/element_ref';

View File

@ -17,9 +17,6 @@
{
"name": "BoundPlayerFactory"
},
{
"name": "CIRCULAR$1"
},
{
"name": "CLEANUP"
},
@ -51,7 +48,7 @@
"name": "DefaultIterableDifferFactory"
},
{
"name": "EMPTY$1"
"name": "EMPTY"
},
{
"name": "EMPTY_ARR"
@ -71,6 +68,9 @@
{
"name": "FLAGS"
},
{
"name": "FactoryPrototype"
},
{
"name": "HEADER_OFFSET"
},
@ -81,7 +81,7 @@
"name": "HOST_NODE"
},
{
"name": "INJECTOR$1"
"name": "INJECTOR"
},
{
"name": "INJECTOR_SIZE"
@ -119,9 +119,15 @@
{
"name": "NG_PROJECT_AS_ATTR_NAME"
},
{
"name": "NOT_FOUND"
},
{
"name": "NO_CHANGE"
},
{
"name": "NO_PARENT_INJECTOR"
},
{
"name": "NgForOf"
},
@ -132,7 +138,10 @@
"name": "NgModuleRef"
},
{
"name": "NodeInjector"
"name": "NodeInjector$1"
},
{
"name": "NodeInjectorFactory"
},
{
"name": "NullInjector"
@ -152,9 +161,6 @@
{
"name": "PARENT_INJECTOR"
},
{
"name": "PublicFeature"
},
{
"name": "QUERIES"
},
@ -263,18 +269,12 @@
{
"name": "_c4"
},
{
"name": "_currentInjector"
},
{
"name": "_currentNamespace"
},
{
"name": "_devMode"
},
{
"name": "_getViewData"
},
{
"name": "_global"
},
@ -315,7 +315,7 @@
"name": "attachPatchData"
},
{
"name": "baseDirectiveCreate"
"name": "baseResolveDirective"
},
{
"name": "bind"
@ -323,24 +323,21 @@
{
"name": "bindPlayerFactory"
},
{
"name": "bindingRootIndex"
},
{
"name": "bindingUpdated"
},
{
"name": "bloomAdd"
},
{
"name": "bloomHasToken"
},
{
"name": "bloomHashBitOrFactory"
},
{
"name": "buildAnimationPlayer"
},
{
"name": "cacheMatchingDirectivesForNode"
},
{
"name": "cacheMatchingLocalNames"
},
@ -437,6 +434,9 @@
{
"name": "createViewQuery"
},
{
"name": "decreaseElementDepthCount"
},
{
"name": "defineComponent"
},
@ -446,6 +446,9 @@
{
"name": "defineInjectable"
},
{
"name": "delegateToClassInput"
},
{
"name": "destroyLView"
},
@ -461,15 +464,9 @@
{
"name": "detectChangesInternal"
},
{
"name": "diPublic"
},
{
"name": "diPublicInInjector"
},
{
"name": "directiveCreate"
},
{
"name": "directiveInject"
},
@ -555,7 +552,7 @@
"name": "firstTemplatePass"
},
{
"name": "generateExpandoBlock"
"name": "generateExpandoInstructionBlock"
},
{
"name": "generateInitialInputs"
@ -566,6 +563,12 @@
{
"name": "getBeforeNodeForView"
},
{
"name": "getBindingsEnabled"
},
{
"name": "getCheckNoChangesMode"
},
{
"name": "getCleanup"
},
@ -587,6 +590,12 @@
{
"name": "getContext"
},
{
"name": "getCreationMode"
},
{
"name": "getCurrentQueries"
},
{
"name": "getCurrentSanitizer"
},
@ -599,6 +608,12 @@
{
"name": "getDirectiveStartIndex"
},
{
"name": "getElementDepthCount"
},
{
"name": "getFirstTemplatePass"
},
{
"name": "getHighestElementContainer"
},
@ -617,6 +632,9 @@
{
"name": "getInjectorIndex"
},
{
"name": "getIsParent"
},
{
"name": "getLContainer"
},
@ -636,10 +654,10 @@
"name": "getNativeByTNode"
},
{
"name": "getOrCreateInjectable"
"name": "getNodeInjectable"
},
{
"name": "getOrCreateNodeInjector"
"name": "getOrCreateInjectable"
},
{
"name": "getOrCreateNodeInjectorForNode"
@ -650,12 +668,21 @@
{
"name": "getOrCreateTView"
},
{
"name": "getParentInjectorIndex"
},
{
"name": "getParentInjectorLocation"
},
{
"name": "getParentInjectorTNode"
},
{
"name": "getParentInjectorView"
},
{
"name": "getParentInjectorViewOffset"
},
{
"name": "getParentNative"
},
@ -702,7 +729,7 @@
"name": "getRootContext"
},
{
"name": "getRootContext$2"
"name": "getRootContext$1"
},
{
"name": "getRootView"
@ -722,6 +749,9 @@
{
"name": "getTNode"
},
{
"name": "getTView"
},
{
"name": "getTViewCleanup"
},
@ -734,6 +764,12 @@
{
"name": "getValue"
},
{
"name": "getViewData"
},
{
"name": "hasParentInjector"
},
{
"name": "hasPlayerBuilderChanged"
},
@ -741,11 +777,23 @@
"name": "hasValueChanged"
},
{
"name": "inject"
"name": "includeViewProviders"
},
{
"name": "increaseElementDepthCount"
},
{
"name": "initNodeFlags"
},
{
"name": "initializeTNodeInputs"
},
{
"name": "injectElementRef"
},
{
"name": "injectRootLimpMode"
},
{
"name": "injectTemplateRef"
},
@ -753,7 +801,7 @@
"name": "injectViewContainerRef"
},
{
"name": "injectorHasToken"
"name": "insertBloom"
},
{
"name": "insertNewMultiProperty"
@ -762,7 +810,10 @@
"name": "insertView"
},
{
"name": "instantiateDirectivesDirectly"
"name": "instantiateAllDirectives"
},
{
"name": "instantiateRootComponent"
},
{
"name": "interpolation1"
@ -777,10 +828,10 @@
"name": "isComponent"
},
{
"name": "isComponentInstance"
"name": "isComponentDef"
},
{
"name": "isContentQueryHost"
"name": "isComponentInstance"
},
{
"name": "isContextDirty"
@ -800,6 +851,9 @@
{
"name": "isDirty"
},
{
"name": "isFactory"
},
{
"name": "isJsObject"
},
@ -833,6 +887,9 @@
{
"name": "leaveView"
},
{
"name": "limitToSingleClasses"
},
{
"name": "listener"
},
@ -869,6 +926,9 @@
{
"name": "nextContext"
},
{
"name": "nextContextImpl"
},
{
"name": "nextNgElementId"
},
@ -878,6 +938,12 @@
{
"name": "pointers"
},
{
"name": "postProcessBaseDirective"
},
{
"name": "postProcessDirective"
},
{
"name": "prefillHostVars"
},
@ -951,7 +1017,7 @@
"name": "resetComponentState"
},
{
"name": "resolveDirective"
"name": "resolveDirectives"
},
{
"name": "saveNameToExportMap"
@ -963,14 +1029,17 @@
"name": "scheduleTick"
},
{
"name": "searchDirectivesOnInjector"
},
{
"name": "searchMatchesQueuedForCreation"
"name": "searchTokensOnInjector"
},
{
"name": "setAnimationPlayState"
},
{
"name": "setBindingRoot"
},
{
"name": "setCheckNoChangesMode"
},
{
"name": "setClass"
},
@ -981,13 +1050,13 @@
"name": "setContextPlayersDirty"
},
{
"name": "setCurrentInjector"
"name": "setCurrentQueries"
},
{
"name": "setDirty"
},
{
"name": "setEnvironment"
"name": "setFirstTemplatePass"
},
{
"name": "setFlag"
@ -995,35 +1064,50 @@
{
"name": "setHostBindings"
},
{
"name": "setIncludeViewProviders"
},
{
"name": "setInjectImplementation"
},
{
"name": "setInputsForProperty"
},
{
"name": "setInputsFromAttrs"
},
{
"name": "setIsParent"
},
{
"name": "setPlayerBuilder"
},
{
"name": "setPlayerBuilderIndex"
},
{
"name": "setPreviousOrParentTNode"
},
{
"name": "setProp"
},
{
"name": "setRendererFactory"
},
{
"name": "setStyle"
},
{
"name": "setUpAttributes"
"name": "setTNodeAndViewData"
},
{
"name": "setUpBloom"
"name": "setUpAttributes"
},
{
"name": "setValue"
},
{
"name": "shouldNotSearchParent"
"name": "shouldSearchParent"
},
{
"name": "storeCleanupFn"
@ -1052,9 +1136,6 @@
{
"name": "textBinding"
},
{
"name": "throwCyclicDependencyError"
},
{
"name": "throwErrorIfNoChangesMode"
},

View File

@ -24,7 +24,7 @@
"name": "DECLARATION_VIEW"
},
{
"name": "EMPTY$1"
"name": "EMPTY"
},
{
"name": "EMPTY_ARRAY$1"
@ -35,6 +35,9 @@
{
"name": "FLAGS"
},
{
"name": "FactoryPrototype"
},
{
"name": "HEADER_OFFSET"
},
@ -45,7 +48,7 @@
"name": "HOST_NODE"
},
{
"name": "INJECTOR$1"
"name": "INJECTOR"
},
{
"name": "INJECTOR_SIZE"
@ -77,6 +80,12 @@
{
"name": "NO_CHANGE"
},
{
"name": "NO_PARENT_INJECTOR"
},
{
"name": "NodeInjectorFactory"
},
{
"name": "ObjectUnsubscribedErrorImpl"
},
@ -86,9 +95,6 @@
{
"name": "PARENT_INJECTOR"
},
{
"name": "PublicFeature"
},
{
"name": "QUERIES"
},
@ -116,9 +122,6 @@
{
"name": "ViewEncapsulation$1"
},
{
"name": "_getViewData"
},
{
"name": "_renderCompCount"
},
@ -129,10 +132,7 @@
"name": "attachPatchData"
},
{
"name": "baseDirectiveCreate"
},
{
"name": "bindingRootIndex"
"name": "baseResolveDirective"
},
{
"name": "bloomAdd"
@ -191,9 +191,6 @@
{
"name": "detectChangesInternal"
},
{
"name": "diPublic"
},
{
"name": "diPublicInInjector"
},
@ -224,6 +221,9 @@
{
"name": "getBeforeNodeForView"
},
{
"name": "getCheckNoChangesMode"
},
{
"name": "getClosureSafeProperty"
},
@ -236,9 +236,15 @@
{
"name": "getContainerRenderParent"
},
{
"name": "getCreationMode"
},
{
"name": "getDirectiveDef"
},
{
"name": "getFirstTemplatePass"
},
{
"name": "getHighestElementContainer"
},
@ -248,6 +254,9 @@
{
"name": "getInjectorIndex"
},
{
"name": "getIsParent"
},
{
"name": "getLContainer"
},
@ -258,7 +267,7 @@
"name": "getNativeByTNode"
},
{
"name": "getOrCreateNodeInjector"
"name": "getNodeInjectable"
},
{
"name": "getOrCreateNodeInjectorForNode"
@ -266,12 +275,18 @@
{
"name": "getOrCreateTView"
},
{
"name": "getParentInjectorIndex"
},
{
"name": "getParentInjectorLocation"
},
{
"name": "getParentInjectorView"
},
{
"name": "getParentInjectorViewOffset"
},
{
"name": "getParentNative"
},
@ -287,9 +302,42 @@
{
"name": "getRenderParent"
},
{
"name": "getRenderer"
},
{
"name": "getRendererFactory"
},
{
"name": "getTView"
},
{
"name": "getViewData"
},
{
"name": "hasParentInjector"
},
{
"name": "includeViewProviders"
},
{
"name": "initNodeFlags"
},
{
"name": "insertBloom"
},
{
"name": "instantiateRootComponent"
},
{
"name": "invertObject"
},
{
"name": "isComponentDef"
},
{
"name": "isFactory"
},
{
"name": "isProceduralRenderer"
},
@ -311,6 +359,9 @@
{
"name": "noSideEffects"
},
{
"name": "postProcessBaseDirective"
},
{
"name": "prefillHostVars"
},
@ -350,14 +401,35 @@
{
"name": "resetComponentState"
},
{
"name": "setBindingRoot"
},
{
"name": "setFirstTemplatePass"
},
{
"name": "setHostBindings"
},
{
"name": "setUpAttributes"
"name": "setIncludeViewProviders"
},
{
"name": "setUpBloom"
"name": "setInjectImplementation"
},
{
"name": "setIsParent"
},
{
"name": "setPreviousOrParentTNode"
},
{
"name": "setRendererFactory"
},
{
"name": "setTNodeAndViewData"
},
{
"name": "setUpAttributes"
},
{
"name": "stringify$2"

View File

@ -570,7 +570,7 @@
"name": "DomElementSchemaRegistry"
},
{
"name": "EMPTY"
"name": "EMPTY$1"
},
{
"name": "EMPTY_ARRAY$4"
@ -780,7 +780,7 @@
"name": "INHERITED_CLASS_WITH_CTOR"
},
{
"name": "INJECTOR"
"name": "INJECTOR$1"
},
{
"name": "INJECTORRefTokenKey"
@ -1343,6 +1343,9 @@
{
"name": "SWITCH_ELEMENT_REF_FACTORY"
},
{
"name": "SWITCH_INJECTOR_FACTORY"
},
{
"name": "SWITCH_RENDERER2_FACTORY"
},
@ -1967,6 +1970,9 @@
{
"name": "__extends$p"
},
{
"name": "__forward_ref__"
},
{
"name": "__metadata"
},
@ -3098,6 +3104,12 @@
{
"name": "injectArgs"
},
{
"name": "injectInjectorOnly"
},
{
"name": "injectRootLimpMode"
},
{
"name": "inlineInterpolate"
},

View File

@ -3,7 +3,7 @@
"name": "APP_ROOT"
},
{
"name": "CIRCULAR$2"
"name": "CIRCULAR$1"
},
{
"name": "EMPTY_ARRAY$2"
@ -12,7 +12,7 @@
"name": "EmptyErrorImpl"
},
{
"name": "INJECTOR"
"name": "INJECTOR$1"
},
{
"name": "Inject"
@ -68,6 +68,9 @@
{
"name": "_THROW_IF_NOT_FOUND"
},
{
"name": "__forward_ref__"
},
{
"name": "__read"
},
@ -120,7 +123,13 @@
"name": "injectArgs"
},
{
"name": "injectableDefRecord"
"name": "injectInjectorOnly"
},
{
"name": "injectRootLimpMode"
},
{
"name": "injectableDefFactory"
},
{
"name": "isExistingProvider"
@ -143,6 +152,9 @@
{
"name": "makeRecord"
},
{
"name": "providerToFactory"
},
{
"name": "providerToRecord"
},

View File

@ -11,9 +11,6 @@
{
"name": "BoundPlayerFactory"
},
{
"name": "CIRCULAR$1"
},
{
"name": "CLEANUP"
},
@ -45,7 +42,7 @@
"name": "DefaultIterableDifferFactory"
},
{
"name": "EMPTY$1"
"name": "EMPTY"
},
{
"name": "EMPTY_ARRAY$1"
@ -59,6 +56,9 @@
{
"name": "FLAGS"
},
{
"name": "FactoryPrototype"
},
{
"name": "HEADER_OFFSET"
},
@ -69,7 +69,7 @@
"name": "HOST_NODE"
},
{
"name": "INJECTOR$1"
"name": "INJECTOR"
},
{
"name": "INJECTOR_SIZE"
@ -107,9 +107,15 @@
{
"name": "NG_PROJECT_AS_ATTR_NAME"
},
{
"name": "NOT_FOUND"
},
{
"name": "NO_CHANGE"
},
{
"name": "NO_PARENT_INJECTOR"
},
{
"name": "NgForOf"
},
@ -126,7 +132,10 @@
"name": "NgModuleRef"
},
{
"name": "NodeInjector"
"name": "NodeInjector$1"
},
{
"name": "NodeInjectorFactory"
},
{
"name": "NullInjector"
@ -146,9 +155,6 @@
{
"name": "PARENT_INJECTOR"
},
{
"name": "PublicFeature"
},
{
"name": "QUERIES"
},
@ -329,18 +335,12 @@
{
"name": "_c9"
},
{
"name": "_currentInjector"
},
{
"name": "_currentNamespace"
},
{
"name": "_devMode"
},
{
"name": "_getViewData"
},
{
"name": "_global"
},
@ -378,14 +378,11 @@
"name": "attachPatchData"
},
{
"name": "baseDirectiveCreate"
"name": "baseResolveDirective"
},
{
"name": "bind"
},
{
"name": "bindingRootIndex"
},
{
"name": "bindingUpdated"
},
@ -393,10 +390,10 @@
"name": "bloomAdd"
},
{
"name": "bloomHashBitOrFactory"
"name": "bloomHasToken"
},
{
"name": "cacheMatchingDirectivesForNode"
"name": "bloomHashBitOrFactory"
},
{
"name": "cacheMatchingLocalNames"
@ -494,6 +491,9 @@
{
"name": "createViewQuery"
},
{
"name": "decreaseElementDepthCount"
},
{
"name": "defineComponent"
},
@ -503,6 +503,9 @@
{
"name": "defineInjectable"
},
{
"name": "delegateToClassInput"
},
{
"name": "destroyLView"
},
@ -518,15 +521,9 @@
{
"name": "detectChangesInternal"
},
{
"name": "diPublic"
},
{
"name": "diPublicInInjector"
},
{
"name": "directiveCreate"
},
{
"name": "directiveInject"
},
@ -600,7 +597,7 @@
"name": "firstTemplatePass"
},
{
"name": "generateExpandoBlock"
"name": "generateExpandoInstructionBlock"
},
{
"name": "generateInitialInputs"
@ -611,6 +608,12 @@
{
"name": "getBeforeNodeForView"
},
{
"name": "getBindingsEnabled"
},
{
"name": "getCheckNoChangesMode"
},
{
"name": "getCleanup"
},
@ -629,6 +632,15 @@
{
"name": "getContainerRenderParent"
},
{
"name": "getContextViewData"
},
{
"name": "getCreationMode"
},
{
"name": "getCurrentQueries"
},
{
"name": "getCurrentSanitizer"
},
@ -638,6 +650,12 @@
{
"name": "getDirectiveDef"
},
{
"name": "getElementDepthCount"
},
{
"name": "getFirstTemplatePass"
},
{
"name": "getHighestElementContainer"
},
@ -656,6 +674,9 @@
{
"name": "getInjectorIndex"
},
{
"name": "getIsParent"
},
{
"name": "getLContainer"
},
@ -675,10 +696,10 @@
"name": "getNativeByTNode"
},
{
"name": "getOrCreateInjectable"
"name": "getNodeInjectable"
},
{
"name": "getOrCreateNodeInjector"
"name": "getOrCreateInjectable"
},
{
"name": "getOrCreateNodeInjectorForNode"
@ -686,12 +707,21 @@
{
"name": "getOrCreateTView"
},
{
"name": "getParentInjectorIndex"
},
{
"name": "getParentInjectorLocation"
},
{
"name": "getParentInjectorTNode"
},
{
"name": "getParentInjectorView"
},
{
"name": "getParentInjectorViewOffset"
},
{
"name": "getParentNative"
},
@ -752,6 +782,9 @@
{
"name": "getTNode"
},
{
"name": "getTView"
},
{
"name": "getTViewCleanup"
},
@ -764,6 +797,12 @@
{
"name": "getValue"
},
{
"name": "getViewData"
},
{
"name": "hasParentInjector"
},
{
"name": "hasPlayerBuilderChanged"
},
@ -771,11 +810,23 @@
"name": "hasValueChanged"
},
{
"name": "inject"
"name": "includeViewProviders"
},
{
"name": "increaseElementDepthCount"
},
{
"name": "initNodeFlags"
},
{
"name": "initializeTNodeInputs"
},
{
"name": "injectElementRef"
},
{
"name": "injectRootLimpMode"
},
{
"name": "injectTemplateRef"
},
@ -783,13 +834,16 @@
"name": "injectViewContainerRef"
},
{
"name": "injectorHasToken"
"name": "insertBloom"
},
{
"name": "insertView"
},
{
"name": "instantiateDirectivesDirectly"
"name": "instantiateAllDirectives"
},
{
"name": "instantiateRootComponent"
},
{
"name": "interpolation1"
@ -801,7 +855,7 @@
"name": "isComponent"
},
{
"name": "isContentQueryHost"
"name": "isComponentDef"
},
{
"name": "isContextDirty"
@ -818,6 +872,9 @@
{
"name": "isDirty"
},
{
"name": "isFactory"
},
{
"name": "isJsObject"
},
@ -848,6 +905,9 @@
{
"name": "leaveView"
},
{
"name": "limitToSingleClasses"
},
{
"name": "listener"
},
@ -884,6 +944,9 @@
{
"name": "nextContext"
},
{
"name": "nextContextImpl"
},
{
"name": "nextNgElementId"
},
@ -893,6 +956,12 @@
{
"name": "pointers"
},
{
"name": "postProcessBaseDirective"
},
{
"name": "postProcessDirective"
},
{
"name": "prefillHostVars"
},
@ -969,7 +1038,7 @@
"name": "resetComponentState"
},
{
"name": "resolveDirective"
"name": "resolveDirectives"
},
{
"name": "restoreView"
@ -984,10 +1053,13 @@
"name": "scheduleTick"
},
{
"name": "searchDirectivesOnInjector"
"name": "searchTokensOnInjector"
},
{
"name": "searchMatchesQueuedForCreation"
"name": "setBindingRoot"
},
{
"name": "setCheckNoChangesMode"
},
{
"name": "setClass"
@ -999,13 +1071,13 @@
"name": "setContextPlayersDirty"
},
{
"name": "setCurrentInjector"
"name": "setCurrentQueries"
},
{
"name": "setDirty"
},
{
"name": "setEnvironment"
"name": "setFirstTemplatePass"
},
{
"name": "setFlag"
@ -1013,35 +1085,50 @@
{
"name": "setHostBindings"
},
{
"name": "setIncludeViewProviders"
},
{
"name": "setInjectImplementation"
},
{
"name": "setInputsForProperty"
},
{
"name": "setInputsFromAttrs"
},
{
"name": "setIsParent"
},
{
"name": "setPlayerBuilder"
},
{
"name": "setPlayerBuilderIndex"
},
{
"name": "setPreviousOrParentTNode"
},
{
"name": "setProp"
},
{
"name": "setRendererFactory"
},
{
"name": "setStyle"
},
{
"name": "setUpAttributes"
"name": "setTNodeAndViewData"
},
{
"name": "setUpBloom"
"name": "setUpAttributes"
},
{
"name": "setValue"
},
{
"name": "shouldNotSearchParent"
"name": "shouldSearchParent"
},
{
"name": "storeCleanupFn"
@ -1067,9 +1154,6 @@
{
"name": "textBinding"
},
{
"name": "throwCyclicDependencyError"
},
{
"name": "throwErrorIfNoChangesMode"
},

View File

@ -80,9 +80,6 @@
{
"name": "CIRCULAR$1"
},
{
"name": "CIRCULAR$2"
},
{
"name": "CLEANUP"
},
@ -308,6 +305,9 @@
{
"name": "FLAGS"
},
{
"name": "FactoryPrototype"
},
{
"name": "FormStyle"
},
@ -500,6 +500,9 @@
{
"name": "NON_ALPHANUMERIC_REGEXP"
},
{
"name": "NOT_FOUND"
},
{
"name": "NOT_YET"
},
@ -509,6 +512,9 @@
{
"name": "NO_NEW_LINE"
},
{
"name": "NO_PARENT_INJECTOR"
},
{
"name": "NULL_INJECTOR"
},
@ -587,6 +593,12 @@
{
"name": "NodeInjector"
},
{
"name": "NodeInjector$1"
},
{
"name": "NodeInjectorFactory"
},
{
"name": "NoopNgZone"
},
@ -668,9 +680,6 @@
{
"name": "Plural"
},
{
"name": "PublicFeature"
},
{
"name": "QUERIES"
},
@ -746,6 +755,9 @@
{
"name": "SWITCH_ELEMENT_REF_FACTORY"
},
{
"name": "SWITCH_INJECTOR_FACTORY"
},
{
"name": "SWITCH_RENDERER2_FACTORY"
},
@ -998,6 +1010,9 @@
{
"name": "__extends$p"
},
{
"name": "__forward_ref__"
},
{
"name": "__read"
},
@ -1115,9 +1130,6 @@
{
"name": "_enable_super_gross_mode_that_will_cause_bad_things"
},
{
"name": "_getViewData"
},
{
"name": "_global"
},
@ -1184,9 +1196,6 @@
{
"name": "addToViewTree"
},
{
"name": "adjustBlueprintForNewNode"
},
{
"name": "allocPlayerContext"
},
@ -1208,17 +1217,14 @@
{
"name": "attachPatchData"
},
{
"name": "baseDirectiveCreate"
},
{
"name": "baseElement"
},
{
"name": "bind"
"name": "baseResolveDirective"
},
{
"name": "bindingRootIndex"
"name": "bind"
},
{
"name": "bindingUpdated"
@ -1230,10 +1236,10 @@
"name": "bloomAdd"
},
{
"name": "bloomHashBitOrFactory"
"name": "bloomHasToken"
},
{
"name": "cacheMatchingDirectivesForNode"
"name": "bloomHashBitOrFactory"
},
{
"name": "cacheMatchingLocalNames"
@ -1394,6 +1400,9 @@
{
"name": "decoratePreventDefault"
},
{
"name": "decreaseElementDepthCount"
},
{
"name": "deepForEach"
},
@ -1427,6 +1436,9 @@
{
"name": "definePipe"
},
{
"name": "delegateToClassInput"
},
{
"name": "destroyLView"
},
@ -1448,15 +1460,9 @@
{
"name": "detectWTF"
},
{
"name": "diPublic"
},
{
"name": "diPublicInInjector"
},
{
"name": "directiveCreate"
},
{
"name": "directiveInject"
},
@ -1602,7 +1608,7 @@
"name": "fromPromise"
},
{
"name": "generateExpandoBlock"
"name": "generateExpandoInstructionBlock"
},
{
"name": "generateInitialInputs"
@ -1616,6 +1622,12 @@
{
"name": "getBeforeNodeForView"
},
{
"name": "getBindingsEnabled"
},
{
"name": "getCheckNoChangesMode"
},
{
"name": "getCleanup"
},
@ -1634,9 +1646,18 @@
{
"name": "getContainerRenderParent"
},
{
"name": "getContextViewData"
},
{
"name": "getCreationMode"
},
{
"name": "getCurrencySymbol"
},
{
"name": "getCurrentQueries"
},
{
"name": "getCurrentSanitizer"
},
@ -1664,9 +1685,15 @@
{
"name": "getDirectiveDef"
},
{
"name": "getElementDepthCount"
},
{
"name": "getErrorLogger"
},
{
"name": "getFirstTemplatePass"
},
{
"name": "getFirstThursdayOfYear"
},
@ -1691,6 +1718,9 @@
{
"name": "getInjectorIndex"
},
{
"name": "getIsParent"
},
{
"name": "getLContainer"
},
@ -1763,6 +1793,9 @@
{
"name": "getNgZone"
},
{
"name": "getNodeInjectable"
},
{
"name": "getNullInjector"
},
@ -1772,9 +1805,6 @@
{
"name": "getOrCreateInjectable"
},
{
"name": "getOrCreateNodeInjector"
},
{
"name": "getOrCreateNodeInjectorForNode"
},
@ -1787,12 +1817,21 @@
{
"name": "getOriginalError"
},
{
"name": "getParentInjectorIndex"
},
{
"name": "getParentInjectorLocation"
},
{
"name": "getParentInjectorTNode"
},
{
"name": "getParentInjectorView"
},
{
"name": "getParentInjectorViewOffset"
},
{
"name": "getParentNative"
},
@ -1865,6 +1904,9 @@
{
"name": "getTNode"
},
{
"name": "getTView"
},
{
"name": "getTViewCleanup"
},
@ -1883,6 +1925,9 @@
{
"name": "getValue"
},
{
"name": "getViewData"
},
{
"name": "globalListener"
},
@ -1895,6 +1940,9 @@
{
"name": "hasOnDestroy"
},
{
"name": "hasParentInjector"
},
{
"name": "hasPlayerBuilderChanged"
},
@ -1907,9 +1955,21 @@
{
"name": "identity"
},
{
"name": "includeViewProviders"
},
{
"name": "increaseElementDepthCount"
},
{
"name": "initDomAdapter"
},
{
"name": "initNodeFlags"
},
{
"name": "initializeTNodeInputs"
},
{
"name": "inject"
},
@ -1919,15 +1979,27 @@
{
"name": "injectAttribute"
},
{
"name": "injectAttributeImpl"
},
{
"name": "injectChangeDetectorRef"
},
{
"name": "injectElementRef"
},
{
"name": "injectInjector"
},
{
"name": "injectInjectorOnly"
},
{
"name": "injectRenderer2"
},
{
"name": "injectRootLimpMode"
},
{
"name": "injectTemplateRef"
},
@ -1935,10 +2007,10 @@
"name": "injectViewContainerRef"
},
{
"name": "injectableDefRecord"
"name": "injectableDefFactory"
},
{
"name": "injectorHasToken"
"name": "insertBloom"
},
{
"name": "insertView"
@ -1947,7 +2019,10 @@
"name": "inspectNativeElement"
},
{
"name": "instantiateDirectivesDirectly"
"name": "instantiateAllDirectives"
},
{
"name": "instantiateRootComponent"
},
{
"name": "interpolation1"
@ -1971,7 +2046,7 @@
"name": "isComponent"
},
{
"name": "isContentQueryHost"
"name": "isComponentDef"
},
{
"name": "isContextDirty"
@ -2000,6 +2075,9 @@
{
"name": "isExistingProvider"
},
{
"name": "isFactory"
},
{
"name": "isFactoryProvider"
},
@ -2081,6 +2159,9 @@
{
"name": "leaveView"
},
{
"name": "limitToSingleClasses"
},
{
"name": "listener"
},
@ -2144,6 +2225,9 @@
{
"name": "nextContext"
},
{
"name": "nextContextImpl"
},
{
"name": "nextNgElementId"
},
@ -2207,6 +2291,12 @@
{
"name": "pointers"
},
{
"name": "postProcessBaseDirective"
},
{
"name": "postProcessDirective"
},
{
"name": "prefillHostVars"
},
@ -2219,6 +2309,9 @@
{
"name": "promise"
},
{
"name": "providerToFactory"
},
{
"name": "providerToRecord"
},
@ -2298,7 +2391,7 @@
"name": "resetComponentState"
},
{
"name": "resolveDirective"
"name": "resolveDirectives"
},
{
"name": "resolveForwardRef$1"
@ -2334,10 +2427,13 @@
"name": "scheduleTick"
},
{
"name": "searchDirectivesOnInjector"
"name": "searchTokensOnInjector"
},
{
"name": "searchMatchesQueuedForCreation"
"name": "setBindingRoot"
},
{
"name": "setCheckNoChangesMode"
},
{
"name": "setClass"
@ -2351,11 +2447,14 @@
{
"name": "setCurrentInjector"
},
{
"name": "setCurrentQueries"
},
{
"name": "setDirty"
},
{
"name": "setEnvironment"
"name": "setFirstTemplatePass"
},
{
"name": "setFlag"
@ -2363,36 +2462,51 @@
{
"name": "setHostBindings"
},
{
"name": "setIncludeViewProviders"
},
{
"name": "setInjectImplementation"
},
{
"name": "setInputsForProperty"
},
{
"name": "setInputsFromAttrs"
},
{
"name": "setIsParent"
},
{
"name": "setPlayerBuilder"
},
{
"name": "setPlayerBuilderIndex"
},
{
"name": "setPreviousOrParentTNode"
},
{
"name": "setProp"
},
{
"name": "setRendererFactory"
},
{
"name": "setRootDomAdapter"
},
{
"name": "setStyle"
},
{
"name": "setTNodeAndViewData"
},
{
"name": "setTestabilityGetter"
},
{
"name": "setUpAttributes"
},
{
"name": "setUpBloom"
},
{
"name": "setValue"
},
@ -2409,7 +2523,7 @@
"name": "shimHostAttribute"
},
{
"name": "shouldNotSearchParent"
"name": "shouldSearchParent"
},
{
"name": "staticError"
@ -2474,9 +2588,6 @@
{
"name": "textBinding"
},
{
"name": "throwCyclicDependencyError"
},
{
"name": "throwErrorIfNoChangesMode"
},

View File

@ -8,7 +8,8 @@
import {defineInjectable, defineInjector} from '../../src/di/defs';
import {InjectionToken} from '../../src/di/injection_token';
import {INJECTOR, Injector, inject} from '../../src/di/injector';
import {INJECTOR, Injector} from '../../src/di/injector';
import {inject} from '../../src/di/injector_compatibility';
import {R3Injector, createInjector} from '../../src/di/r3_injector';
describe('InjectorDef-based createInjector()', () => {

View File

@ -239,7 +239,7 @@ function declareTests({useJit}: {useJit: boolean}) {
ci.ctxProp = 'ha <script>evil()</script>';
fixture.detectChanges();
expect(getDOM().getInnerHTML(e)).toEqual('ha evil()');
expect(getDOM().getInnerHTML(e)).toEqual('ha ');
ci.ctxProp = 'also <img src="x" onerror="evil()"> evil';
fixture.detectChanges();

View File

@ -397,7 +397,7 @@ describe('InheritDefinitionFeature', () => {
const subDef = SubDirective.ngDirectiveDef as DirectiveDef<any>;
subDef.contentQueries !();
subDef.contentQueries !(0);
expect(log).toEqual(['super', 'sub']);
});

View File

@ -11,9 +11,8 @@ import {withBody} from '@angular/private/testing';
import {ChangeDetectionStrategy, ChangeDetectorRef, DoCheck, RendererType2} from '../../src/core';
import {getRenderedText, whenRendered} from '../../src/render3/component';
import {directiveInject} from '../../src/render3/di';
import {LifecycleHooksFeature, defineComponent, defineDirective, templateRefExtractor} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, detectChanges, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, listener, markDirty, reference, text, template, textBinding, tick} from '../../src/render3/instructions';
import {bind, container, containerRefreshEnd, containerRefreshStart, detectChanges, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, listener, markDirty, reference, text, template, textBinding, tick} from '../../src/render3/instructions';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {RElement, Renderer3, RendererFactory3} from '../../src/render3/interfaces/renderer';

View File

@ -10,8 +10,9 @@ import {NgForOfContext} from '@angular/common';
import {ElementRef, TemplateRef} from '@angular/core';
import {AttributeMarker, defineComponent, templateRefExtractor} from '../../src/render3/index';
import {bind, template, elementEnd, elementProperty, elementStart, getCurrentView, interpolation1, interpolation2, interpolation3, interpolationV, listener, load, nextContext, restoreView, text, textBinding, elementContainerStart, elementContainerEnd, reference} from '../../src/render3/instructions';
import {bind, template, elementEnd, elementProperty, elementStart, interpolation1, interpolation2, interpolation3, interpolationV, listener, load, nextContext, text, textBinding, elementContainerStart, elementContainerEnd, reference} from '../../src/render3/instructions';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {getCurrentView, restoreView} from '../../src/render3/state';
import {NgForOf, NgIf, NgTemplateOutlet} from './common_with_def';
import {ComponentFixture} from './render_util';

View File

@ -31,7 +31,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: ChildComponent,
selectors: [['child']],
factory: function ChildComponent_Factory() { return new ChildComponent(); },
factory: function ChildComponent_Factory(t) { return new (t || ChildComponent)(); },
consts: 1,
vars: 0,
template: function ChildComponent_Template(rf: $RenderFlags$, ctx: $ChildComponent$) {
@ -52,7 +52,7 @@ describe('components & directives', () => {
static ngDirectiveDef = $r3$.ɵdefineDirective({
type: SomeDirective,
selectors: [['', 'some-directive', '']],
factory: () => new SomeDirective(),
factory: function SomeDirective_Factory(t) { return new (t || SomeDirective)(); },
});
// /NORMATIVE
}
@ -68,7 +68,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: () => new MyComponent(),
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 2,
vars: 0,
template: function(rf: $RenderFlags$, ctx: $MyComponent$) {
@ -101,7 +101,7 @@ describe('components & directives', () => {
static ngDirectiveDef = $r3$.ɵdefineDirective({
type: HostBindingDir,
selectors: [['', 'hostBindingDir', '']],
factory: function HostBindingDir_Factory() { return new HostBindingDir(); },
factory: function HostBindingDir_Factory(t) { return new (t || HostBindingDir)(); },
hostVars: 1,
hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) {
$r3$.ɵelementProperty(
@ -123,7 +123,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 1,
vars: 0,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
@ -175,7 +175,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 2,
vars: 0,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
@ -205,7 +205,7 @@ describe('components & directives', () => {
static ngDirectiveDef = $r3$.ɵdefineDirective({
selectors: [['', 'hostAttributeDir', '']],
type: HostAttributeDir,
factory: function HostAttributeDir_Factory() { return new HostAttributeDir(); },
factory: function HostAttributeDir_Factory(t) { return new (t || HostAttributeDir)(); },
attributes: ['role', 'listbox']
});
// /NORMATIVE
@ -223,7 +223,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 1,
vars: 0,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
@ -252,7 +252,7 @@ describe('components & directives', () => {
static ngDirectiveDef = $r3$.ɵdefineDirective({
type: HostBindingDir,
selectors: [['', 'hostBindingDir', '']],
factory: function HostBindingDir_Factory() { return new HostBindingDir(); },
factory: function HostBindingDir_Factory(t) { return new (t || HostBindingDir)(); },
hostVars: 1,
hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) {
$r3$.ɵelementAttribute(
@ -274,7 +274,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 1,
vars: 0,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
@ -311,7 +311,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComp,
selectors: [['my-comp']],
factory: function MyComp_Factory() { return new MyComp(); },
factory: function MyComp_Factory(t) { return new (t || MyComp)(); },
consts: 1,
vars: 1,
template: function MyComp_Template(rf: $RenderFlags$, ctx: $MyComp$) {
@ -340,7 +340,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 1,
vars: 1,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
@ -388,7 +388,9 @@ describe('components & directives', () => {
static ngDirectiveDef = $r3$.ɵdefineDirective({
type: IfDirective,
selectors: [['', 'if', '']],
factory: () => new IfDirective($r3$.ɵdirectiveInject(TemplateRef as any)),
factory: function IfDirective_Factory(t) {
return new (t || IfDirective)($r3$.ɵdirectiveInject(TemplateRef as any));
},
});
// /NORMATIVE
}
@ -406,7 +408,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: () => new MyComponent(),
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 3,
vars: 0,
template: function(rf: $RenderFlags$, ctx: $MyComponent$) {
@ -440,7 +442,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyArrayComp,
selectors: [['my-array-comp']],
factory: function MyArrayComp_Factory() { return new MyArrayComp(); },
factory: function MyArrayComp_Factory(t) { return new (t || MyArrayComp)(); },
consts: 1,
vars: 2,
template: function MyArrayComp_Template(rf: $RenderFlags$, ctx: $MyArrayComp$) {
@ -473,7 +475,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 1,
vars: 0,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
@ -519,7 +521,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 1,
vars: 2,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
@ -555,7 +557,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComp,
selectors: [['my-comp']],
factory: function MyComp_Factory() { return new MyComp(); },
factory: function MyComp_Factory(t) { return new (t || MyComp)(); },
consts: 1,
vars: 1,
template: function MyComp_Template(rf: $RenderFlags$, ctx: $MyComp$) {
@ -589,7 +591,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 1,
vars: 2,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
@ -634,7 +636,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 1,
vars: 3,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
@ -684,7 +686,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComp,
selectors: [['my-comp']],
factory: function MyComp_Factory() { return new MyComp(); },
factory: function MyComp_Factory(t) { return new (t || MyComp)(); },
consts: 12,
vars: 12,
template: function MyComp_Template(rf: $RenderFlags$, ctx: $MyComp$) {
@ -749,7 +751,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 1,
vars: 11,
template: function MyApp_Template(rf: $RenderFlags$, c: $any$) {
@ -793,7 +795,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: ObjectComp,
selectors: [['object-comp']],
factory: function ObjectComp_Factory() { return new ObjectComp(); },
factory: function ObjectComp_Factory(t) { return new (t || ObjectComp)(); },
consts: 4,
vars: 2,
template: function ObjectComp_Template(rf: $RenderFlags$, ctx: $ObjectComp$) {
@ -831,7 +833,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 1,
vars: 3,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
@ -874,7 +876,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: NestedComp,
selectors: [['nested-comp']],
factory: function NestedComp_Factory() { return new NestedComp(); },
factory: function NestedComp_Factory(t) { return new (t || NestedComp)(); },
consts: 6,
vars: 3,
template: function NestedComp_Template(rf: $RenderFlags$, ctx: $NestedComp$) {
@ -921,7 +923,7 @@ describe('components & directives', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 1,
vars: 8,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {

View File

@ -24,7 +24,7 @@ describe('content projection', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: SimpleComponent,
selectors: [['simple']],
factory: () => new SimpleComponent(),
factory: function SimpleComponent_Factory(t) { return new (t || SimpleComponent)(); },
consts: 1,
vars: 0,
template: function(rf: $RenderFlags$, ctx: $SimpleComponent$) {
@ -55,7 +55,7 @@ describe('content projection', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: ComplexComponent,
selectors: [['complex']],
factory: () => new ComplexComponent(),
factory: function ComplexComponent_Factory(t) { return new (t || ComplexComponent)(); },
consts: 4,
vars: 0,
template: function(rf: $RenderFlags$, ctx: $ComplexComponent$) {
@ -80,7 +80,7 @@ describe('content projection', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: () => new MyApp(),
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 2,
vars: 0,
template: function(rf: $RenderFlags$, ctx: $MyApp$) {

View File

@ -34,7 +34,7 @@ describe('elements', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: () => new MyComponent(),
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 5,
vars: 0,
template: function(rf: $RenderFlags$, ctx: $MyComponent$) {
@ -65,7 +65,7 @@ describe('elements', () => {
static ngDirectiveDef = $r3$.ɵdefineDirective({
type: Dir,
selectors: [['', 'dir', '']],
factory: function DirA_Factory() { return new Dir(); },
factory: function DirA_Factory(t) { return new (t || Dir)(); },
exportAs: 'dir'
});
}
@ -87,7 +87,7 @@ describe('elements', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: LocalRefComp,
selectors: [['local-ref-comp']],
factory: function LocalRefComp_Factory() { return new LocalRefComp(); },
factory: function LocalRefComp_Factory(t) { return new (t || LocalRefComp)(); },
consts: 4,
vars: 2,
template: function LocalRefComp_Template(rf: $RenderFlags$, ctx: $LocalRefComp$) {
@ -131,7 +131,7 @@ describe('elements', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: ListenerComp,
selectors: [['listener-comp']],
factory: function ListenerComp_Factory() { return new ListenerComp(); },
factory: function ListenerComp_Factory(t) { return new (t || ListenerComp)(); },
consts: 2,
vars: 0,
template: function ListenerComp_Template(rf: $RenderFlags$, ctx: $ListenerComp$) {
@ -188,7 +188,7 @@ describe('elements', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: () => new MyComponent(),
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 5,
vars: 0,
template: function(rf: $RenderFlags$, ctx: $MyComponent$) {
@ -222,7 +222,7 @@ describe('elements', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: function MyComponent_Factory() { return new MyComponent(); },
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 1,
vars: 1,
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
@ -255,7 +255,7 @@ describe('elements', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: function MyComponent_Factory() { return new MyComponent(); },
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 1,
vars: 1,
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
@ -289,7 +289,7 @@ describe('elements', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: function MyComponent_Factory() { return new MyComponent(); },
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 1,
vars: 0,
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
@ -334,7 +334,7 @@ describe('elements', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: function MyComponent_Factory() { return new MyComponent(); },
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 1,
vars: 0,
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
@ -379,7 +379,7 @@ describe('elements', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: function MyComponent_Factory() { return new MyComponent(); },
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 1,
vars: 1,
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
@ -419,7 +419,7 @@ describe('elements', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: StyleComponent,
selectors: [['style-comp']],
factory: function StyleComponent_Factory() { return new StyleComponent(); },
factory: function StyleComponent_Factory(t) { return new (t || StyleComponent)(); },
consts: 1,
vars: 0,
template: function StyleComponent_Template(rf: $RenderFlags$, ctx: $StyleComponent$) {

View File

@ -25,7 +25,7 @@ describe('i18n', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: () => new MyApp(),
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 2,
vars: 0,
template: function(rf: $RenderFlags$, ctx: $MyApp$) {
@ -52,7 +52,7 @@ describe('i18n', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: () => new MyApp(),
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 2,
vars: 1,
template: function(rf: $RenderFlags$, ctx: $MyApp$) {
@ -81,7 +81,7 @@ describe('i18n', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: () => new MyApp(),
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 2,
vars: 1,
template: function(rf: $RenderFlags$, ctx: $MyApp$) {
@ -125,7 +125,7 @@ describe('i18n', () => {
items: string[] = ['1', '2'];
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
factory: () => new MyApp(),
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
selectors: [['my-app']],
consts: 2,
vars: 1,

View File

@ -8,6 +8,7 @@
import {Attribute, ChangeDetectorRef, Component, INJECTOR, Inject, InjectFlags, Injectable, Injector, SkipSelf, defineInjectable, inject} from '../../../src/core';
import * as $r3$ from '../../../src/core_render3_private_export';
import {ProvidersFeature} from '../../../src/render3/features/providers_feature';
import {renderComponent, toHtml} from '../render_util';
@ -31,8 +32,8 @@ describe('injection', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComp,
selectors: [['my-comp']],
factory: function MyComp_Factory() {
return new MyComp($r3$.ɵdirectiveInject(ChangeDetectorRef as any));
factory: function MyComp_Factory(t) {
return new (t || MyComp)($r3$.ɵdirectiveInject(ChangeDetectorRef as any));
},
consts: 1,
vars: 1,
@ -52,7 +53,7 @@ describe('injection', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 1,
vars: 0,
/** <my-comp></my-comp> */
@ -83,7 +84,9 @@ describe('injection', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComp,
selectors: [['my-comp']],
factory: function MyComp_Factory() { return new MyComp($r3$.ɵinjectAttribute('title')); },
factory: function MyComp_Factory(t) {
return new (t || MyComp)($r3$.ɵinjectAttribute('title'));
},
consts: 1,
vars: 1,
template: function MyComp_Template(rf: $RenderFlags$, ctx: $MyComp$) {
@ -102,7 +105,7 @@ describe('injection', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 1,
vars: 0,
/** <my-comp></my-comp> */
@ -153,15 +156,14 @@ describe('injection', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() {
return new MyApp(
factory: function MyApp_Factory(t) {
return new (t || MyApp)(
$r3$.ɵdirectiveInject(ServiceA), $r3$.ɵdirectiveInject(ServiceB), inject(INJECTOR));
},
consts: 0,
vars: 0,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {},
providers: [ServiceA],
viewProviders: [ServiceB],
features: [ProvidersFeature([ServiceA], [ServiceB])]
});
}
const e0_attrs = ['title', 'WORKS'];

View File

@ -45,7 +45,7 @@ describe('lifecycle hooks', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: LifecycleComp,
selectors: [['lifecycle-comp']],
factory: function LifecycleComp_Factory() { return new LifecycleComp(); },
factory: function LifecycleComp_Factory(t) { return new (t || LifecycleComp)(); },
consts: 0,
vars: 0,
template: function LifecycleComp_Template(rf: $RenderFlags$, ctx: $LifecycleComp$) {},
@ -70,7 +70,9 @@ describe('lifecycle hooks', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: SimpleLayout,
selectors: [['simple-layout']],
factory: function SimpleLayout_Factory() { return simpleLayout = new SimpleLayout(); },
factory: function SimpleLayout_Factory(t) {
return simpleLayout = new (t || SimpleLayout)();
},
consts: 2,
vars: 2,
template: function SimpleLayout_Template(rf: $RenderFlags$, ctx: $SimpleLayout$) {

View File

@ -27,7 +27,7 @@ describe('local references', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: () => new MyComponent,
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 3,
vars: 1,
template: function(rf: $RenderFlags$, ctx: $MyComponent$) {
@ -64,7 +64,7 @@ describe('local references', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: () => new MyComponent,
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 3,
vars: 1,
template: function(rf: $RenderFlags$, ctx: $MyComponent$) {

View File

@ -34,7 +34,7 @@ xdescribe('NgModule', () => {
constructor(name: String) {}
// NORMATIVE
static ngInjectableDef = defineInjectable({
factory: () => new Toast($r3$.ɵdirectiveInject(String)),
factory: function Toast_Factory() { return new Toast($r3$.ɵdirectiveInject(String)); },
});
// /NORMATIVE
}
@ -53,7 +53,7 @@ xdescribe('NgModule', () => {
constructor(toast: Toast) {}
// NORMATIVE
static ngInjectorDef = defineInjector({
factory: () => new MyModule($r3$.ɵdirectiveInject(Toast)),
factory: function MyModule_Factory() { return new MyModule($r3$.ɵdirectiveInject(Toast)); },
provider: [
{provide: Toast, deps: [String]}, // If Toast has metadata generate this line
Toast, // If Toast has no metadata generate this line.
@ -70,9 +70,11 @@ xdescribe('NgModule', () => {
// NORMATIVE
static ngInjectableDef = defineInjectable({
providedIn: MyModule,
factory: () => new BurntToast(
$r3$.ɵdirectiveInject(Toast, $core$.InjectFlags.Optional),
$r3$.ɵdirectiveInject(String)),
factory: function BurntToast_Factory() {
return new BurntToast(
$r3$.ɵdirectiveInject(Toast, $core$.InjectFlags.Optional),
$r3$.ɵdirectiveInject(String));
},
});
// /NORMATIVE
}

View File

@ -43,7 +43,7 @@ describe('pipes', () => {
static ngPipeDef = $r3$.ɵdefinePipe({
name: 'myPipe',
type: MyPipe,
factory: function MyPipe_Factory() { return new MyPipe(); },
factory: function MyPipe_Factory(t) { return new (t || MyPipe)(); },
pure: false,
});
// /NORMATIVE
@ -63,7 +63,7 @@ describe('pipes', () => {
static ngPipeDef = $r3$.ɵdefinePipe({
name: 'myPurePipe',
type: MyPurePipe,
factory: function MyPurePipe_Factory() { return new MyPurePipe(); },
factory: function MyPurePipe_Factory(t) { return new (t || MyPurePipe)(); },
pure: true,
});
// /NORMATIVE
@ -83,7 +83,7 @@ describe('pipes', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 3,
vars: 7,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
@ -146,9 +146,11 @@ describe('pipes', () => {
static ngDirectiveDef = $r3$.ɵdefineDirective({
type: OneTimeIf,
selectors: [['', 'oneTimeIf', '']],
factory: () => new OneTimeIf(
$r3$.ɵdirectiveInject(ViewContainerRef as any),
$r3$.ɵdirectiveInject(TemplateRef as any)),
factory: function OneTimeIf_Factory(t) {
return new (t || OneTimeIf)(
$r3$.ɵdirectiveInject(ViewContainerRef as any),
$r3$.ɵdirectiveInject(TemplateRef as any));
},
inputs: {oneTimeIf: 'oneTimeIf'}
});
// /NORMATIVE
@ -181,7 +183,7 @@ describe('pipes', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 5,
vars: 9,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {

View File

@ -25,8 +25,7 @@ describe('queries', () => {
static ngDirectiveDef = $r3$.ɵdefineDirective({
type: SomeDirective,
selectors: [['', 'someDir', '']],
factory: function SomeDirective_Factory() { return someDir = new SomeDirective(); },
features: [$r3$.ɵPublicFeature]
factory: function SomeDirective_Factory(t) { return someDir = new (t || SomeDirective)(); }
});
}
@ -53,7 +52,7 @@ describe('queries', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: ViewQueryComponent,
selectors: [['view-query-component']],
factory: function ViewQueryComponent_Factory() { return new ViewQueryComponent(); },
factory: function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); },
consts: 3,
vars: 0,
template: function ViewQueryComponent_Template(
@ -111,12 +110,14 @@ describe('queries', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: ContentQueryComponent,
selectors: [['content-query-component']],
factory: function ContentQueryComponent_Factory() { return new ContentQueryComponent(); },
factory: function ContentQueryComponent_Factory(t) {
return new (t || ContentQueryComponent)();
},
consts: 2,
vars: 0,
contentQueries: function ContentQueryComponent_ContentQueries() {
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false));
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false));
contentQueries: function ContentQueryComponent_ContentQueries(dirIndex: $number$) {
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false), dirIndex);
$r3$.ɵregisterContentQuery($r3$.ɵquery(null, SomeDirective, false), dirIndex);
},
contentQueriesRefresh: function ContentQueryComponent_ContentQueriesRefresh(
dirIndex: $number$, queryStartIndex: $number$) {
@ -155,7 +156,7 @@ describe('queries', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyApp,
selectors: [['my-app']],
factory: function MyApp_Factory() { return new MyApp(); },
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
consts: 2,
vars: 0,
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {

View File

@ -40,7 +40,7 @@ describe('compiler sanitization', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: function MyComponent_Factory() { return new MyComponent(); },
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 2,
vars: 4,
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {

View File

@ -74,8 +74,8 @@ class ToDoAppComponent {
static ngComponentDef = r3.defineComponent({
type: ToDoAppComponent,
selectors: [['todo-app']],
factory: function ToDoAppComponent_Factory() {
return new ToDoAppComponent(r3.directiveInject(AppState));
factory: function ToDoAppComponent_Factory(t) {
return new (t || ToDoAppComponent)(r3.directiveInject(AppState));
},
consts: 6,
vars: 1,
@ -135,7 +135,7 @@ class ToDoItemComponent {
static ngComponentDef = r3.defineComponent({
type: ToDoItemComponent,
selectors: [['todo']],
factory: function ToDoItemComponent_Factory() { return new ToDoItemComponent(); },
factory: function ToDoItemComponent_Factory(t) { return new (t || ToDoItemComponent)(); },
consts: 6,
vars: 2,
template: function ToDoItemComponent_Template(rf: $RenderFlags$, ctx: ToDoItemComponent) {
@ -174,7 +174,7 @@ const e1_attrs = ['type', 'checkbox'];
class ToDoAppModule {
// NORMATIVE
static ngInjectorDef = defineInjector({
factory: () => new ToDoAppModule(),
factory: function ToDoAppModule_Factory() { return new ToDoAppModule(); },
providers: [AppState],
});
// /NORMATIVE

View File

@ -69,8 +69,8 @@ describe('template variables', () => {
static ngDirectiveDef = $r3$.ɵdefineDirective({
type: ForOfDirective,
selectors: [['', 'forOf', '']],
factory: function ForOfDirective_Factory() {
return new ForOfDirective(
factory: function ForOfDirective_Factory(t) {
return new (t || ForOfDirective)(
$r3$.ɵdirectiveInject(ViewContainerRef as any),
$r3$.ɵdirectiveInject(TemplateRef as any));
},
@ -111,7 +111,7 @@ describe('template variables', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: function MyComponent_Factory() { return new MyComponent(); },
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 2,
vars: 1,
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
@ -205,7 +205,7 @@ describe('template variables', () => {
static ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
selectors: [['my-component']],
factory: function MyComponent_Factory() { return new MyComponent(); },
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
consts: 2,
vars: 1,
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
/**
* @license
* Copyright Google Inc. 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 {getComponent, getDirectives, getHostComponent, getInjector, getRootComponents} from '../../src/render3/discovery_utils';
import {GLOBAL_PUBLISH_EXPANDO_KEY, GlobalDevModeContainer, publishDefaultGlobalUtils, publishGlobalUtil} from '../../src/render3/publish_global_util';
import {global} from '../../src/util';
describe('dev mode utils', () => {
describe('devModePublish', () => {
it('should publish a function to the window', () => {
const w = global as any as GlobalDevModeContainer;
expect(w[GLOBAL_PUBLISH_EXPANDO_KEY]['foo']).toBeFalsy();
const fooFn = () => {};
publishGlobalUtil('foo', fooFn);
expect(w[GLOBAL_PUBLISH_EXPANDO_KEY]['foo']).toBe(fooFn);
});
});
describe('publishDefaultGlobalUtils', () => {
beforeEach(() => publishDefaultGlobalUtils());
it('should publish getComponent', () => { assertPublished('getComponent', getComponent); });
it('should publish getRootComponents',
() => { assertPublished('getRootComponents', getRootComponents); });
it('should publish getDirectives', () => { assertPublished('getDirectives', getDirectives); });
it('should publish getHostComponent',
() => { assertPublished('getHostComponent', getHostComponent); });
it('should publish getInjector', () => { assertPublished('getInjector', getInjector); });
});
});
function assertPublished(name: string, value: {}) {
const w = global as any as GlobalDevModeContainer;
expect(w[GLOBAL_PUBLISH_EXPANDO_KEY][name]).toBe(value);
}

View File

@ -10,14 +10,15 @@ import {Attribute, ChangeDetectorRef, ElementRef, Host, InjectFlags, Injector, O
import {RenderFlags} from '@angular/core/src/render3/interfaces/definition';
import {defineComponent} from '../../src/render3/definition';
import {bloomAdd, bloomHashBitOrFactory as bloomHash, getOrCreateNodeInjector, injectAttribute, injectorHasToken} from '../../src/render3/di';
import {PublicFeature, defineDirective, directiveInject, elementProperty, load, templateRefExtractor} from '../../src/render3/index';
import {bloomAdd, bloomHasToken, bloomHashBitOrFactory as bloomHash, getOrCreateNodeInjectorForNode} from '../../src/render3/di';
import {defineDirective, elementProperty, load, templateRefExtractor} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, template, text, textBinding, elementContainerStart, elementContainerEnd, _getViewData} from '../../src/render3/instructions';
import {bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLViewData, createTView, directiveInject, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, injectAttribute, interpolation2, projection, projectionDef, reference, template, text, textBinding, elementContainerStart, elementContainerEnd} from '../../src/render3/instructions';
import {isProceduralRenderer, RElement} from '../../src/render3/interfaces/renderer';
import {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node';
import {getNativeByIndex} from '../../src/render3/util';
import {LViewFlags} from '../../src/render3/interfaces/view';
import {getViewData, enterView, leaveView} from '../../src/render3/state';
import {ViewRef} from '../../src/render3/view_ref';
import {getRendererFactory2} from './imported_renderer2';
@ -68,7 +69,6 @@ describe('di', () => {
selectors: [['', 'dirB', '']],
type: DirB,
factory: () => new DirB(),
features: [PublicFeature],
inputs: {value: 'value'}
});
}
@ -78,12 +78,8 @@ describe('di', () => {
it('should create directive with intra view dependencies', () => {
class DirA {
value: string = 'DirA';
static ngDirectiveDef = defineDirective({
type: DirA,
selectors: [['', 'dirA', '']],
factory: () => new DirA(),
features: [PublicFeature]
});
static ngDirectiveDef =
defineDirective({type: DirA, selectors: [['', 'dirA', '']], factory: () => new DirA()});
}
class DirC {
@ -246,12 +242,8 @@ describe('di', () => {
value = 'DirA';
constructor() { log.push(this.value); }
static ngDirectiveDef = defineDirective({
selectors: [['', 'dirA', '']],
type: DirA,
factory: () => new DirA(),
features: [PublicFeature]
});
static ngDirectiveDef =
defineDirective({selectors: [['', 'dirA', '']], type: DirA, factory: () => new DirA()});
}
class DirB {
@ -270,12 +262,8 @@ describe('di', () => {
value = 'DirC';
constructor() { log.push(this.value); }
static ngDirectiveDef = defineDirective({
selectors: [['', 'dirC', '']],
type: DirC,
factory: () => new DirC(),
features: [PublicFeature]
});
static ngDirectiveDef =
defineDirective({selectors: [['', 'dirC', '']], type: DirC, factory: () => new DirC()});
}
/** <div dirA dirB dirC></div> */
@ -310,8 +298,7 @@ describe('di', () => {
static ngDirectiveDef = defineDirective({
selectors: [['', 'dirA', '']],
type: DirA,
factory: () => new DirA(directiveInject(DirC)),
features: [PublicFeature]
factory: () => new DirA(directiveInject(DirC))
});
}
@ -322,8 +309,7 @@ describe('di', () => {
static ngDirectiveDef = defineDirective({
selectors: [['', 'dirC', '']],
type: DirC,
factory: () => new DirC(directiveInject(DirB)),
features: [PublicFeature]
factory: () => new DirC(directiveInject(DirB))
});
}
@ -334,8 +320,7 @@ describe('di', () => {
static ngDirectiveDef = defineDirective({
selectors: [['', 'dirD', '']],
type: DirD,
factory: () => new DirD(directiveInject(DirA)),
features: [PublicFeature]
factory: () => new DirD(directiveInject(DirA))
});
}
@ -379,8 +364,7 @@ describe('di', () => {
element(0, 'div', ['dirA', '', 'dirB', '', 'dirC', 'dirC']);
}
},
directives: [DirA, DirB],
features: [PublicFeature],
directives: [DirA, DirB]
});
}
@ -409,12 +393,8 @@ describe('di', () => {
this.count = count++;
}
static ngDirectiveDef = defineDirective({
selectors: [['', 'dirB', '']],
type: DirB,
factory: () => new DirB(),
features: [PublicFeature],
});
static ngDirectiveDef =
defineDirective({selectors: [['', 'dirB', '']], type: DirB, factory: () => new DirB()});
}
/** <div dirA dirB></div> */
@ -447,7 +427,6 @@ describe('di', () => {
type: DirA,
selectors: [['', 'dirA', '']],
factory: () => new DirA(directiveInject(DirB), directiveInject(ViewContainerRef as any)),
features: [PublicFeature],
exportAs: 'dirA'
});
}
@ -599,8 +578,7 @@ describe('di', () => {
selectors: [['', 'structuralDir', '']],
factory: () => structuralDir =
new StructuralDir(directiveInject(ViewContainerRef as any)),
inputs: {tmp: 'tmp'},
features: [PublicFeature]
inputs: {tmp: 'tmp'}
});
}
@ -763,18 +741,13 @@ describe('di', () => {
static ngDirectiveDef = defineDirective({
selectors: [['', 'dir', '']],
type: Dir,
factory: () => new Dir(directiveInject(OtherDir)),
features: [PublicFeature]
factory: () => new Dir(directiveInject(OtherDir))
});
}
class OtherDir {
static ngDirectiveDef = defineDirective({
selectors: [['', 'other', '']],
type: OtherDir,
factory: () => new OtherDir(),
features: [PublicFeature]
});
static ngDirectiveDef = defineDirective(
{selectors: [['', 'other', '']], type: OtherDir, factory: () => new OtherDir()});
}
/** <div dir></div> */
@ -794,18 +767,13 @@ describe('di', () => {
static ngDirectiveDef = defineDirective({
selectors: [['', 'dir', '']],
type: Dir,
factory: () => new Dir(directiveInject(OtherDir)),
features: [PublicFeature]
factory: () => new Dir(directiveInject(OtherDir))
});
}
class OtherDir {
static ngDirectiveDef = defineDirective({
selectors: [['', 'other', '']],
type: OtherDir,
factory: () => new OtherDir(),
features: [PublicFeature]
});
static ngDirectiveDef = defineDirective(
{selectors: [['', 'other', '']], type: OtherDir, factory: () => new OtherDir()});
}
/**
@ -830,8 +798,7 @@ describe('di', () => {
static ngDirectiveDef = defineDirective({
selectors: [['', 'dirA', '']],
type: DirA,
factory: () => new DirA(directiveInject(DirB)),
features: [PublicFeature]
factory: () => new DirA(directiveInject(DirB))
});
}
@ -841,8 +808,7 @@ describe('di', () => {
static ngDirectiveDef = defineDirective({
selectors: [['', 'dirB', '']],
type: DirB,
factory: () => new DirB(directiveInject(DirA)),
features: [PublicFeature]
factory: () => new DirB(directiveInject(DirA))
});
}
@ -853,7 +819,7 @@ describe('di', () => {
}
}, 1, 0, [DirA, DirB]);
expect(() => new ComponentFixture(App)).toThrowError(/Cannot instantiate cyclic dependency!/);
expect(() => new ComponentFixture(App)).toThrowError(/Circular dep for/);
});
it('should throw if directive tries to inject itself', () => {
@ -863,8 +829,7 @@ describe('di', () => {
static ngDirectiveDef = defineDirective({
selectors: [['', 'dir', '']],
type: Dir,
factory: () => new Dir(directiveInject(Dir)),
features: [PublicFeature]
factory: () => new Dir(directiveInject(Dir))
});
}
@ -875,7 +840,7 @@ describe('di', () => {
}
}, 1, 0, [Dir]);
expect(() => new ComponentFixture(App)).toThrowError(/Cannot instantiate cyclic dependency!/);
expect(() => new ComponentFixture(App)).toThrowError(/Circular dep for/);
});
describe('flags', () => {
@ -888,8 +853,7 @@ describe('di', () => {
type: DirB,
selectors: [['', 'dirB', '']],
factory: () => new DirB(),
inputs: {value: 'dirB'},
features: [PublicFeature]
inputs: {value: 'dirB'}
});
}
@ -1006,7 +970,9 @@ describe('di', () => {
}
}, 2, 0, [DirA, DirB]);
expect(() => { new ComponentFixture(App); }).toThrowError(/Injector: NOT_FOUND \[DirB\]/);
expect(() => {
new ComponentFixture(App);
}).toThrowError(/NodeInjector: NOT_FOUND \[DirB\]/);
});
it('should check only the current node with @Self even with false positive', () => {
@ -1041,7 +1007,7 @@ describe('di', () => {
(DirA as any)['__NG_ELEMENT_ID__'] = 1;
(DirC as any)['__NG_ELEMENT_ID__'] = 257;
new ComponentFixture(App);
}).toThrowError(/Injector: NOT_FOUND \[DirB\]/);
}).toThrowError(/NodeInjector: NOT_FOUND \[DirB\]/);
});
it('should not pass component boundary with @Host', () => {
@ -1071,7 +1037,9 @@ describe('di', () => {
}
}, 1, 0, [Comp, DirB]);
expect(() => { new ComponentFixture(App); }).toThrowError(/Injector: NOT_FOUND \[DirB\]/);
expect(() => {
new ComponentFixture(App);
}).toThrowError(/NodeInjector: NOT_FOUND \[DirB\]/);
});
@ -1081,6 +1049,53 @@ describe('di', () => {
describe('Special tokens', () => {
describe('Injector', () => {
it('should inject the injector', () => {
let injectorDir !: InjectorDir;
let otherInjectorDir !: OtherInjectorDir;
let divElement !: HTMLElement;
class InjectorDir {
constructor(public injector: Injector) {}
static ngDirectiveDef = defineDirective({
type: InjectorDir,
selectors: [['', 'injectorDir', '']],
factory: () => injectorDir = new InjectorDir(directiveInject(Injector as any))
});
}
class OtherInjectorDir {
constructor(public otherDir: InjectorDir, public injector: Injector) {}
static ngDirectiveDef = defineDirective({
type: OtherInjectorDir,
selectors: [['', 'otherInjectorDir', '']],
factory: () => otherInjectorDir = new OtherInjectorDir(
directiveInject(InjectorDir), directiveInject(Injector as any))
});
}
/** <div injectorDir otherInjectorDir></div> */
const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
if (rf & RenderFlags.Create) {
element(0, 'div', ['injectorDir', '', 'otherInjectorDir', '']);
}
// testing only
divElement = load(0);
}, 1, 0, [InjectorDir, OtherInjectorDir]);
const fixture = new ComponentFixture(App);
expect(injectorDir.injector.get(ElementRef).nativeElement).toBe(divElement);
expect(otherInjectorDir.injector.get(ElementRef).nativeElement).toBe(divElement);
expect(otherInjectorDir.injector.get(InjectorDir)).toBe(injectorDir);
expect(injectorDir.injector).not.toBe(otherInjectorDir.injector);
});
});
describe('ElementRef', () => {
it('should create directive with ElementRef dependencies', () => {
@ -1097,7 +1112,6 @@ describe('di', () => {
type: Directive,
selectors: [['', 'dir', '']],
factory: () => dir = new Directive(directiveInject(ElementRef)),
features: [PublicFeature],
exportAs: 'dir'
});
}
@ -1121,7 +1135,7 @@ describe('di', () => {
if (rf & RenderFlags.Create) {
elementStart(0, 'div', ['dir', '', 'dirSame', '']);
elementEnd();
div = getNativeByIndex(0, _getViewData());
div = getNativeByIndex(0, getViewData());
}
}, 1, 0, [Directive, DirectiveSameInstance]);
@ -1148,7 +1162,6 @@ describe('di', () => {
type: Directive,
selectors: [['', 'dir', '']],
factory: () => dir = new Directive(directiveInject(ElementRef)),
features: [PublicFeature],
exportAs: 'dir'
});
}
@ -1179,7 +1192,6 @@ describe('di', () => {
type: Directive,
selectors: [['', 'dir', '']],
factory: () => new Directive(directiveInject(TemplateRef as any)),
features: [PublicFeature],
exportAs: 'dir'
});
}
@ -1234,7 +1246,6 @@ describe('di', () => {
type: Directive,
selectors: [['', 'dir', '']],
factory: () => new Directive(directiveInject(ViewContainerRef as any)),
features: [PublicFeature],
exportAs: 'dir'
});
}
@ -1298,8 +1309,7 @@ describe('di', () => {
projectionDef();
projection(0);
}
},
features: [PublicFeature]
}
});
}
@ -1312,7 +1322,6 @@ describe('di', () => {
type: Directive,
selectors: [['', 'dir', '']],
factory: () => dir = new Directive(directiveInject(ChangeDetectorRef as any)),
features: [PublicFeature],
exportAs: 'dir'
});
}
@ -1324,8 +1333,7 @@ describe('di', () => {
type: DirectiveSameInstance,
selectors: [['', 'dirSame', '']],
factory: () => dirSameInstance =
new DirectiveSameInstance(directiveInject(ChangeDetectorRef as any)),
features: [PublicFeature]
new DirectiveSameInstance(directiveInject(ChangeDetectorRef as any))
});
}
@ -1421,8 +1429,7 @@ describe('di', () => {
textBinding(3, bind(tmp.value));
}
},
directives: directives,
features: [PublicFeature]
directives: directives
});
}
@ -1749,15 +1756,15 @@ describe('di', () => {
bloomAdd(0, mockTView, Dir198);
bloomAdd(0, mockTView, Dir231);
expect(injectorHasToken(bloomHash(Dir0) as number, 0, mockTView.data)).toEqual(true);
expect(injectorHasToken(bloomHash(Dir1) as number, 0, mockTView.data)).toEqual(false);
expect(injectorHasToken(bloomHash(Dir33) as number, 0, mockTView.data)).toEqual(true);
expect(injectorHasToken(bloomHash(Dir66) as number, 0, mockTView.data)).toEqual(true);
expect(injectorHasToken(bloomHash(Dir99) as number, 0, mockTView.data)).toEqual(true);
expect(injectorHasToken(bloomHash(Dir132) as number, 0, mockTView.data)).toEqual(true);
expect(injectorHasToken(bloomHash(Dir165) as number, 0, mockTView.data)).toEqual(true);
expect(injectorHasToken(bloomHash(Dir198) as number, 0, mockTView.data)).toEqual(true);
expect(injectorHasToken(bloomHash(Dir231) as number, 0, mockTView.data)).toEqual(true);
expect(bloomHasToken(bloomHash(Dir0) as number, 0, mockTView.data)).toEqual(true);
expect(bloomHasToken(bloomHash(Dir1) as number, 0, mockTView.data)).toEqual(false);
expect(bloomHasToken(bloomHash(Dir33) as number, 0, mockTView.data)).toEqual(true);
expect(bloomHasToken(bloomHash(Dir66) as number, 0, mockTView.data)).toEqual(true);
expect(bloomHasToken(bloomHash(Dir99) as number, 0, mockTView.data)).toEqual(true);
expect(bloomHasToken(bloomHash(Dir132) as number, 0, mockTView.data)).toEqual(true);
expect(bloomHasToken(bloomHash(Dir165) as number, 0, mockTView.data)).toEqual(true);
expect(bloomHasToken(bloomHash(Dir198) as number, 0, mockTView.data)).toEqual(true);
expect(bloomHasToken(bloomHash(Dir231) as number, 0, mockTView.data)).toEqual(true);
});
});
@ -1771,7 +1778,6 @@ describe('di', () => {
type: ChildDirective,
selectors: [['', 'childDir', '']],
factory: () => new ChildDirective(directiveInject(ParentDirective)),
features: [PublicFeature],
exportAs: 'childDir'
});
}
@ -1847,7 +1853,7 @@ describe('di', () => {
// so that we have smaller HelloWorld.
(parentTNode as{parent: any}).parent = undefined;
const injector: any = getOrCreateNodeInjector(); // TODO: Review use of `any` here (#19904)
const injector = getOrCreateNodeInjectorForNode(parentTNode, contentView);
expect(injector).not.toEqual(-1);
} finally {
leaveView(oldView);

View File

@ -7,7 +7,7 @@
*/
import {StaticInjector} from '../../src/di/injector';
import {getComponent, getDirectives, getHostComponent, getInjector, getLocalRefs, getRootComponents} from '../../src/render3/discovery_utils';
import {PublicFeature, RenderFlags, defineComponent, defineDirective} from '../../src/render3/index';
import {RenderFlags, defineComponent, defineDirective} from '../../src/render3/index';
import {element, elementEnd, elementStart, elementStyling, elementStylingApply} from '../../src/render3/instructions';
import {ComponentFixture} from './render_util';
@ -231,8 +231,7 @@ describe('discovery utils', () => {
factory: () => new Comp(),
consts: 0,
vars: 0,
template: (rf: RenderFlags, ctx: Comp) => {},
features: [PublicFeature]
template: (rf: RenderFlags, ctx: Comp) => {}
});
}
@ -252,8 +251,7 @@ describe('discovery utils', () => {
factory: () => new Comp(),
consts: 0,
vars: 0,
template: (rf: RenderFlags, ctx: Comp) => {},
features: [PublicFeature]
template: (rf: RenderFlags, ctx: Comp) => {}
});
}

View File

@ -7,14 +7,15 @@
*/
import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
import {RendererStyleFlags2, RendererType2} from '../../src/render/api';
import {AttributeMarker, defineComponent, defineDirective, templateRefExtractor} from '../../src/render3/index';
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, enableBindings, disableBindings, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, load, projection, projectionDef, reference, text, textBinding, template} from '../../src/render3/instructions';
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, load, projection, projectionDef, reference, text, textBinding, template, elementStylingMap, directiveInject} from '../../src/render3/instructions';
import {InitialStylingFlags, RenderFlags} from '../../src/render3/interfaces/definition';
import {RElement, Renderer3, RendererFactory3, domRendererFactory3, RText, RComment, RNode, RendererStyleFlags3, ProceduralRenderer3} from '../../src/render3/interfaces/renderer';
import {NO_CHANGE} from '../../src/render3/tokens';
import {HEADER_OFFSET, CONTEXT} from '../../src/render3/interfaces/view';
import {enableBindings, disableBindings} from '../../src/render3/state';
import {sanitizeUrl} from '../../src/sanitization/sanitization';
import {Sanitizer, SecurityContext} from '../../src/sanitization/security';
@ -23,7 +24,6 @@ import {ComponentFixture, TemplateFixture, createComponent, renderToHtml} from '
import {getContext} from '../../src/render3/context_discovery';
import {StylingIndex} from '../../src/render3/interfaces/styling';
import {MONKEY_PATCH_KEY_NAME} from '../../src/render3/interfaces/context';
import {directiveInject} from '../../src/render3/di';
describe('render3 integration test', () => {
@ -1627,6 +1627,62 @@ describe('render3 integration test', () => {
expect(fixture.html)
.toEqual('<structural-comp class="">Comp Content</structural-comp>Temp Content');
});
let mockClassDirective: DirWithClassDirective;
class DirWithClassDirective {
static ngDirectiveDef = defineDirective({
type: DirWithClassDirective,
selectors: [['', 'DirWithClass', '']],
factory: () => mockClassDirective = new DirWithClassDirective(),
inputs: {'klass': 'class'}
});
public classesVal: string = '';
set klass(value: string) { this.classesVal = value; }
}
it('should delegate all initial classes to a [class] input binding if present on a directive on the same element',
() => {
/**
* <my-comp class="apple orange banana" DirWithClass></my-comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
elementStart(0, 'div', ['DirWithClass']);
elementStyling([
InitialStylingFlags.VALUES_MODE, 'apple', true, 'orange', true, 'banana', true
]);
elementEnd();
}
if (rf & RenderFlags.Update) {
elementStylingApply(0);
}
}, 1, 0, [DirWithClassDirective]);
const fixture = new ComponentFixture(App);
expect(mockClassDirective !.classesVal).toEqual('apple orange banana');
});
it('should update `[class]` and bindings in the provided directive if the input is matched',
() => {
/**
* <my-comp DirWithClass></my-comp>
*/
const App = createComponent('app', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
elementStart(0, 'div', ['DirWithClass']);
elementStyling();
elementEnd();
}
if (rf & RenderFlags.Update) {
elementStylingMap(0, 'cucumber grape');
elementStylingApply(0);
}
}, 1, 0, [DirWithClassDirective]);
const fixture = new ComponentFixture(App);
expect(mockClassDirective !.classesVal).toEqual('cucumber grape');
});
});
});

View File

@ -10,7 +10,7 @@ import 'reflect-metadata';
import {InjectorDef, defineInjectable} from '@angular/core/src/di/defs';
import {Injectable} from '@angular/core/src/di/injectable';
import {inject, setCurrentInjector} from '@angular/core/src/di/injector';
import {inject, setCurrentInjector} from '@angular/core/src/di/injector_compatibility';
import {ivyEnabled} from '@angular/core/src/ivy_switch';
import {Component, HostBinding, HostListener, Input, Output, Pipe} from '@angular/core/src/metadata/directives';
import {NgModule, NgModuleDef} from '@angular/core/src/metadata/ng_module';
@ -239,7 +239,7 @@ ivyEnabled && describe('render3 jit', () => {
const pipeDef = (P as any).ngPipeDef as PipeDef<P>;
expect(pipeDef.name).toBe('test-pipe');
expect(pipeDef.pure).toBe(false, 'pipe should not be pure');
expect(pipeDef.factory() instanceof P)
expect(pipeDef.factory(null) instanceof P)
.toBe(true, 'factory() should create an instance of the pipe');
});

View File

@ -36,7 +36,7 @@ describe('NgOnChangesFeature', () => {
}
const myDir =
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).factory() as MyDirective;
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).factory(null) as MyDirective;
myDir.valA = 'first';
expect(myDir.valA).toEqual('first');
myDir.valB = 'second';
@ -89,7 +89,7 @@ describe('NgOnChangesFeature', () => {
}
const myDir =
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).factory() as SubDirective;
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).factory(null) as SubDirective;
myDir.valA = 'first';
expect(myDir.valA).toEqual('first');
@ -142,7 +142,7 @@ describe('NgOnChangesFeature', () => {
}
const myDir =
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).factory() as SubDirective;
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).factory(null) as SubDirective;
myDir.valA = 'first';
myDir.valB = 'second';
@ -183,7 +183,7 @@ describe('NgOnChangesFeature', () => {
}
const myDir =
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).factory() as SubDirective;
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).factory(null) as SubDirective;
myDir.valA = 'first';
myDir.valB = 'second';
@ -237,7 +237,7 @@ describe('NgOnChangesFeature', () => {
}
const myDir =
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).factory() as SubDirective;
(SubDirective.ngDirectiveDef as DirectiveDef<SubDirective>).factory(null) as SubDirective;
myDir.valA = 'first';
expect(myDir.valA).toEqual('first');
@ -279,7 +279,7 @@ describe('NgOnChangesFeature', () => {
}
const myDir =
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).factory() as MyDirective;
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).factory(null) as MyDirective;
myDir.valA = 'first';
myDir.valB = 'second';
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).doCheck !.call(myDir);
@ -315,7 +315,7 @@ describe('NgOnChangesFeature', () => {
}
const myDir =
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).factory() as MyDirective;
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).factory(null) as MyDirective;
myDir.onlySetter = 'someValue';
expect(myDir.onlySetter).toBeUndefined();
(MyDirective.ngDirectiveDef as DirectiveDef<MyDirective>).doCheck !.call(myDir);

View File

@ -11,15 +11,16 @@ import {AttributeMarker, TAttributes, TNode, TNodeType} from '../../src/render3/
import {CssSelector, CssSelectorList, NG_PROJECT_AS_ATTR_NAME, SelectorFlags,} from '../../src/render3/interfaces/projection';
import {getProjectAsAttrValue, isNodeMatchingSelectorList, isNodeMatchingSelector} from '../../src/render3/node_selector_matcher';
import {createTNode} from '@angular/core/src/render3/instructions';
import {getViewData} from '@angular/core/src/render3/state';
function testLStaticData(tagName: string, attrs: TAttributes | null): TNode {
return createTNode(TNodeType.Element, 0, tagName, attrs, null);
return createTNode(getViewData(), TNodeType.Element, 0, tagName, attrs, null);
}
describe('css selector matching', () => {
function isMatching(tagName: string, attrs: TAttributes | null, selector: CssSelector): boolean {
return isNodeMatchingSelector(
createTNode(TNodeType.Element, 0, tagName, attrs, null), selector);
createTNode(getViewData(), TNodeType.Element, 0, tagName, attrs, null), selector);
}
describe('isNodeMatchingSimpleSelector', () => {

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, InjectionToken, OnChanges, OnDestroy, Pipe, PipeTransform, createInjector, defineInjectable, defineInjector, ɵNgModuleDef as NgModuleDef, ɵPublicFeature as PublicFeature, ɵdefineComponent as defineComponent, ɵdirectiveInject as directiveInject} from '@angular/core';
import {Directive, InjectionToken, OnChanges, OnDestroy, Pipe, PipeTransform, createInjector, defineInjectable, defineInjector, ɵNgModuleDef as NgModuleDef, ɵdefineComponent as defineComponent, ɵdirectiveInject as directiveInject} from '@angular/core';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {defineDirective, definePipe} from '../../src/render3/definition';
@ -367,7 +367,6 @@ describe('pipe', () => {
static ngComponentDef = defineComponent({
type: MyComponent,
selectors: [['my-app']],
features: [PublicFeature],
factory: function MyComponent_Factory() { return new MyComponent(); },
consts: 2,
vars: 3,

View File

@ -8,9 +8,10 @@
import {EventEmitter} from '@angular/core';
import {AttributeMarker, PublicFeature, defineComponent, template, defineDirective} from '../../src/render3/index';
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, listener, load, reference, text, textBinding} from '../../src/render3/instructions';
import {AttributeMarker, defineComponent, template, defineDirective} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, listener, load, reference, text, textBinding} from '../../src/render3/instructions';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {NO_CHANGE} from '../../src/render3/tokens';
import {pureFunction1, pureFunction2} from '../../src/render3/pure_function';
import {ComponentFixture, TemplateFixture, createComponent, renderToHtml, createDirective} from './render_util';
@ -167,8 +168,7 @@ describe('elementProperty', () => {
hostVars: 1,
hostBindings: (directiveIndex: number, elementIndex: number) => {
elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
},
features: [PublicFeature]
}
});
}
@ -189,8 +189,7 @@ describe('elementProperty', () => {
const ctx = load(dirIndex) as HostBindingComp;
elementProperty(elIndex, 'title', bind(ctx.title));
},
template: (rf: RenderFlags, ctx: HostBindingComp) => {},
features: [PublicFeature]
template: (rf: RenderFlags, ctx: HostBindingComp) => {}
});
}
@ -230,8 +229,7 @@ describe('elementProperty', () => {
hostVars: 1,
hostBindings: (directiveIndex: number, elementIndex: number) => {
elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
},
features: [PublicFeature]
}
});
}
@ -271,8 +269,7 @@ describe('elementProperty', () => {
hostVars: 1,
hostBindings: (directiveIndex: number, elementIndex: number) => {
elementProperty(elementIndex, 'id', bind(load<HostBindingDir>(directiveIndex).id));
},
features: [PublicFeature]
}
});
}

View File

@ -10,13 +10,13 @@ import {NgForOfContext} from '@angular/common';
import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
import {EventEmitter} from '../..';
import {directiveInject} from '../../src/render3/di';
import {AttributeMarker, QueryList, defineComponent, defineDirective, detectChanges} from '../../src/render3/index';
import {getNativeByIndex} from '../../src/render3/util';
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadQueryList, reference, registerContentQuery, template, _getViewData} from '../../src/render3/instructions';
import {bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadQueryList, reference, registerContentQuery, template} from '../../src/render3/instructions';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {query, queryRefresh} from '../../src/render3/query';
import {getViewData} from '../../src/render3/state';
import {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
import {NgForOf, NgIf, NgTemplateOutlet} from './common_with_def';
@ -115,7 +115,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
element(1, 'div', ['child', '']);
elToQuery = getNativeByIndex(1, _getViewData());
elToQuery = getNativeByIndex(1, getViewData());
}
},
2, 0, [Child], [],
@ -222,7 +222,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
element(1, 'div', null, ['foo', '']);
elToQuery = getNativeByIndex(1, _getViewData());
elToQuery = getNativeByIndex(1, getViewData());
element(3, 'div');
}
},
@ -259,7 +259,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
element(2, 'div', null, ['foo', '', 'bar', '']);
elToQuery = getNativeByIndex(2, _getViewData());
elToQuery = getNativeByIndex(2, getViewData());
element(5, 'div');
}
},
@ -306,10 +306,10 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
element(1, 'div', null, ['foo', '']);
el1ToQuery = getNativeByIndex(1, _getViewData());
el1ToQuery = getNativeByIndex(1, getViewData());
element(3, 'div');
element(4, 'div', null, ['bar', '']);
el2ToQuery = getNativeByIndex(4, _getViewData());
el2ToQuery = getNativeByIndex(4, getViewData());
}
},
6, 0, [], [],
@ -345,7 +345,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
element(1, 'div', null, ['foo', '']);
elToQuery = getNativeByIndex(1, _getViewData());
elToQuery = getNativeByIndex(1, getViewData());
element(3, 'div');
}
},
@ -381,7 +381,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
elementContainerStart(1, null, ['foo', '']);
elToQuery = getNativeByIndex(1, _getViewData());
elToQuery = getNativeByIndex(1, getViewData());
elementContainerEnd();
}
},
@ -417,7 +417,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
elementContainerStart(1, null, ['foo', '']);
elToQuery = getNativeByIndex(1, _getViewData());
elToQuery = getNativeByIndex(1, getViewData());
elementContainerEnd();
}
},
@ -480,7 +480,7 @@ describe('query', () => {
elementContainerStart(2);
{
element(3, 'div', null, ['foo', '']);
elToQuery = getNativeByIndex(3, _getViewData());
elToQuery = getNativeByIndex(3, getViewData());
}
elementContainerEnd();
}
@ -890,7 +890,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
element(1, 'div', ['child', ''], ['foo', 'child']);
div = getNativeByIndex(1, _getViewData());
div = getNativeByIndex(1, getViewData());
}
},
3, 0, [Child], [],
@ -925,7 +925,7 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
element(1, 'div', ['child', ''], ['foo', '', 'bar', 'child']);
div = getNativeByIndex(1, _getViewData());
div = getNativeByIndex(1, getViewData());
}
if (rf & RenderFlags.Update) {
childInstance = getDirectiveOnNode(1);
@ -1409,7 +1409,7 @@ describe('query', () => {
{
if (rf1 & RenderFlags.Create) {
element(0, 'div', null, ['foo', '']);
firstEl = getNativeByIndex(0, _getViewData());
firstEl = getNativeByIndex(0, getViewData());
}
}
embeddedViewEnd();
@ -1461,10 +1461,10 @@ describe('query', () => {
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
element(1, 'span', null, ['foo', '']);
firstEl = getNativeByIndex(1, _getViewData());
firstEl = getNativeByIndex(1, getViewData());
container(3);
element(4, 'span', null, ['foo', '']);
lastEl = getNativeByIndex(4, _getViewData());
lastEl = getNativeByIndex(4, getViewData());
}
if (rf & RenderFlags.Update) {
containerRefreshStart(3);
@ -1474,7 +1474,7 @@ describe('query', () => {
{
if (rf1 & RenderFlags.Create) {
element(0, 'div', null, ['foo', '']);
viewEl = getNativeByIndex(0, _getViewData());
viewEl = getNativeByIndex(0, getViewData());
}
}
embeddedViewEnd();
@ -1541,7 +1541,7 @@ describe('query', () => {
{
if (rf0 & RenderFlags.Create) {
element(0, 'div', null, ['foo', '']);
firstEl = getNativeByIndex(0, _getViewData());
firstEl = getNativeByIndex(0, getViewData());
}
}
embeddedViewEnd();
@ -1551,7 +1551,7 @@ describe('query', () => {
{
if (rf1 & RenderFlags.Create) {
element(0, 'span', null, ['foo', '']);
lastEl = getNativeByIndex(0, _getViewData());
lastEl = getNativeByIndex(0, getViewData());
}
}
embeddedViewEnd();
@ -1614,7 +1614,7 @@ describe('query', () => {
{
if (rf0 & RenderFlags.Create) {
element(0, 'div', null, ['foo', '']);
firstEl = getNativeByIndex(0, _getViewData());
firstEl = getNativeByIndex(0, getViewData());
container(2);
}
if (rf0 & RenderFlags.Update) {
@ -1625,7 +1625,7 @@ describe('query', () => {
{
if (rf2) {
element(0, 'span', null, ['foo', '']);
lastEl = getNativeByIndex(0, _getViewData());
lastEl = getNativeByIndex(0, getViewData());
}
}
embeddedViewEnd();
@ -1911,7 +1911,8 @@ describe('query', () => {
type: WithContentDirective,
selectors: [['', 'with-content', '']],
factory: () => new WithContentDirective(),
contentQueries: () => { registerContentQuery(query(null, ['foo'], true)); },
contentQueries:
(dirIndex) => { registerContentQuery(query(null, ['foo'], true), dirIndex); },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
withContentInstance = load<WithContentDirective>(dirIndex);
@ -1932,7 +1933,8 @@ describe('query', () => {
template: function(rf: RenderFlags, ctx: any) {},
consts: 0,
vars: 0,
contentQueries: () => { registerContentQuery(query(null, ['foo'], false)); },
contentQueries:
(dirIndex) => { registerContentQuery(query(null, ['foo'], false), dirIndex); },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
shallowCompInstance = load<ShallowComp>(dirIndex);
@ -1971,6 +1973,51 @@ describe('query', () => {
`Expected content query results to be available when ngAfterContentChecked was called.`);
});
it('should support content queries for directives within repeated embedded views', () => {
/**
* % for (let i = 0; i < 3; i++) {
* <div with-content>
* <span #foo></span>
* </div>
* % }
*/
const AppComponent = createComponent('app-component', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
container(0);
}
if (rf & RenderFlags.Update) {
containerRefreshStart(0);
{
for (let i = 0; i < 3; i++) {
let rf = embeddedViewStart(1, 3, 0);
if (rf & RenderFlags.Create) {
elementStart(0, 'div', [AttributeMarker.SelectOnly, 'with-content']);
{ element(1, 'span', null, ['foo', '']); }
elementEnd();
}
embeddedViewEnd();
}
}
containerRefreshEnd();
}
}, 1, 0, [WithContentDirective]);
const fixture = new ComponentFixture(AppComponent);
expect(withContentInstance !.foos.length)
.toBe(1, `Expected content query to match <span #foo>.`);
expect(withContentInstance !.contentInitQuerySnapshot)
.toBe(
1,
`Expected content query results to be available when ngAfterContentInit was called.`);
expect(withContentInstance !.contentCheckedQuerySnapshot)
.toBe(
1,
`Expected content query results to be available when ngAfterContentChecked was called.`);
});
it('should support content query matches on directive hosts', () => {
/**
* <div with-content #foo>
@ -2109,10 +2156,10 @@ describe('query', () => {
selectors: [['', 'query', '']],
exportAs: 'query',
factory: () => new QueryDirective(),
contentQueries: () => {
contentQueries: (dirIndex) => {
// @ContentChildren('foo, bar, baz', {descendants: true}) fooBars:
// QueryList<ElementRef>;
registerContentQuery(query(null, ['foo', 'bar', 'baz'], true));
registerContentQuery(query(null, ['foo', 'bar', 'baz'], true), dirIndex);
},
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
@ -2173,10 +2220,10 @@ describe('query', () => {
selectors: [['', 'query', '']],
exportAs: 'query',
factory: () => new QueryDirective(),
contentQueries: () => {
contentQueries: (dirIndex) => {
// @ContentChildren('foo, bar, baz', {descendants: true}) fooBars:
// QueryList<ElementRef>;
registerContentQuery(query(null, ['foo'], false));
registerContentQuery(query(null, ['foo'], false), dirIndex);
},
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
@ -2227,9 +2274,9 @@ describe('query', () => {
selectors: [['', 'shallow-query', '']],
exportAs: 'shallow-query',
factory: () => new ShallowQueryDirective(),
contentQueries: () => {
contentQueries: (dirIndex) => {
// @ContentChildren('foo', {descendants: false}) foos: QueryList<ElementRef>;
registerContentQuery(query(null, ['foo'], false));
registerContentQuery(query(null, ['foo'], false), dirIndex);
},
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
@ -2247,9 +2294,9 @@ describe('query', () => {
selectors: [['', 'deep-query', '']],
exportAs: 'deep-query',
factory: () => new DeepQueryDirective(),
contentQueries: () => {
contentQueries: (dirIndex) => {
// @ContentChildren('foo', {descendants: false}) foos: QueryList<ElementRef>;
registerContentQuery(query(null, ['foo'], true));
registerContentQuery(query(null, ['foo'], true), dirIndex);
},
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;

View File

@ -14,7 +14,7 @@ import {Renderer2} from '@angular/core/src/render/api';
import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util';
import {SWITCH_CHANGE_DETECTOR_REF_FACTORY__POST_R3__ as R3_CHANGE_DETECTOR_REF_FACTORY} from '../../src/change_detection/change_detector_ref';
import {Injector} from '../../src/di/injector';
import {Injector, SWITCH_INJECTOR_FACTORY__POST_R3__ as R3_INJECTOR_FACTORY} from '../../src/di/injector';
import {SWITCH_ELEMENT_REF_FACTORY__POST_R3__ as R3_ELEMENT_REF_FACTORY} from '../../src/linker/element_ref';
import {SWITCH_TEMPLATE_REF_FACTORY__POST_R3__ as R3_TEMPLATE_REF_FACTORY} from '../../src/linker/template_ref';
import {SWITCH_VIEW_CONTAINER_REF_FACTORY__POST_R3__ as R3_VIEW_CONTAINER_REF_FACTORY} from '../../src/linker/view_container_ref';
@ -23,12 +23,13 @@ import {CreateComponentOptions} from '../../src/render3/component';
import {discoverDirectives, getContext, isComponentInstance} from '../../src/render3/context_discovery';
import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition';
import {NG_ELEMENT_ID} from '../../src/render3/fields';
import {ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, PublicFeature, RenderFlags, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index';
import {_getViewData, renderTemplate} from '../../src/render3/instructions';
import {ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, RenderFlags, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index';
import {renderTemplate} from '../../src/render3/instructions';
import {DirectiveDefList, DirectiveTypesOrFactory, PipeDef, PipeDefList, PipeTypesOrFactory} from '../../src/render3/interfaces/definition';
import {PlayerHandler} from '../../src/render3/interfaces/player';
import {RElement, RText, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer';
import {HEADER_OFFSET, LViewData} from '../../src/render3/interfaces/view';
import {getViewData} from '../../src/render3/state';
import {Sanitizer} from '../../src/sanitization/security';
import {Type} from '../../src/type';
@ -275,7 +276,6 @@ export function createComponent(
factory: () => new Component,
template: template,
viewQuery: viewQuery,
features: [PublicFeature],
directives: directives,
pipes: pipes
});
@ -289,7 +289,6 @@ export function createDirective(
type: Directive,
selectors: [['', name, '']],
factory: () => new Directive(),
features: [PublicFeature],
exportAs: exportAs,
});
};
@ -297,7 +296,7 @@ export function createDirective(
/** Gets the directive on the given node at the given index */
export function getDirectiveOnNode(nodeIndex: number, dirIndex: number = 0) {
const directives = discoverDirectives(nodeIndex + HEADER_OFFSET, _getViewData(), true);
const directives = discoverDirectives(nodeIndex + HEADER_OFFSET, getViewData(), true);
if (directives == null) {
throw new Error(`No directives exist on node in slot ${nodeIndex}`);
}
@ -323,4 +322,5 @@ export function enableIvyInjectableFactories() {
R3_VIEW_CONTAINER_REF_FACTORY(ViewContainerRef, ElementRef);
(ChangeDetectorRef as any)[NG_ELEMENT_ID] = () => R3_CHANGE_DETECTOR_REF_FACTORY();
(Renderer2 as any)[NG_ELEMENT_ID] = () => R3_RENDERER2_FACTORY();
(Injector as any)[NG_ELEMENT_ID] = () => R3_INJECTOR_FACTORY();
}

View File

@ -8,10 +8,9 @@
import {Component, ComponentFactoryResolver, ElementRef, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core';
import {ViewEncapsulation} from '../../src/metadata';
import {directiveInject} from '../../src/render3/di';
import {AttributeMarker, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, load} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, nextContext, projection, projectionDef, reference, template, text, textBinding} from '../../src/render3/instructions';
import {bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, nextContext, projection, projectionDef, reference, template, text, textBinding} from '../../src/render3/instructions';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {templateRefExtractor} from '../../src/render3/view_engine_compatibility_prebound';
import {NgModuleFactory} from '../../src/render3/ng_module_ref';

Some files were not shown because too many files have changed in this diff Show More