diff --git a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts
index 6ddae7974d..3435abc781 100644
--- a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts
+++ b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {InitialStylingFlags} from '@angular/compiler/src/core';
+import {AttributeMarker, InitialStylingFlags} from '@angular/compiler/src/core';
import {setup} from '@angular/compiler/test/aot/test_util';
import {compile, expectEmit} from './mock_compile';
@@ -200,7 +200,7 @@ describe('compiler compliance', () => {
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
- const $c1$ = ["class", "my-app", 0, "http://someuri/foo", "foo:bar", "baz", "title", "Hello", 0, "http://someuri/foo", "foo:qux", "quacks"];
+ const $e0_attrs$ = ["class", "my-app", 0, "http://someuri/foo", "foo:bar", "baz", "title", "Hello", 0, "http://someuri/foo", "foo:qux", "quacks"];
…
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
@@ -315,9 +315,11 @@ describe('compiler compliance', () => {
const factory =
'factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
const template = `
+ const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "id"];
+ …
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
- $r3$.ɵelement(0, "div");
+ $r3$.ɵelement(0, "div", $e0_attrs$);
}
if (rf & 2) {
$r3$.ɵelementProperty(0, "id", $r3$.ɵbind(ctx.id));
@@ -357,19 +359,20 @@ describe('compiler compliance', () => {
}
};
+ const $e0_attrs$ = [];
const factory =
'factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
const template = `
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
- $r3$.ɵelement(0, "div");
+ $r3$.ɵelement(0, "div", $e0_attrs$);
$r3$.ɵpipe(1,"pipe");
}
if (rf & 2) {
- $r3$.ɵelementProperty(0, "ternary", $r3$.ɵbind((ctx.cond ? $r3$.ɵpureFunction1(8, _c0, ctx.a): _c1)));
+ $r3$.ɵelementProperty(0, "ternary", $r3$.ɵbind((ctx.cond ? $r3$.ɵpureFunction1(8, $c0$, ctx.a): $c1$)));
$r3$.ɵelementProperty(0, "pipe", $r3$.ɵbind($r3$.ɵpipeBind3(1, 4, ctx.value, 1, 2)));
- $r3$.ɵelementProperty(0, "and", $r3$.ɵbind((ctx.cond && $r3$.ɵpureFunction1(10, _c0, ctx.b))));
- $r3$.ɵelementProperty(0, "or", $r3$.ɵbind((ctx.cond || $r3$.ɵpureFunction1(12, _c0, ctx.c))));
+ $r3$.ɵelementProperty(0, "and", $r3$.ɵbind((ctx.cond && $r3$.ɵpureFunction1(10, $c0$, ctx.b))));
+ $r3$.ɵelementProperty(0, "or", $r3$.ɵbind((ctx.cond || $r3$.ɵpureFunction1(12, $c0$, ctx.c))));
}
}
`;
@@ -705,6 +708,7 @@ describe('compiler compliance', () => {
};
const MyAppDeclaration = `
+ const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "names"];
const $e0_ff$ = function ($v$) { return ["Nancy", $v$]; };
…
MyApp.ngComponentDef = $r3$.ɵdefineComponent({
@@ -716,7 +720,7 @@ describe('compiler compliance', () => {
vars: 3,
template: function MyApp_Template(rf, ctx) {
if (rf & 1) {
- $r3$.ɵelement(0, "my-comp");
+ $r3$.ɵelement(0, "my-comp", $e0_attrs$);
}
if (rf & 2) {
$r3$.ɵelementProperty(0, "names", $r3$.ɵbind($r3$.ɵpureFunction1(1, $e0_ff$, ctx.customName)));
@@ -784,6 +788,7 @@ describe('compiler compliance', () => {
};
const MyAppDefinition = `
+ const $e0_attr$ = [${AttributeMarker.SelectOnly}, "names"];
const $e0_ff$ = function ($v0$, $v1$, $v2$, $v3$, $v4$, $v5$, $v6$, $v7$, $v8$) {
return ["start-", $v0$, $v1$, $v2$, $v3$, $v4$, "-middle-", $v5$, $v6$, $v7$, $v8$, "-end"];
}
@@ -797,7 +802,7 @@ describe('compiler compliance', () => {
vars: 11,
template: function MyApp_Template(rf, ctx) {
if (rf & 1) {
- $r3$.ɵelement(0, "my-comp");
+ $r3$.ɵelement(0, "my-comp", $e0_attr$);
}
if (rf & 2) {
$r3$.ɵelementProperty(
@@ -849,6 +854,7 @@ describe('compiler compliance', () => {
};
const MyAppDefinition = `
+ const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "config"];
const $e0_ff$ = function ($v$) { return {"duration": 500, animation: $v$}; };
…
MyApp.ngComponentDef = $r3$.ɵdefineComponent({
@@ -860,7 +866,7 @@ describe('compiler compliance', () => {
vars: 3,
template: function MyApp_Template(rf, ctx) {
if (rf & 1) {
- $r3$.ɵelement(0, "object-comp");
+ $r3$.ɵelement(0, "object-comp", $e0_attrs$);
}
if (rf & 2) {
$r3$.ɵelementProperty(0, "config", $r3$.ɵbind($r3$.ɵpureFunction1(1, $e0_ff$, ctx.name)));
@@ -913,6 +919,7 @@ describe('compiler compliance', () => {
};
const MyAppDefinition = `
+ const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "config"];
const $c0$ = {opacity: 0, duration: 0};
const $e0_ff$ = function ($v$) { return {opacity: 1, duration: $v$}; };
const $e0_ff_1$ = function ($v$) { return [$c0$, $v$]; };
@@ -927,7 +934,7 @@ describe('compiler compliance', () => {
vars: 8,
template: function MyApp_Template(rf, ctx) {
if (rf & 1) {
- $r3$.ɵelement(0, "nested-comp");
+ $r3$.ɵelement(0, "nested-comp", $e0_attrs$);
}
if (rf & 2) {
$r3$.ɵelementProperty(
@@ -1433,9 +1440,9 @@ describe('compiler compliance', () => {
};
const template = `
- const $c0$ = ["ngFor","","ngForOf",""];
+ const $c0$ = ["ngFor", "" , ${AttributeMarker.SelectOnly}, "ngForOf"];
const $c1$ = ["foo", ""];
- const $c2$ = ["ngIf",""];
+ const $c2$ = [${AttributeMarker.SelectOnly}, "ngIf"];
function MyComponent_div_span_Template_3(rf, ctx) {
if (rf & 1) {
@@ -1545,8 +1552,8 @@ describe('compiler compliance', () => {
vars: 2,
template: function SimpleLayout_Template(rf, ctx) {
if (rf & 1) {
- $r3$.ɵelement(0, "lifecycle-comp");
- $r3$.ɵelement(1, "lifecycle-comp");
+ $r3$.ɵelement(0, "lifecycle-comp", $e0_attrs$);
+ $r3$.ɵelement(1, "lifecycle-comp", $e1_attrs$);
}
if (rf & 2) {
$r3$.ɵelementProperty(0, "name", $r3$.ɵbind(ctx.name1));
@@ -1656,7 +1663,7 @@ describe('compiler compliance', () => {
`;
const MyComponentDefinition = `
- const $_c0$ = ["for","","forOf",""];
+ const $t1_attrs$ = ["for", "", ${AttributeMarker.SelectOnly}, "forOf"];
function MyComponent__svg_g_Template_1(rf, ctx) {
if (rf & 1) {
$r3$.ɵnamespaceSVG();
@@ -1677,7 +1684,7 @@ describe('compiler compliance', () => {
if (rf & 1) {
$r3$.ɵnamespaceSVG();
$r3$.ɵelementStart(0,"svg");
- $r3$.ɵtemplate(1, MyComponent__svg_g_Template_1, 2, 0, null, $_c0$);
+ $r3$.ɵtemplate(1, MyComponent__svg_g_Template_1, 2, 0, null, $t1_attrs$);
$r3$.ɵelementEnd();
}
if (rf & 2) { $r3$.ɵelementProperty(1,"forOf",$r3$.ɵbind(ctx.items)); }
@@ -1732,7 +1739,7 @@ describe('compiler compliance', () => {
`;
const MyComponentDefinition = `
- const $_c0$ = ["for","","forOf",""];
+ const $t1_attrs$ = ["for", "", ${AttributeMarker.SelectOnly}, "forOf"];
function MyComponent_li_Template_1(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "li");
@@ -1755,7 +1762,7 @@ describe('compiler compliance', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "ul");
- $r3$.ɵtemplate(1, MyComponent_li_Template_1, 2, 1, null, $_c0$);
+ $r3$.ɵtemplate(1, MyComponent_li_Template_1, 2, 1, null, $t1_attrs$);
$r3$.ɵelementEnd();
}
if (rf & 2) {
@@ -1812,7 +1819,7 @@ describe('compiler compliance', () => {
};
const MyComponentDefinition = `
- const $c1$ = ["for", "", "forOf", ""];
+ const $t4_attrs$ = ["for", "", ${AttributeMarker.SelectOnly}, "forOf"];
function MyComponent_li_li_Template_4(rf, ctx) {
if (rf & 1) {
$r3$.ɵelementStart(0, "li");
@@ -1833,7 +1840,7 @@ describe('compiler compliance', () => {
$r3$.ɵtext(2);
$r3$.ɵelementEnd();
$r3$.ɵelementStart(3, "ul");
- $r3$.ɵtemplate(4, MyComponent_li_li_Template_4, 2, 2, null, $c1$);
+ $r3$.ɵtemplate(4, MyComponent_li_li_Template_4, 2, 2, null, $t4_attrs$);
$r3$.ɵelementEnd();
$r3$.ɵelementEnd();
}
diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts
index 43235bbc61..24923b575f 100644
--- a/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts
+++ b/packages/compiler-cli/test/compliance/r3_view_compiler_binding_spec.ts
@@ -5,7 +5,7 @@
* 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 {AttributeMarker} from '@angular/compiler/src/core';
import {MockDirectory, setup} from '@angular/compiler/test/aot/test_util';
import {compile, expectEmit} from './mock_compile';
@@ -73,9 +73,11 @@ describe('compiler compliance: bindings', () => {
};
const template = `
+ const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "title"];
+ …
template:function MyComponent_Template(rf, $ctx$){
if (rf & 1) {
- $i0$.ɵelement(0, "a");
+ $i0$.ɵelement(0, "a", $e0_attrs$);
}
if (rf & 2) {
$i0$.ɵelementProperty(0, "title", $i0$.ɵbind($ctx$.title));
@@ -105,9 +107,11 @@ describe('compiler compliance: bindings', () => {
};
const template = `
+ const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "title"];
+ …
template:function MyComponent_Template(rf, $ctx$){
if (rf & 1) {
- $i0$.ɵelement(0, "a");
+ $i0$.ɵelement(0, "a", $e0_attrs$);
}
if (rf & 2) {
$i0$.ɵelementProperty(0, "title", $i0$.ɵinterpolation1("Hello ", $ctx$.name, ""));
diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_directives_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_directives_spec.ts
new file mode 100644
index 0000000000..26e650aa57
--- /dev/null
+++ b/packages/compiler-cli/test/compliance/r3_view_compiler_directives_spec.ts
@@ -0,0 +1,299 @@
+/**
+ * @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 {AttributeMarker} from '@angular/compiler/src/core';
+import {setup} from '@angular/compiler/test/aot/test_util';
+import {compile, expectEmit} from './mock_compile';
+
+describe('compiler compliance: directives', () => {
+
+ const angularFiles = setup({
+ compileAngular: false,
+ compileAnimations: false,
+ compileFakeCore: true,
+ });
+
+ describe('matching', () => {
+
+ it('should not match directives on i18n attribute', () => {
+ const files = {
+ app: {
+ 'spec.ts': `
+ import {Component, Directive, Input, NgModule} from '@angular/core';
+
+ @Directive({selector: '[i18n]'})
+ export class I18nDirective {}
+
+ @Component({selector: 'my-component', template: '
'})
+ export class MyComponent {}
+
+ @NgModule({declarations: [I18nDirective, MyComponent]})
+ export class MyModule{}`
+ }
+ };
+
+ // MyComponent definition should be:
+ const MyComponentDefinition = `
+ MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
+ 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) {
+ if (rf & 1) {
+ $r3$.ɵelement(0, "div");
+ }
+ }
+ });
+ `;
+
+ const result = compile(files, angularFiles);
+ const source = result.source;
+
+ expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');
+ });
+
+ it('should not match directives on i18n-prefixed attributes', () => {
+ const files = {
+ app: {
+ 'spec.ts': `
+ import {Component, Directive, Input, NgModule} from '@angular/core';
+
+ @Directive({selector: '[i18n]'})
+ export class I18nDirective {}
+
+ @Directive({selector: '[i18n-foo]'})
+ export class I18nFooDirective {}
+
+ @Directive({selector: '[foo]'})
+ export class FooDirective {}
+
+ @Component({selector: 'my-component', template: ''})
+ export class MyComponent {}
+
+ @NgModule({declarations: [I18nDirective, I18nFooDirective, FooDirective, MyComponent]})
+ export class MyModule{}`
+ }
+ };
+
+ // MyComponent definition should be:
+ const MyComponentDefinition = `
+ MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
+ 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) {
+ if (rf & 1) {
+ $r3$.ɵelement(0, "div");
+ }
+ }
+ });
+ `;
+
+ const result = compile(files, angularFiles);
+ const source = result.source;
+
+ expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');
+ });
+
+ it('should match directives on element bindings', () => {
+
+ const files = {
+ app: {
+ 'spec.ts': `
+ import {Component, Directive, Input, NgModule} from '@angular/core';
+
+ @Directive({selector: '[someDirective]'})
+ export class SomeDirective {
+ @Input() someDirective;
+ }
+
+ @Component({selector: 'my-component', template: ''})
+ export class MyComponent {}
+
+ @NgModule({declarations: [SomeDirective, MyComponent]})
+ export class MyModule{}
+ `
+ }
+ };
+
+
+ // MyComponent definition should be:
+ const MyComponentDefinition = `
+ …
+ const _c0 = [${AttributeMarker.SelectOnly}, "someDirective"];
+ …
+ MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
+ …
+ template: function MyComponent_Template(rf, ctx) {
+ if (rf & 1) {
+ $r3$.ɵelement(0, "div", _c0);
+ }
+ if (rf & 2) {
+ $r3$.ɵelementProperty(0, "someDirective", $r3$.ɵbind(true));
+ }
+ },
+ …
+ directives: [SomeDirective]
+ });
+ `;
+
+ const result = compile(files, angularFiles);
+ const source = result.source;
+
+ expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');
+ });
+
+ it('should match directives on ng-template bindings', () => {
+
+ const files = {
+ app: {
+ 'spec.ts': `
+ import {Component, Directive, Input, NgModule} from '@angular/core';
+
+ @Directive({selector: '[someDirective]'})
+ export class SomeDirective {
+ @Input() someDirective;
+ }
+
+ @Component({selector: 'my-component', template: ''})
+ export class MyComponent {}
+
+ @NgModule({declarations: [SomeDirective, MyComponent]})
+ export class MyModule{}
+ `
+ }
+ };
+
+
+ // MyComponent definition should be:
+ const MyComponentDefinition = `
+ …
+ const $c0_a0$ = [${AttributeMarker.SelectOnly}, "someDirective"];
+ …
+ MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
+ …
+ template: function MyComponent_Template(rf, ctx) {
+ if (rf & 1) {
+ $r3$.ɵtemplate(0, Template_0, 0, 0, null, $c0_a0$);
+ }
+ if (rf & 2) {
+ $r3$.ɵelementProperty(0, "someDirective", $r3$.ɵbind(true));
+ }
+ },
+ …
+ directives: [SomeDirective]
+ });
+ `;
+
+ const result = compile(files, angularFiles);
+ const source = result.source;
+
+ expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');
+ });
+
+ it('should match structural directives', () => {
+
+ const files = {
+ app: {
+ 'spec.ts': `
+ import {Component, Directive, Input, NgModule} from '@angular/core';
+
+ @Directive({selector: '[someDirective]'})
+ export class SomeDirective {
+ @Input() someDirective;
+ }
+
+ @Component({selector: 'my-component', template: ''})
+ export class MyComponent {}
+
+ @NgModule({declarations: [SomeDirective, MyComponent]})
+ export class MyModule{}
+ `
+ }
+ };
+
+ // MyComponent definition should be:
+ const MyComponentDefinition = `
+ …
+ const $c0_a0$ = ["someDirective", ""];
+ …
+ MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
+ …
+ template: function MyComponent_Template(rf, ctx) {
+ if (rf & 1) {
+ $r3$.ɵtemplate(0, MyComponent_div_Template_0, 1, 0, null, $c0_a0$);
+ }
+ },
+ …
+ directives: [SomeDirective]
+ });
+ `;
+
+ const result = compile(files, angularFiles);
+ const source = result.source;
+
+ expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');
+
+ });
+
+ it('should match directives on element outputs', () => {
+
+ const files = {
+ app: {
+ 'spec.ts': `
+ import {Component, Directive, Output, EventEmitter, NgModule} from '@angular/core';
+
+ @Directive({selector: '[someDirective]'})
+ export class SomeDirective {
+ @Output() someDirective = new EventEmitter();
+ }
+
+ @Component({selector: 'my-component', template: ''})
+ export class MyComponent {
+ noop() {}
+ }
+
+ @NgModule({declarations: [SomeDirective, MyComponent]})
+ export class MyModule{}
+ `
+ }
+ };
+
+
+ // MyComponent definition should be:
+ const MyComponentDefinition = `
+ …
+ const $c0_a0$ = [${AttributeMarker.SelectOnly}, "someDirective"];
+ …
+ MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
+ …
+ template: function MyComponent_Template(rf, ctx) {
+ if (rf & 1) {
+ $r3$.ɵelementStart(0, "div", $c0_a0$);
+ $r3$.ɵlistener("someDirective", function MyComponent_Template_div_someDirective_listener($event) { return ctx.noop(); });
+ $r3$.ɵelementEnd();
+ }
+ },
+ …
+ directives: [SomeDirective]
+ });
+ `;
+
+ const result = compile(files, angularFiles);
+ const source = result.source;
+
+ expectEmit(source, MyComponentDefinition, 'Incorrect ChildComponent.ngComponentDef');
+ });
+
+ });
+});
\ No newline at end of file
diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_listener_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_listener_spec.ts
index 582627cec7..2b4bb092ab 100644
--- a/packages/compiler-cli/test/compliance/r3_view_compiler_listener_spec.ts
+++ b/packages/compiler-cli/test/compliance/r3_view_compiler_listener_spec.ts
@@ -5,8 +5,8 @@
* 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 {MockDirectory, setup} from '@angular/compiler/test/aot/test_util';
+import {AttributeMarker} from '@angular/compiler/src/core';
+import {setup} from '@angular/compiler/test/aot/test_util';
import {compile, expectEmit} from './mock_compile';
/* These tests are codified version of the tests in compiler_canonical_spec.ts. Every
@@ -41,9 +41,11 @@ describe('compiler compliance: listen()', () => {
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
+ const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "click"];
+ …
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
- $r3$.ɵelementStart(0, "div");
+ $r3$.ɵelementStart(0, "div", $e0_attrs$);
$r3$.ɵlistener("click", function MyComponent_Template_div_click_listener($event) {
ctx.onClick($event);
return (1 == 2);
@@ -86,20 +88,21 @@ describe('compiler compliance: listen()', () => {
};
const template = `
- const $c0$ = ["ngIf",""];
+ const $t0_attrs$ = [${AttributeMarker.SelectOnly}, "ngIf"];
+ const $e_attrs$ = [${AttributeMarker.SelectOnly}, "click"];
function MyComponent_div_Template_0(rf, ctx) {
if (rf & 1) {
const $s$ = $r3$.ɵgetCurrentView();
$r3$.ɵelementStart(0, "div");
- $r3$.ɵelementStart(1, "div");
+ $r3$.ɵelementStart(1, "div", $e_attrs$);
$r3$.ɵlistener("click", function MyComponent_div_Template_0_div_click_listener($event) {
$r3$.ɵrestoreView($s$);
const $comp$ = $r3$.ɵnextContext();
return $comp$.onClick($comp$.foo);
});
$r3$.ɵelementEnd();
- $r3$.ɵelementStart(2, "button");
+ $r3$.ɵelementStart(2, "button", $e_attrs$);
$r3$.ɵlistener("click", function MyComponent_div_Template_0_button_click_listener($event) {
$r3$.ɵrestoreView($s$);
const $comp2$ = $r3$.ɵnextContext();
@@ -147,7 +150,8 @@ describe('compiler compliance: listen()', () => {
};
const MyComponentDefinition = `
- const $c0$ = ["user", ""];
+ const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "click"];
+ const $e2_refs$ = ["user", ""];
…
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
@@ -158,14 +162,14 @@ describe('compiler compliance: listen()', () => {
vars: 0,
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
- $r3$.ɵelementStart(0, "button");
+ $r3$.ɵelementStart(0, "button", $e0_attrs$);
$r3$.ɵlistener("click", function MyComponent_Template_button_click_listener($event) {
const $user$ = $r3$.ɵreference(3);
return ctx.onClick($user$.value);
});
$r3$.ɵtext(1, "Save");
$r3$.ɵelementEnd();
- $r3$.ɵelement(2, "input", null, $c0$);
+ $r3$.ɵelement(2, "input", null, $e2_refs$);
}
}
});
diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts
index 09726efddb..816c796dd2 100644
--- a/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts
+++ b/packages/compiler-cli/test/compliance/r3_view_compiler_styling_spec.ts
@@ -6,9 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {InitialStylingFlags, ViewEncapsulation} from '@angular/compiler/src/core';
-import {MockDirectory, setup} from '@angular/compiler/test/aot/test_util';
-
+import {AttributeMarker, InitialStylingFlags, ViewEncapsulation} from '@angular/compiler/src/core';
+import {setup} from '@angular/compiler/test/aot/test_util';
import {compile, expectEmit} from './mock_compile';
describe('compiler compliance: styling', () => {
@@ -168,7 +167,8 @@ describe('compiler compliance: styling', () => {
};
const template = `
- const _c0 = ["opacity","width","height",${InitialStylingFlags.VALUES_MODE},"opacity","1"];
+ const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "style"];
+ const $e0_styling$ = ["opacity","width","height",${InitialStylingFlags.VALUES_MODE},"opacity","1"];
…
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
@@ -181,8 +181,8 @@ describe('compiler compliance: styling', () => {
vars: 1,
template: function MyComponent_Template(rf, $ctx$) {
if (rf & 1) {
- $r3$.ɵelementStart(0, "div");
- $r3$.ɵelementStyling(null, _c0, $r3$.ɵzss);
+ $r3$.ɵelementStart(0, "div", $e0_attrs$);
+ $r3$.ɵelementStyling(null, $e0_styling$, $r3$.ɵzss);
$r3$.ɵelementEnd();
}
if (rf & 2) {
@@ -324,7 +324,8 @@ describe('compiler compliance: styling', () => {
};
const template = `
- const _c0 = ["grape","apple","orange",${InitialStylingFlags.VALUES_MODE},"grape",true];
+ const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "class"];
+ const $e0_cd$ = ["grape","apple","orange",${InitialStylingFlags.VALUES_MODE},"grape",true];
…
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
@@ -337,8 +338,8 @@ describe('compiler compliance: styling', () => {
vars: 1,
template: function MyComponent_Template(rf, $ctx$) {
if (rf & 1) {
- $r3$.ɵelementStart(0, "div");
- $r3$.ɵelementStyling(_c0);
+ $r3$.ɵelementStart(0, "div", $e0_attrs$);
+ $r3$.ɵelementStyling($e0_cd$);
$r3$.ɵelementEnd();
}
if (rf & 2) {
@@ -379,8 +380,9 @@ describe('compiler compliance: styling', () => {
};
const template = `
- const _c0 = ["foo",${InitialStylingFlags.VALUES_MODE},"foo",true];
- const _c1 = ["width",${InitialStylingFlags.VALUES_MODE},"width","100px"];
+ const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "class", "style"];
+ const $e0_cd$ = ["foo",${InitialStylingFlags.VALUES_MODE},"foo",true];
+ const $e0_sd$ = ["width",${InitialStylingFlags.VALUES_MODE},"width","100px"];
…
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
type: MyComponent,
@@ -393,8 +395,8 @@ describe('compiler compliance: styling', () => {
vars: 2,
template: function MyComponent_Template(rf, $ctx$) {
if (rf & 1) {
- $r3$.ɵelementStart(0, "div");
- $r3$.ɵelementStyling(_c0, _c1);
+ $r3$.ɵelementStart(0, "div", $e0_attrs$);
+ $r3$.ɵelementStyling($e0_cd$, $e0_sd$);
$r3$.ɵelementEnd();
}
if (rf & 2) {
diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts
index 45623362ab..6e7b5e795c 100644
--- a/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts
+++ b/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts
@@ -5,7 +5,7 @@
* 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 {AttributeMarker} from '@angular/compiler/src/core';
import {setup} from '@angular/compiler/test/aot/test_util';
import {compile, expectEmit} from './mock_compile';
@@ -50,12 +50,13 @@ describe('compiler compliance: template', () => {
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
- const $c0$ = ["ngFor","","ngForOf",""];
+ const $c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
+ const $e0_attrs$ = [${AttributeMarker.SelectOnly}, "title", "click"];
function MyComponent_ul_li_div_Template_1(rf, ctx) {
if (rf & 1) {
const $s$ = $i0$.ɵgetCurrentView();
- $i0$.ɵelementStart(0, "div");
+ $i0$.ɵelementStart(0, "div", $e0_attrs$);
$i0$.ɵlistener("click", function MyComponent_ul_li_div_Template_1_div_click_listener($event){
$i0$.ɵrestoreView($s$);
const $inner$ = ctx.$implicit;
@@ -138,7 +139,7 @@ describe('compiler compliance: template', () => {
};
const template = `
- const $c0$ = ["ngFor", "", "ngForOf", ""];
+ const $c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
function MyComponent_span_Template_0(rf, ctx) {
if (rf & 1) {
@@ -191,8 +192,8 @@ describe('compiler compliance: template', () => {
};
const template = `
- const $c0$ = ["ngFor", "", "ngForOf", ""];
- const $c1$ = ["ngIf", ""];
+ const $c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
+ const $c1$ = [${AttributeMarker.SelectOnly}, "ngIf"];
function MyComponent_div_span_Template_1(rf, ctx) {
if (rf & 1) {
@@ -262,7 +263,7 @@ describe('compiler compliance: template', () => {
// The template should look like this (where IDENT is a wild card for an identifier):
const template = `
- const $c0$ = ["ngFor", "", "ngForOf", ""];
+ const $c0$ = ["ngFor", "", ${AttributeMarker.SelectOnly}, "ngForOf"];
function MyComponent_div_div_div_Template_1(rf, ctx) {
if (rf & 1) {
$i0$.ɵelementStart(0, "div");
@@ -336,7 +337,7 @@ describe('compiler compliance: template', () => {
};
const template = `
- const $c0$ = ["attr", "", "boundAttr", ""];
+ const $c0$ = ["attr", "l", ${AttributeMarker.SelectOnly}, "boundAttr"];
function Template_0(rf, ctx) {
if (rf & 1) {
@@ -380,7 +381,7 @@ describe('compiler compliance: template', () => {
};
const template = `
- const _c0 = ["foo", ""];
+ const $t0_refs$ = ["foo", ""];
function Template_0(rf, ctx) {
if (rf & 1) {
@@ -392,7 +393,7 @@ describe('compiler compliance: template', () => {
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
- $i0$.ɵtemplate(0, Template_0, 1, 0, null, null, _c0, i0.ɵtemplateRefExtractor);
+ $i0$.ɵtemplate(0, Template_0, 1, 0, null, null, $t0_refs$, $i0$.ɵtemplateRefExtractor);
}
}`;
diff --git a/packages/compiler/src/core.ts b/packages/compiler/src/core.ts
index e34aa4c1c0..58577c9039 100644
--- a/packages/compiler/src/core.ts
+++ b/packages/compiler/src/core.ts
@@ -383,3 +383,25 @@ export const enum RenderFlags {
export const enum InitialStylingFlags {
VALUES_MODE = 0b1,
}
+
+// Pasted from render3/interfaces/node.ts
+/**
+ * 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.
+ */
+export const enum AttributeMarker {
+ /**
+ * Marker indicates that the following 3 values in the attributes array are:
+ * namespaceUri, attributeName, attributeValue
+ * in that order.
+ */
+ NamespaceURI = 0,
+
+ /**
+ * This marker indicates that the following attribute names were extracted from bindings (ex.:
+ * [foo]="exp") and / or event handlers (ex. (bar)="doSth()").
+ * Taking the above bindings and outputs as an example an attributes array could look as follows:
+ * ['class', 'fade in', AttributeMarker.SelectOnly, 'foo', 'bar']
+ */
+ SelectOnly = 1
+}
\ No newline at end of file
diff --git a/packages/compiler/src/render3/r3_ast.ts b/packages/compiler/src/render3/r3_ast.ts
index 52767919ad..792f5a31e9 100644
--- a/packages/compiler/src/render3/r3_ast.ts
+++ b/packages/compiler/src/render3/r3_ast.ts
@@ -71,10 +71,10 @@ export class Element implements Node {
export class Template implements Node {
constructor(
- public attributes: TextAttribute[], public inputs: BoundAttribute[], public children: Node[],
- public references: Reference[], public variables: Variable[],
- public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan|null,
- public endSourceSpan: ParseSourceSpan|null) {}
+ public attributes: TextAttribute[], public inputs: BoundAttribute[],
+ public outputs: BoundEvent[], public children: Node[], public references: Reference[],
+ public variables: Variable[], public sourceSpan: ParseSourceSpan,
+ public startSourceSpan: ParseSourceSpan|null, public endSourceSpan: ParseSourceSpan|null) {}
visit(visitor: Visitor): Result { return visitor.visitTemplate(this); }
}
@@ -167,6 +167,7 @@ export class TransformVisitor implements Visitor {
visitTemplate(template: Template): Node {
const newAttributes = transformAll(this, template.attributes);
const newInputs = transformAll(this, template.inputs);
+ const newOutputs = transformAll(this, template.outputs);
const newChildren = transformAll(this, template.children);
const newReferences = transformAll(this, template.references);
const newVariables = transformAll(this, template.variables);
@@ -174,8 +175,8 @@ export class TransformVisitor implements Visitor {
newChildren != template.children || newVariables != template.variables ||
newReferences != template.references) {
return new Template(
- newAttributes, newInputs, newChildren, newReferences, newVariables, template.sourceSpan,
- template.startSourceSpan, template.endSourceSpan);
+ newAttributes, newInputs, newOutputs, newChildren, newReferences, newVariables,
+ template.sourceSpan, template.startSourceSpan, template.endSourceSpan);
}
return template;
}
diff --git a/packages/compiler/src/render3/r3_template_transform.ts b/packages/compiler/src/render3/r3_template_transform.ts
index 22c31d8bcb..cabf6ae64a 100644
--- a/packages/compiler/src/render3/r3_template_transform.ts
+++ b/packages/compiler/src/render3/r3_template_transform.ts
@@ -181,7 +181,7 @@ class HtmlAstToIvyAst implements html.Visitor {
const attrs = this.extractAttributes(element.name, parsedProperties);
parsedElement = new t.Template(
- attributes, attrs.bound, children, references, variables, element.sourceSpan,
+ attributes, attrs.bound, boundEvents, children, references, variables, element.sourceSpan,
element.startSourceSpan, element.endSourceSpan);
} else {
const attrs = this.extractAttributes(element.name, parsedProperties);
@@ -193,9 +193,10 @@ class HtmlAstToIvyAst implements html.Visitor {
if (elementHasInlineTemplate) {
const attrs = this.extractAttributes('ng-template', templateParsedProperties);
+ // TODO(pk): test for this case
parsedElement = new t.Template(
- attrs.literal, attrs.bound, [parsedElement], [], templateVariables, element.sourceSpan,
- element.startSourceSpan, element.endSourceSpan);
+ attrs.literal, attrs.bound, [], [parsedElement], [], templateVariables,
+ element.sourceSpan, element.startSourceSpan, element.endSourceSpan);
}
return parsedElement;
}
diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts
index ffe9fe3801..28968f4c88 100644
--- a/packages/compiler/src/render3/view/template.ts
+++ b/packages/compiler/src/render3/view/template.ts
@@ -30,7 +30,7 @@ import {htmlAstToRender3Ast} from '../r3_template_transform';
import {R3QueryMetadata} from './api';
import {parseStyle} from './styling';
-import {CONTEXT_NAME, I18N_ATTR, I18N_ATTR_PREFIX, ID_SEPARATOR, IMPLICIT_REFERENCE, MEANING_SEPARATOR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, invalid, mapToExpression, trimTrailingNulls, unsupported} from './util';
+import {CONTEXT_NAME, I18N_ATTR, I18N_ATTR_PREFIX, ID_SEPARATOR, IMPLICIT_REFERENCE, MEANING_SEPARATOR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, invalid, isI18NAttribute, mapToExpression, trimTrailingNulls, unsupported} from './util';
function mapBindingToInstruction(type: BindingType): o.ExternalReference|undefined {
switch (type) {
@@ -317,11 +317,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
}
// Match directives on non i18n attributes
- if (this.directiveMatcher) {
- const selector = createCssSelector(element.name, outputAttrs);
- this.directiveMatcher.match(
- selector, (sel: CssSelector, staticType: any) => { this.directives.add(staticType); });
- }
+ this.matchDirectives(element.name, element);
// Regular element or ng-container creation mode
const parameters: o.Expression[] = [o.literal(elementIndex)];
@@ -455,10 +451,9 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
const hasStylingInstructions = initialStyleDeclarations.length || styleInputs.length ||
initialClassDeclarations.length || classInputs.length;
- const attrArg: o.Expression = attributes.length > 0 ?
- this.constantPool.getConstLiteral(o.literalArr(attributes), true) :
- o.TYPED_NULL_EXPR;
- parameters.push(attrArg);
+ // add attributes for directive matching purposes
+ attributes.push(...this.prepareSelectOnlyAttrs(allOtherInputs, element.outputs));
+ parameters.push(this.toAttrsParam(attributes));
// local refs (ex.: )
parameters.push(this.prepareRefsParameter(element.references));
@@ -690,31 +685,15 @@ export class TemplateDefinitionBuilder implements t.Visitor
, LocalResolver
o.TYPED_NULL_EXPR,
];
- // Match directives on both attributes and bound properties
- const attributeNames: o.Expression[] = [];
- const attributeMap: {[name: string]: string} = {};
+ // find directives matching on a given node
+ this.matchDirectives('ng-template', template);
- template.attributes.forEach(a => {
- attributeNames.push(asLiteral(a.name), asLiteral(''));
- attributeMap[a.name] = a.value;
- });
-
- template.inputs.forEach(i => {
- attributeNames.push(asLiteral(i.name), asLiteral(''));
- attributeMap[i.name] = '';
- });
-
- if (this.directiveMatcher) {
- const selector = createCssSelector('ng-template', attributeMap);
- this.directiveMatcher.match(
- selector, (cssSelector, staticType) => { this.directives.add(staticType); });
- }
-
- if (attributeNames.length) {
- parameters.push(this.constantPool.getConstLiteral(o.literalArr(attributeNames), true));
- } else {
- parameters.push(o.TYPED_NULL_EXPR);
- }
+ // prepare attributes parameter (including attributes used for directive matching)
+ const attrsExprs: o.Expression[] = [];
+ template.attributes.forEach(
+ (a: t.TextAttribute) => { attrsExprs.push(asLiteral(a.name), asLiteral(a.value)); });
+ attrsExprs.push(...this.prepareSelectOnlyAttrs(template.inputs, template.outputs));
+ parameters.push(this.toAttrsParam(attrsExprs));
// local refs (ex.: )
if (template.references && template.references.length) {
@@ -722,7 +701,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
parameters.push(o.importExpr(R3.templateRefExtractor));
}
- // e.g. p(1, 'forOf', ɵbind(ctx.items));
+ // handle property bindings e.g. p(1, 'forOf', ɵbind(ctx.items));
const context = o.variable(CONTEXT_NAME);
template.inputs.forEach(input => {
const value = input.value.visit(this._valueConverter);
@@ -859,6 +838,47 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
o.importExpr(R3.bind).callFn([valExpr]);
}
+ private matchDirectives(tagName: string, elOrTpl: t.Element|t.Template) {
+ if (this.directiveMatcher) {
+ const selector = createCssSelector(tagName, this.getAttrsForDirectiveMatching(elOrTpl));
+ this.directiveMatcher.match(
+ selector, (cssSelector, staticType) => { this.directives.add(staticType); });
+ }
+ }
+
+ private getAttrsForDirectiveMatching(elOrTpl: t.Element|t.Template): {[name: string]: string} {
+ const attributesMap: {[name: string]: string} = {};
+
+ elOrTpl.attributes.forEach(a => {
+ if (!isI18NAttribute(a.name)) {
+ attributesMap[a.name] = a.value;
+ }
+ });
+ elOrTpl.inputs.forEach(i => { attributesMap[i.name] = ''; });
+ elOrTpl.outputs.forEach(o => { attributesMap[o.name] = ''; });
+
+ return attributesMap;
+ }
+
+ private prepareSelectOnlyAttrs(inputs: t.BoundAttribute[], outputs: t.BoundEvent[]):
+ o.Expression[] {
+ const attrExprs: o.Expression[] = [];
+
+ if (inputs.length || outputs.length) {
+ attrExprs.push(o.literal(core.AttributeMarker.SelectOnly));
+ inputs.forEach((i: t.BoundAttribute) => { attrExprs.push(asLiteral(i.name)); });
+ outputs.forEach((o: t.BoundEvent) => { attrExprs.push(asLiteral(o.name)); });
+ }
+
+ return attrExprs;
+ }
+
+ private toAttrsParam(attrsExprs: o.Expression[]): o.Expression {
+ return attrsExprs.length > 0 ?
+ this.constantPool.getConstLiteral(o.literalArr(attrsExprs), true) :
+ o.TYPED_NULL_EXPR;
+ }
+
private prepareRefsParameter(references: t.Reference[]): o.Expression {
if (!references || references.length === 0) {
return o.TYPED_NULL_EXPR;
diff --git a/packages/compiler/src/render3/view/util.ts b/packages/compiler/src/render3/view/util.ts
index 20e548515a..c5462e90c7 100644
--- a/packages/compiler/src/render3/view/util.ts
+++ b/packages/compiler/src/render3/view/util.ts
@@ -64,6 +64,10 @@ export function invalid(arg: o.Expression | o.Statement | t.Node): never {
`Invalid state: Visitor ${this.constructor.name} doesn't handle ${o.constructor.name}`);
}
+export function isI18NAttribute(name: string): boolean {
+ return name === I18N_ATTR || name.startsWith(I18N_ATTR_PREFIX);
+}
+
export function asLiteral(value: any): o.Expression {
if (Array.isArray(value)) {
return o.literalArr(value.map(asLiteral));
diff --git a/packages/core/src/render3/STATUS.md b/packages/core/src/render3/STATUS.md
index f3d1550331..50cb4bffea 100644
--- a/packages/core/src/render3/STATUS.md
+++ b/packages/core/src/render3/STATUS.md
@@ -157,9 +157,9 @@ The goal is for the `@Component` (and friends) to be the compiler of template. S
| `` | ❌ | ❌ | ❌ |
| `
` | ❌ | ❌ | ❌ |
| `
` | ❌ | ❌ | ❌ |
-| [`
`][gh23560] | ✅ | ❌ | ❌ |
+| [`
`][gh23560] | ✅ | ✅ | ✅ |
| [`
`][gh23561] | ❌ | ❌ | ❌ |
-| [``][gh24381] | ❌ | ❌ | ❌ |
+| [``][gh24381] | ✅ | ✅ | ✅ |
[gh23560]: https://github.com/angular/angular/issues/23560
[gh23561]: https://github.com/angular/angular/issues/23561
diff --git a/packages/core/src/render3/jit/directive.ts b/packages/core/src/render3/jit/directive.ts
index e756c47002..cd7c5e35b5 100644
--- a/packages/core/src/render3/jit/directive.ts
+++ b/packages/core/src/render3/jit/directive.ts
@@ -85,8 +85,8 @@ export function compileComponent(type: Type, metadata: Component): void {
// If component compilation is async, then the @NgModule annotation which declares the
// component may execute and set an ngSelectorScope property on the component type. This
- // allows the component to patch itself with directiveDefs from the module after it finishes
- // compiling.
+ // allows the component to patch itself with directiveDefs from the module after it
+ // finishes compiling.
if (hasSelectorScope(type)) {
const scopes = transitiveScopesFor(type.ngSelectorScope);
patchComponentDefWithScope(ngComponentDef, scopes);
diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json
index 6f3ddfd2f7..2f1cc03898 100644
--- a/packages/core/test/bundling/todo/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json
@@ -257,6 +257,15 @@
{
"name": "_c2"
},
+ {
+ "name": "_c20"
+ },
+ {
+ "name": "_c21"
+ },
+ {
+ "name": "_c22"
+ },
{
"name": "_c3"
},
diff --git a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json
index 1a4e32d3f5..ceb4d8a9df 100644
--- a/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json
+++ b/packages/core/test/bundling/todo_r2/bundle.golden_symbols.json
@@ -1019,6 +1019,15 @@
{
"name": "_c2"
},
+ {
+ "name": "_c20"
+ },
+ {
+ "name": "_c21"
+ },
+ {
+ "name": "_c22"
+ },
{
"name": "_c3"
},