fix(ivy): enable packages/core/test/render3 test for AoT (#26863)
PR Close #26863
This commit is contained in:
parent
516af6c531
commit
c13f46c7c5
|
@ -9,11 +9,10 @@ ng_module(
|
|||
srcs = ["index.ts"],
|
||||
tags = [
|
||||
"ivy-only",
|
||||
"no-ivy-jit",
|
||||
],
|
||||
deps = [
|
||||
"//packages/core",
|
||||
"//packages/platform-browser-dynamic",
|
||||
"//packages/platform-browser",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -28,12 +27,11 @@ ng_rollup_bundle(
|
|||
entry_point = "packages/core/test/bundling/hello_world_r2/index.js",
|
||||
tags = [
|
||||
"ivy-only",
|
||||
"no-ivy-jit",
|
||||
],
|
||||
deps = [
|
||||
":hello_world",
|
||||
"//packages/core",
|
||||
"//packages/platform-browser-dynamic",
|
||||
"//packages/platform-browser",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -43,7 +41,6 @@ ts_library(
|
|||
srcs = glob(["*_spec.ts"]),
|
||||
tags = [
|
||||
"ivy-only",
|
||||
"no-ivy-jit",
|
||||
],
|
||||
deps = [
|
||||
"//packages:types",
|
||||
|
@ -63,7 +60,6 @@ jasmine_node_test(
|
|||
],
|
||||
tags = [
|
||||
"ivy-only",
|
||||
"no-ivy-jit",
|
||||
],
|
||||
deps = [":test_lib"],
|
||||
)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
import {platformBrowser} from '@angular/platform-browser';
|
||||
|
||||
@Component({selector: 'hello-world', template: 'Hello World!'})
|
||||
export class HelloWorldComponent {
|
||||
|
@ -16,4 +16,4 @@ export class HelloWorldComponent {
|
|||
export class HelloWorldModule {
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(HelloWorldModule);
|
||||
platformBrowser().bootstrapModule(HelloWorldModule);
|
||||
|
|
|
@ -61,7 +61,6 @@ jasmine_node_test(
|
|||
"angular/packages/core/test/render3/load_domino",
|
||||
],
|
||||
tags = [
|
||||
"fixme-ivy-aot",
|
||||
"fixme-ivy-jit",
|
||||
],
|
||||
deps = [
|
||||
|
@ -73,7 +72,6 @@ jasmine_node_test(
|
|||
ts_web_test_suite(
|
||||
name = "render3_web",
|
||||
tags = [
|
||||
"fixme-ivy-aot",
|
||||
"fixme-ivy-jit",
|
||||
],
|
||||
deps = [
|
||||
|
|
|
@ -1,961 +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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
|
||||
import * as $r3$ from '../../../src/core_render3_private_export';
|
||||
import {ComponentDef} from '../../../src/render3/interfaces/definition';
|
||||
import {renderComponent, toHtml} from '../render_util';
|
||||
|
||||
|
||||
|
||||
/// See: `normative.md`
|
||||
describe('components & directives', () => {
|
||||
type $RenderFlags$ = $r3$.ɵRenderFlags;
|
||||
type $any$ = any;
|
||||
type $number$ = number;
|
||||
|
||||
it('should instantiate directives', () => {
|
||||
type $ChildComponent$ = ChildComponent;
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
const log: string[] = [];
|
||||
@Component({selector: 'child', template: 'child-view'})
|
||||
class ChildComponent {
|
||||
constructor() { log.push('ChildComponent'); }
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: ChildComponent,
|
||||
selectors: [['child']],
|
||||
factory: function ChildComponent_Factory(t) { return new (t || ChildComponent)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function ChildComponent_Template(rf: $RenderFlags$, ctx: $ChildComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtext(0, 'child-view');
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: '[some-directive]',
|
||||
})
|
||||
class SomeDirective {
|
||||
constructor() { log.push('SomeDirective'); }
|
||||
// NORMATIVE
|
||||
static ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
type: SomeDirective,
|
||||
selectors: [['', 'some-directive', '']],
|
||||
factory: function SomeDirective_Factory(t) { return new (t || SomeDirective)(); },
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// Important: keep arrays outside of function to not create new instances.
|
||||
// NORMATIVE
|
||||
const $e0_attrs$ = ['some-directive', ''];
|
||||
// /NORMATIVE
|
||||
|
||||
@Component({selector: 'my-component', template: `<child some-directive></child>!`})
|
||||
class MyComponent {
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-component']],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 2,
|
||||
vars: 0,
|
||||
template: function(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'child', $e0_attrs$);
|
||||
$r3$.ɵtext(1, '!');
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE (done by defineNgModule)
|
||||
(MyComponent.ngComponentDef as ComponentDef<any>).directiveDefs =
|
||||
[(ChildComponent.ngComponentDef as ComponentDef<any>), SomeDirective.ngDirectiveDef];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
expect(renderComp(MyComponent)).toEqual('<child some-directive="">child-view</child>!');
|
||||
expect(log).toEqual(['ChildComponent', 'SomeDirective']);
|
||||
});
|
||||
|
||||
it('should support host bindings', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
|
||||
@Directive({selector: '[hostBindingDir]'})
|
||||
class HostBindingDir {
|
||||
@HostBinding('id') dirId = 'some id';
|
||||
|
||||
// NORMATIVE
|
||||
static ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
type: HostBindingDir,
|
||||
selectors: [['', 'hostBindingDir', '']],
|
||||
factory: function HostBindingDir_Factory(t) { return new (t || HostBindingDir)(); },
|
||||
hostVars: 1,
|
||||
hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) {
|
||||
$r3$.ɵelementProperty(
|
||||
elIndex, 'id', $r3$.ɵbind($r3$.ɵload<HostBindingDir>(dirIndex).dirId));
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
const $e0_attrs$ = ['hostBindingDir', ''];
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<div hostBindingDir></div>
|
||||
`
|
||||
})
|
||||
class MyApp {
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'div', $e0_attrs$);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// NON-NORMATIVE (done by defineNgModule)
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs = [HostBindingDir.ngDirectiveDef];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
expect(renderComp(MyApp)).toEqual(`<div hostbindingdir="" id="some id"></div>`);
|
||||
});
|
||||
|
||||
it('should support host listeners', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
|
||||
@Directive({selector: '[hostlistenerDir]'})
|
||||
class HostListenerDir {
|
||||
@HostListener('click')
|
||||
onClick() {}
|
||||
|
||||
// NORMATIVE
|
||||
static ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
selectors: [['', 'hostListenerDir', '']],
|
||||
type: HostListenerDir,
|
||||
factory: function HostListenerDir_Factory() {
|
||||
const $dir$ = new HostListenerDir();
|
||||
$r3$.ɵlistener(
|
||||
'click', function HostListenerDir_click_Handler(event: any) { $dir$.onClick(); });
|
||||
return $dir$;
|
||||
},
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
const $e0_attrs$ = ['hostListenerDir', ''];
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<button hostListenerDir>Click</button>
|
||||
`
|
||||
})
|
||||
class MyApp {
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 2,
|
||||
vars: 0,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'button', $e0_attrs$);
|
||||
$r3$.ɵtext(1, 'Click');
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// NON-NORMATIVE (done by defineNgModule)
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs = [HostListenerDir.ngDirectiveDef];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
expect(renderComp(MyApp)).toEqual(`<button hostlistenerdir="">Click</button>`);
|
||||
});
|
||||
|
||||
|
||||
it('should support setting of host attributes', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
|
||||
@Directive({selector: '[hostAttributeDir]', host: {'role': 'listbox'}})
|
||||
class HostAttributeDir {
|
||||
// NORMATIVE
|
||||
static ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
selectors: [['', 'hostAttributeDir', '']],
|
||||
type: HostAttributeDir,
|
||||
factory: function HostAttributeDir_Factory(t) { return new (t || HostAttributeDir)(); },
|
||||
attributes: ['role', 'listbox']
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
const $e0_attrs$ = ['hostAttributeDir', ''];
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<div hostAttributeDir></div>
|
||||
`
|
||||
})
|
||||
class MyApp {
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'div', $e0_attrs$);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// NON-NORMATIVE (done by defineNgModule)
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs = [HostAttributeDir.ngDirectiveDef];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
expect(renderComp(MyApp)).toEqual(`<div hostattributedir="" role="listbox"></div>`);
|
||||
});
|
||||
|
||||
it('should support bindings of host attributes', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
|
||||
@Directive({selector: '[hostBindingDir]'})
|
||||
class HostBindingDir {
|
||||
@HostBinding('attr.aria-label') label = 'some label';
|
||||
|
||||
// NORMATIVE
|
||||
static ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
type: HostBindingDir,
|
||||
selectors: [['', 'hostBindingDir', '']],
|
||||
factory: function HostBindingDir_Factory(t) { return new (t || HostBindingDir)(); },
|
||||
hostVars: 1,
|
||||
hostBindings: function HostBindingDir_HostBindings(dirIndex: $number$, elIndex: $number$) {
|
||||
$r3$.ɵelementAttribute(
|
||||
elIndex, 'aria-label', $r3$.ɵbind($r3$.ɵload<HostBindingDir>(dirIndex).label));
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
const $e0_attrs$ = ['hostBindingDir', ''];
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<div hostBindingDir></div>
|
||||
`
|
||||
})
|
||||
class MyApp {
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'div', $e0_attrs$);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// NON-NORMATIVE (done by defineNgModule)
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs = [HostBindingDir.ngDirectiveDef];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
expect(renderComp(MyApp)).toEqual(`<div aria-label="some label" hostbindingdir=""></div>`);
|
||||
});
|
||||
|
||||
it('should support onPush components', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
type $MyComp$ = MyComp;
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: `
|
||||
{{ name }}
|
||||
`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
class MyComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() name !: string;
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComp,
|
||||
selectors: [['my-comp']],
|
||||
factory: function MyComp_Factory(t) { return new (t || MyComp)(); },
|
||||
consts: 1,
|
||||
vars: 1,
|
||||
template: function MyComp_Template(rf: $RenderFlags$, ctx: $MyComp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtext(0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵtextBinding(0, $r3$.ɵbind(ctx.name));
|
||||
}
|
||||
},
|
||||
inputs: {name: 'name'},
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<my-comp [name]="name"></my-comp>
|
||||
`
|
||||
})
|
||||
class MyApp {
|
||||
name = 'some name';
|
||||
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 1,
|
||||
vars: 1,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'my-comp');
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, 'name', $r3$.ɵbind(ctx.name));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// NON-NORMATIVE (done by defineNgModule)
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
|
||||
[(MyComp.ngComponentDef as ComponentDef<any>)];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
expect(renderComp(MyApp)).toEqual(`<my-comp>some name</my-comp>`);
|
||||
});
|
||||
|
||||
xit('should support structural directives', () => {
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
function C1(rf1: $RenderFlags$, ctx1: $any$) {
|
||||
if (rf1 & 1) {
|
||||
$r3$.ɵelementStart(0, 'li');
|
||||
$r3$.ɵtext(1);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf1 & 2) {
|
||||
const $comp$ = $r3$.ɵnextContext();
|
||||
const $foo$ = $r3$.ɵreference(1);
|
||||
$r3$.ɵtextBinding(1, $r3$.ɵinterpolation2('', $comp$.salutation, ' ', $foo$, ''));
|
||||
}
|
||||
}
|
||||
|
||||
const log: string[] = [];
|
||||
@Directive({
|
||||
selector: '[if]',
|
||||
})
|
||||
class IfDirective {
|
||||
constructor(template: TemplateRef<any>) { log.push('ifDirective'); }
|
||||
// NORMATIVE
|
||||
static ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
type: IfDirective,
|
||||
selectors: [['', 'if', '']],
|
||||
factory: function IfDirective_Factory(t) {
|
||||
return new (t || IfDirective)($r3$.ɵdirectiveInject(TemplateRef as any));
|
||||
},
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// Important: keep arrays outside of function to not create new instances.
|
||||
// NORMATIVE
|
||||
const $e0_locals$ = ['foo', ''];
|
||||
// /NORMATIVE
|
||||
|
||||
@Component(
|
||||
{selector: 'my-component', template: `<ul #foo><li *if>{{salutation}} {{foo}}</li></ul>`})
|
||||
class MyComponent {
|
||||
salutation = 'Hello';
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-component']],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 3,
|
||||
vars: 0,
|
||||
template: function(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'ul', null, $e0_locals$);
|
||||
$r3$.ɵtemplate(2, C1, 2, 1, '', ['if', '']);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
expect(renderComp(MyComponent)).toEqual('<child some-directive="">child-view</child>!');
|
||||
expect(log).toEqual(['ChildComponent', 'SomeDirective']);
|
||||
});
|
||||
|
||||
describe('value composition', () => {
|
||||
type $MyArrayComp$ = MyArrayComp;
|
||||
|
||||
@Component({
|
||||
selector: 'my-array-comp',
|
||||
template: `
|
||||
{{ names[0] }} {{ names[1] }}
|
||||
`
|
||||
})
|
||||
class MyArrayComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() names !: string[];
|
||||
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyArrayComp,
|
||||
selectors: [['my-array-comp']],
|
||||
factory: function MyArrayComp_Factory(t) { return new (t || MyArrayComp)(); },
|
||||
consts: 1,
|
||||
vars: 2,
|
||||
template: function MyArrayComp_Template(rf: $RenderFlags$, ctx: $MyArrayComp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtext(0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵtextBinding(0, $r3$.ɵinterpolation2('', ctx.names[0], ' ', ctx.names[1], ''));
|
||||
}
|
||||
},
|
||||
inputs: {names: 'names'}
|
||||
});
|
||||
}
|
||||
|
||||
it('should support array literals of constants', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
|
||||
// NORMATIVE
|
||||
const $e0_arr$ = ['Nancy', 'Bess'];
|
||||
// /NORMATIVE
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<my-array-comp [names]="['Nancy', 'Bess']"></my-array-comp>
|
||||
`
|
||||
})
|
||||
class MyApp {
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'my-array-comp');
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, 'names', rf & 1 ? $e0_arr$ : $r3$.ɵNO_CHANGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE (done by defineNgModule)
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
|
||||
[(MyArrayComp.ngComponentDef as ComponentDef<any>)];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
expect(renderComp(MyApp)).toEqual(`<my-array-comp>Nancy Bess</my-array-comp>`);
|
||||
});
|
||||
|
||||
it('should support array literals of constants inside function calls', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
|
||||
// NORMATIVE
|
||||
const $e0_ff$ = () => ['Nancy', 'Bess'];
|
||||
// /NORMATIVE
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<my-array-comp [names]="someFn(['Nancy', 'Bess'])"></my-array-comp>
|
||||
`
|
||||
})
|
||||
class MyApp {
|
||||
someFn(arr: string[]): string[] {
|
||||
arr[0] = arr[0].toUpperCase();
|
||||
return arr;
|
||||
}
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 1,
|
||||
vars: 2,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'my-array-comp');
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(
|
||||
0, 'names', $r3$.ɵbind(ctx.someFn($r3$.ɵpureFunction0(1, $e0_ff$))));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE (done by defineNgModule)
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
|
||||
[(MyArrayComp.ngComponentDef as ComponentDef<any>)];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
expect(renderComp(MyApp)).toEqual(`<my-array-comp>NANCY Bess</my-array-comp>`);
|
||||
});
|
||||
|
||||
it('should support array literals of constants inside expressions', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
type $MyComp$ = MyComp;
|
||||
|
||||
@Component({selector: 'my-comp', template: `{{ num }}`})
|
||||
class MyComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
num !: number;
|
||||
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComp,
|
||||
selectors: [['my-comp']],
|
||||
factory: function MyComp_Factory(t) { return new (t || MyComp)(); },
|
||||
consts: 1,
|
||||
vars: 1,
|
||||
template: function MyComp_Template(rf: $RenderFlags$, ctx: $MyComp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtext(0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
// clang-format wants to break this line by changing the second 'ɵ' to an invalid
|
||||
// unicode sequence.
|
||||
// clang-format off
|
||||
$r3$.ɵtextBinding(0, $r3$.ɵbind(ctx.num));
|
||||
// clang-format on
|
||||
}
|
||||
},
|
||||
inputs: {num: 'num'}
|
||||
});
|
||||
}
|
||||
|
||||
// NORMATIVE
|
||||
const $e0_ff$ = () => ['Nancy', 'Bess'];
|
||||
// /NORMATIVE
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<my-comp [num]="['Nancy', 'Bess'].length + 1"></my-comp>
|
||||
`
|
||||
})
|
||||
class MyApp {
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 1,
|
||||
vars: 2,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'my-comp');
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(
|
||||
0, 'num', $r3$.ɵbind($r3$.ɵpureFunction0(1, $e0_ff$).length + 1));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE (done by defineNgModule)
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
|
||||
[(MyComp.ngComponentDef as ComponentDef<any>)];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
expect(renderComp(MyApp)).toEqual(`<my-comp>3</my-comp>`);
|
||||
});
|
||||
|
||||
|
||||
it('should support array literals', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
|
||||
// NORMATIVE
|
||||
const $e0_ff$ = (v: any) => ['Nancy', v];
|
||||
// /NORMATIVE
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<my-array-comp [names]="['Nancy', customName]"></my-array-comp>
|
||||
`
|
||||
})
|
||||
class MyApp {
|
||||
customName = 'Bess';
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 1,
|
||||
vars: 3,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'my-array-comp');
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(
|
||||
0, 'names', $r3$.ɵbind($r3$.ɵpureFunction1(1, $e0_ff$, ctx.customName)));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE (done by defineNgModule)
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
|
||||
[(MyArrayComp.ngComponentDef as ComponentDef<any>)];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
expect(renderComp(MyApp)).toEqual(`<my-array-comp>Nancy Bess</my-array-comp>`);
|
||||
});
|
||||
|
||||
it('should support 9+ bindings in array literals', () => {
|
||||
type $MyComp$ = MyComp;
|
||||
|
||||
@Component({
|
||||
selector: 'my-comp',
|
||||
template: `
|
||||
{{ names[0] }}
|
||||
{{ names[1] }}
|
||||
{{ names[3] }}
|
||||
{{ names[4] }}
|
||||
{{ names[5] }}
|
||||
{{ names[6] }}
|
||||
{{ names[7] }}
|
||||
{{ names[8] }}
|
||||
{{ names[9] }}
|
||||
{{ names[10] }}
|
||||
{{ names[11] }}
|
||||
`
|
||||
})
|
||||
class MyComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() names !: string[];
|
||||
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComp,
|
||||
selectors: [['my-comp']],
|
||||
factory: function MyComp_Factory(t) { return new (t || MyComp)(); },
|
||||
consts: 12,
|
||||
vars: 12,
|
||||
template: function MyComp_Template(rf: $RenderFlags$, ctx: $MyComp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtext(0);
|
||||
$r3$.ɵtext(1);
|
||||
$r3$.ɵtext(2);
|
||||
$r3$.ɵtext(3);
|
||||
$r3$.ɵtext(4);
|
||||
$r3$.ɵtext(5);
|
||||
$r3$.ɵtext(6);
|
||||
$r3$.ɵtext(7);
|
||||
$r3$.ɵtext(8);
|
||||
$r3$.ɵtext(9);
|
||||
$r3$.ɵtext(10);
|
||||
$r3$.ɵtext(11);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵtextBinding(0, $r3$.ɵbind(ctx.names[0]));
|
||||
$r3$.ɵtextBinding(1, $r3$.ɵbind(ctx.names[1]));
|
||||
$r3$.ɵtextBinding(2, $r3$.ɵbind(ctx.names[2]));
|
||||
$r3$.ɵtextBinding(3, $r3$.ɵbind(ctx.names[3]));
|
||||
$r3$.ɵtextBinding(4, $r3$.ɵbind(ctx.names[4]));
|
||||
$r3$.ɵtextBinding(5, $r3$.ɵbind(ctx.names[5]));
|
||||
$r3$.ɵtextBinding(6, $r3$.ɵbind(ctx.names[6]));
|
||||
$r3$.ɵtextBinding(7, $r3$.ɵbind(ctx.names[7]));
|
||||
$r3$.ɵtextBinding(8, $r3$.ɵbind(ctx.names[8]));
|
||||
$r3$.ɵtextBinding(9, $r3$.ɵbind(ctx.names[9]));
|
||||
$r3$.ɵtextBinding(10, $r3$.ɵbind(ctx.names[10]));
|
||||
$r3$.ɵtextBinding(11, $r3$.ɵbind(ctx.names[11]));
|
||||
}
|
||||
},
|
||||
inputs: {names: 'names'}
|
||||
});
|
||||
}
|
||||
|
||||
// NORMATIVE
|
||||
const $e0_ff$ =
|
||||
(v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any, v7: any,
|
||||
v8: any) => ['start-', v0, v1, v2, v3, v4, '-middle-', v5, v6, v7, v8, '-end'];
|
||||
// /NORMATIVE
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<my-comp [names]="['start-', n0, n1, n2, n3, n4, '-middle-', n5, n6, n7, n8, '-end']">
|
||||
</my-comp>
|
||||
`
|
||||
})
|
||||
class MyApp {
|
||||
n0 = 'a';
|
||||
n1 = 'b';
|
||||
n2 = 'c';
|
||||
n3 = 'd';
|
||||
n4 = 'e';
|
||||
n5 = 'f';
|
||||
n6 = 'g';
|
||||
n7 = 'h';
|
||||
n8 = 'i';
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 1,
|
||||
vars: 11,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, c: $any$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'my-comp');
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(
|
||||
0, 'names',
|
||||
$r3$.ɵbind($r3$.ɵpureFunctionV(
|
||||
1, $e0_ff$, [c.n0, c.n1, c.n2, c.n3, c.n4, c.n5, c.n6, c.n7, c.n8])));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE (done by defineNgModule)
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
|
||||
[(MyComp.ngComponentDef as ComponentDef<any>)];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
expect(renderComp(MyApp)).toEqual(`<my-comp>start-abcde-middle-fghi-end</my-comp>`);
|
||||
});
|
||||
|
||||
it('should support object literals', () => {
|
||||
type $ObjectComp$ = ObjectComp;
|
||||
type $MyApp$ = MyApp;
|
||||
|
||||
@Component({
|
||||
selector: 'object-comp',
|
||||
template: `
|
||||
<p> {{ config['duration'] }} </p>
|
||||
<p> {{ config.animation }} </p>
|
||||
`
|
||||
})
|
||||
class ObjectComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
config !: {[key: string]: any};
|
||||
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: ObjectComp,
|
||||
selectors: [['object-comp']],
|
||||
factory: function ObjectComp_Factory(t) { return new (t || ObjectComp)(); },
|
||||
consts: 4,
|
||||
vars: 2,
|
||||
template: function ObjectComp_Template(rf: $RenderFlags$, ctx: $ObjectComp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'p');
|
||||
$r3$.ɵtext(1);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵelementStart(2, 'p');
|
||||
$r3$.ɵtext(3);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵtextBinding(1, $r3$.ɵbind(ctx.config['duration']));
|
||||
$r3$.ɵtextBinding(3, $r3$.ɵbind(ctx.config.animation));
|
||||
}
|
||||
},
|
||||
inputs: {config: 'config'}
|
||||
});
|
||||
}
|
||||
|
||||
// NORMATIVE
|
||||
const $e0_ff$ = (v: any) => { return {'duration': 500, animation: v}; };
|
||||
// /NORMATIVE
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<object-comp [config]="{'duration': 500, animation: name}"></object-comp>
|
||||
`
|
||||
})
|
||||
class MyApp {
|
||||
name = 'slide';
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 1,
|
||||
vars: 3,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'object-comp');
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(
|
||||
0, 'config', $r3$.ɵbind($r3$.ɵpureFunction1(1, $e0_ff$, ctx.name)));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE (done by defineNgModule)
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
|
||||
[(ObjectComp.ngComponentDef as ComponentDef<any>)];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
expect(renderComp(MyApp)).toEqual(`<object-comp><p>500</p><p>slide</p></object-comp>`);
|
||||
});
|
||||
|
||||
it('should support expressions nested deeply in object/array literals', () => {
|
||||
type $NestedComp$ = NestedComp;
|
||||
type $MyApp$ = MyApp;
|
||||
|
||||
@Component({
|
||||
selector: 'nested-comp',
|
||||
template: `
|
||||
<p> {{ config.animation }} </p>
|
||||
<p> {{config.actions[0].opacity }} </p>
|
||||
<p> {{config.actions[1].duration }} </p>
|
||||
`
|
||||
})
|
||||
class NestedComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
config !: {[key: string]: any};
|
||||
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: NestedComp,
|
||||
selectors: [['nested-comp']],
|
||||
factory: function NestedComp_Factory(t) { return new (t || NestedComp)(); },
|
||||
consts: 6,
|
||||
vars: 3,
|
||||
template: function NestedComp_Template(rf: $RenderFlags$, ctx: $NestedComp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'p');
|
||||
$r3$.ɵtext(1);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵelementStart(2, 'p');
|
||||
$r3$.ɵtext(3);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵelementStart(4, 'p');
|
||||
$r3$.ɵtext(5);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵtextBinding(1, $r3$.ɵbind(ctx.config.animation));
|
||||
$r3$.ɵtextBinding(3, $r3$.ɵbind(ctx.config.actions[0].opacity));
|
||||
$r3$.ɵtextBinding(5, $r3$.ɵbind(ctx.config.actions[1].duration));
|
||||
}
|
||||
},
|
||||
inputs: {config: 'config'}
|
||||
});
|
||||
}
|
||||
|
||||
// NORMATIVE
|
||||
const $e0_ff$ = (v: any) => { return {opacity: 1, duration: v}; };
|
||||
const $c0$ = {opacity: 0, duration: 0};
|
||||
const $e0_ff_1$ = (v: any) => [$c0$, v];
|
||||
const $e0_ff_2$ = (v1: any, v2: any) => { return {animation: v1, actions: v2}; };
|
||||
// /NORMATIVE
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<nested-comp [config]="{animation: name, actions: [{ opacity: 0, duration: 0}, {opacity: 1, duration: duration }]}">
|
||||
</nested-comp>
|
||||
`
|
||||
})
|
||||
class MyApp {
|
||||
name = 'slide';
|
||||
duration = 100;
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 1,
|
||||
vars: 8,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'nested-comp');
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(
|
||||
0, 'config',
|
||||
$r3$.ɵbind($r3$.ɵpureFunction2(
|
||||
5, $e0_ff_2$, ctx.name,
|
||||
$r3$.ɵpureFunction1(
|
||||
3, $e0_ff_1$, $r3$.ɵpureFunction1(1, $e0_ff$, ctx.duration)))));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE (done by defineNgModule)
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
|
||||
[(NestedComp.ngComponentDef as ComponentDef<any>)];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
expect(renderComp(MyApp))
|
||||
.toEqual(`<nested-comp><p>slide</p><p>0</p><p>100</p></nested-comp>`);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function renderComp<T>(type: $r3$.ɵComponentType<T>): string {
|
||||
return toHtml(renderComponent(type));
|
||||
}
|
|
@ -1,98 +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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
|
||||
import * as $r3$ from '../../../src/core_render3_private_export';
|
||||
|
||||
/// See: `normative.md`
|
||||
describe('content projection', () => {
|
||||
type $RenderFlags$ = $r3$.ɵRenderFlags;
|
||||
|
||||
it('should support content projection', () => {
|
||||
type $SimpleComponent$ = SimpleComponent;
|
||||
type $ComplexComponent$ = ComplexComponent;
|
||||
type $MyApp$ = MyApp;
|
||||
|
||||
@Component({selector: 'simple', template: `<div><ng-content></ng-content></div>`})
|
||||
class SimpleComponent {
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: SimpleComponent,
|
||||
selectors: [['simple']],
|
||||
factory: function SimpleComponent_Factory(t) { return new (t || SimpleComponent)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function(rf: $RenderFlags$, ctx: $SimpleComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵprojectionDef();
|
||||
$r3$.ɵelement(0, 'div');
|
||||
$r3$.ɵprojection(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NORMATIVE
|
||||
const $pD_0P$: $r3$.ɵCssSelectorList[] =
|
||||
[[['span', 'title', 'toFirst']], [['span', 'title', 'toSecond']]];
|
||||
const $pD_0R$: string[] = ['span[title=toFirst]', 'span[title=toSecond]'];
|
||||
// /NORMATIVE
|
||||
|
||||
@Component({
|
||||
selector: 'complex',
|
||||
template: `
|
||||
<div id="first"><ng-content select="span[title=toFirst]"></ng-content></div>
|
||||
<div id="second"><ng-content select="span[title=toSecond]"></ng-content></div>`
|
||||
})
|
||||
class ComplexComponent {
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: ComplexComponent,
|
||||
selectors: [['complex']],
|
||||
factory: function ComplexComponent_Factory(t) { return new (t || ComplexComponent)(); },
|
||||
consts: 4,
|
||||
vars: 0,
|
||||
template: function(rf: $RenderFlags$, ctx: $ComplexComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵprojectionDef($pD_0P$, $pD_0R$);
|
||||
$r3$.ɵelement(0, 'div', ['id', 'first']);
|
||||
$r3$.ɵprojection(1, 1);
|
||||
$r3$.ɵelement(2, 'div', ['id', 'second']);
|
||||
$r3$.ɵprojection(3, 2);
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `<simple>content</simple>
|
||||
<complex></complex>`
|
||||
})
|
||||
class MyApp {
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 2,
|
||||
vars: 0,
|
||||
template: function(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'simple');
|
||||
$r3$.ɵtext(1, 'content');
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
},
|
||||
directives: () => [SimpleComponent]
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
|
@ -1,445 +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 {Component} from '../../../src/core';
|
||||
import * as $r3$ from '../../../src/core_render3_private_export';
|
||||
import {AttributeMarker} from '../../../src/render3';
|
||||
import {ComponentDef, InitialStylingFlags} from '../../../src/render3/interfaces/definition';
|
||||
import {ComponentFixture, renderComponent, toHtml} from '../render_util';
|
||||
|
||||
|
||||
/// See: `normative.md`
|
||||
describe('elements', () => {
|
||||
// Saving type as $any$, etc to simplify testing for compiler, as types aren't saved
|
||||
type $any$ = any;
|
||||
type $RenderFlags$ = $r3$.ɵRenderFlags;
|
||||
|
||||
it('should translate DOM structure', () => {
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
// Important: keep arrays outside of function to not create new instances.
|
||||
const $e0_attrs$ = ['class', 'my-app', 'title', 'Hello'];
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: `<div class="my-app" title="Hello">Hello <b>World</b>!</div>`
|
||||
})
|
||||
class MyComponent {
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-component']],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 5,
|
||||
vars: 0,
|
||||
template: function(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'div', $e0_attrs$);
|
||||
$r3$.ɵtext(1, 'Hello ');
|
||||
$r3$.ɵelementStart(2, 'b');
|
||||
$r3$.ɵtext(3, 'World');
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵtext(4, '!');
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
expect(toHtml(renderComponent(MyComponent)))
|
||||
.toEqual('<div class="my-app" title="Hello">Hello <b>World</b>!</div>');
|
||||
});
|
||||
|
||||
it('should support local refs', () => {
|
||||
type $LocalRefComp$ = LocalRefComp;
|
||||
|
||||
class Dir {
|
||||
value = 'one';
|
||||
|
||||
static ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
type: Dir,
|
||||
selectors: [['', 'dir', '']],
|
||||
factory: function DirA_Factory(t) { return new (t || Dir)(); },
|
||||
exportAs: 'dir'
|
||||
});
|
||||
}
|
||||
|
||||
// NORMATIVE
|
||||
const $e0_attrs$ = ['dir', ''];
|
||||
const $e0_locals$ = ['dir', 'dir', 'foo', ''];
|
||||
// /NORMATIVE
|
||||
|
||||
@Component({
|
||||
selector: 'local-ref-comp',
|
||||
template: `
|
||||
<div dir #dir="dir" #foo></div>
|
||||
{{ dir.value }} - {{ foo.tagName }}
|
||||
`
|
||||
})
|
||||
class LocalRefComp {
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: LocalRefComp,
|
||||
selectors: [['local-ref-comp']],
|
||||
factory: function LocalRefComp_Factory(t) { return new (t || LocalRefComp)(); },
|
||||
consts: 4,
|
||||
vars: 2,
|
||||
template: function LocalRefComp_Template(rf: $RenderFlags$, ctx: $LocalRefComp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'div', $e0_attrs$, $e0_locals$);
|
||||
$r3$.ɵtext(3);
|
||||
}
|
||||
if (rf & 2) {
|
||||
const $tmp$ = $r3$.ɵreference(1) as any;
|
||||
const $tmp_2$ = $r3$.ɵreference(2) as any;
|
||||
$r3$.ɵtextBinding(
|
||||
3, $r3$.ɵinterpolation2(' ', $tmp$.value, ' - ', $tmp_2$.tagName, ''));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE
|
||||
(LocalRefComp.ngComponentDef as ComponentDef<any>).directiveDefs = () => [Dir.ngDirectiveDef];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
const fixture = new ComponentFixture(LocalRefComp);
|
||||
expect(fixture.html).toEqual(`<div dir=""></div> one - DIV`);
|
||||
});
|
||||
|
||||
it('should support listeners', () => {
|
||||
type $ListenerComp$ = ListenerComp;
|
||||
|
||||
@Component({
|
||||
selector: 'listener-comp',
|
||||
template:
|
||||
`<button (click)="onClick()" (keypress)="onPress($event); onPress2($event)">Click</button>`
|
||||
})
|
||||
class ListenerComp {
|
||||
onClick() {}
|
||||
onPress(e: Event) {}
|
||||
onPress2(e: Event) {}
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: ListenerComp,
|
||||
selectors: [['listener-comp']],
|
||||
factory: function ListenerComp_Factory(t) { return new (t || ListenerComp)(); },
|
||||
consts: 2,
|
||||
vars: 0,
|
||||
template: function ListenerComp_Template(rf: $RenderFlags$, ctx: $ListenerComp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'button');
|
||||
$r3$.ɵlistener(
|
||||
'click', function ListenerComp_click_Handler() { return ctx.onClick(); });
|
||||
$r3$.ɵlistener('keypress', function ListenerComp_keypress_Handler($event: $any$) {
|
||||
ctx.onPress($event);
|
||||
return ctx.onPress2($event);
|
||||
});
|
||||
$r3$.ɵtext(1, 'Click');
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
const listenerComp = renderComponent(ListenerComp);
|
||||
expect(toHtml(listenerComp)).toEqual('<button>Click</button>');
|
||||
});
|
||||
|
||||
it('should support namespaced attributes', () => {
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
// Important: keep arrays outside of function to not create new instances.
|
||||
const $e0_attrs$ = [
|
||||
// class="my-app"
|
||||
'class',
|
||||
'my-app',
|
||||
// foo:bar="baz"
|
||||
AttributeMarker.NamespaceURI,
|
||||
'http://someuri/foo',
|
||||
'foo:bar',
|
||||
'baz',
|
||||
// title="Hello"
|
||||
'title',
|
||||
'Hello',
|
||||
// foo:qux="quacks"
|
||||
AttributeMarker.NamespaceURI,
|
||||
'http://someuri/foo',
|
||||
'foo:qux',
|
||||
'quacks',
|
||||
];
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template:
|
||||
`<div xmlns:foo="http://someuri/foo" class="my-app" foo:bar="baz" title="Hello" foo:qux="quacks">Hello <b>World</b>!</div>`
|
||||
})
|
||||
class MyComponent {
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-component']],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 5,
|
||||
vars: 0,
|
||||
template: function(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'div', $e0_attrs$);
|
||||
$r3$.ɵtext(1, 'Hello ');
|
||||
$r3$.ɵelementStart(2, 'b');
|
||||
$r3$.ɵtext(3, 'World');
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵtext(4, '!');
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
expect(toHtml(renderComponent(MyComponent)))
|
||||
.toEqual(
|
||||
'<div class="my-app" foo:bar="baz" foo:qux="quacks" title="Hello">Hello <b>World</b>!</div>');
|
||||
});
|
||||
|
||||
describe('bindings', () => {
|
||||
it('should bind to property', () => {
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
@Component({selector: 'my-component', template: `<div [id]="someProperty"></div>`})
|
||||
class MyComponent {
|
||||
someProperty: string = 'initial';
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-component']],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 1,
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'div');
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, 'id', $r3$.ɵbind(ctx.someProperty));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
const comp = renderComponent(MyComponent);
|
||||
expect(toHtml(comp)).toEqual('<div id="initial"></div>');
|
||||
|
||||
comp.someProperty = 'changed';
|
||||
$r3$.ɵdetectChanges(comp);
|
||||
expect(toHtml(comp)).toEqual('<div id="changed"></div>');
|
||||
});
|
||||
|
||||
it('should bind to attribute', () => {
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
@Component({selector: 'my-component', template: `<div [attr.title]="someAttribute"></div>`})
|
||||
class MyComponent {
|
||||
someAttribute: string = 'initial';
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-component']],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 1,
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'div');
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementAttribute(0, 'title', $r3$.ɵbind(ctx.someAttribute));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
const comp = renderComponent(MyComponent);
|
||||
expect(toHtml(comp)).toEqual('<div title="initial"></div>');
|
||||
|
||||
comp.someAttribute = 'changed';
|
||||
$r3$.ɵdetectChanges(comp);
|
||||
expect(toHtml(comp)).toEqual('<div title="changed"></div>');
|
||||
});
|
||||
|
||||
it('should bind to a specific class', () => {
|
||||
const c1: (string | InitialStylingFlags | boolean)[] = ['foo'];
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
@Component({selector: 'my-component', template: `<div [class.foo]="someFlag"></div>`})
|
||||
class MyComponent {
|
||||
someFlag: boolean = false;
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-component']],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'div');
|
||||
$r3$.ɵelementStyling(c1);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementClassProp(0, 0, ctx.someFlag);
|
||||
$r3$.ɵelementStylingApply(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
const comp = renderComponent(MyComponent);
|
||||
|
||||
// This is a fix for a change in how Domino renders this on the server in v2.1.0
|
||||
const source = toHtml(comp);
|
||||
const matches = source === '<div></div>' || source === '<div class=""></div>';
|
||||
expect(matches).toBeTruthy();
|
||||
|
||||
comp.someFlag = true;
|
||||
$r3$.ɵdetectChanges(comp);
|
||||
expect(toHtml(comp)).toEqual('<div class="foo"></div>');
|
||||
});
|
||||
|
||||
it('should bind to a specific style', () => {
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
const c0 = ['color', 'width'];
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: `<div [style.color]="someColor" [style.width.px]="someWidth"></div>`
|
||||
})
|
||||
class MyComponent {
|
||||
someColor: string = 'red';
|
||||
someWidth: number = 50;
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-component']],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'div');
|
||||
$r3$.ɵelementStyling(null, c0);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementStyleProp(0, 0, ctx.someColor);
|
||||
$r3$.ɵelementStyleProp(0, 1, ctx.someWidth, 'px');
|
||||
$r3$.ɵelementStylingApply(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
const comp = renderComponent(MyComponent);
|
||||
expect(toHtml(comp)).toEqual('<div style="color: red; width: 50px;"></div>');
|
||||
|
||||
comp.someColor = 'blue';
|
||||
comp.someWidth = 100;
|
||||
$r3$.ɵdetectChanges(comp);
|
||||
expect(toHtml(comp)).toEqual('<div style="color: blue; width: 100px;"></div>');
|
||||
});
|
||||
|
||||
it('should bind to many and keep order', () => {
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
const c0 = ['foo'];
|
||||
const c1 = ['color', InitialStylingFlags.VALUES_MODE, 'color', 'red'];
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template:
|
||||
`<div [id]="someString+1" [class.foo]="someString=='initial'" [attr.style]="'color: red;'"></div>`
|
||||
})
|
||||
class MyComponent {
|
||||
someString: string = 'initial';
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-component']],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 1,
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'div');
|
||||
$r3$.ɵelementStyling(c0, c1);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, 'id', $r3$.ɵbind(ctx.someString + 1));
|
||||
$r3$.ɵelementClassProp(0, 0, ctx.someString == 'initial');
|
||||
$r3$.ɵelementStylingApply(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
const comp = renderComponent(MyComponent);
|
||||
expect(toHtml(comp)).toEqual('<div class="foo" id="initial1" style="color: red;"></div>');
|
||||
|
||||
comp.someString = 'changed';
|
||||
$r3$.ɵdetectChanges(comp);
|
||||
expect(toHtml(comp)).toEqual('<div class="" id="changed1" style="color: red;"></div>');
|
||||
});
|
||||
|
||||
it('should bind [class] and [style] to the element', () => {
|
||||
type $StyleComponent$ = StyleComponent;
|
||||
|
||||
@Component(
|
||||
{selector: 'style-comp', template: `<div [class]="classExp" [style]="styleExp"></div>`})
|
||||
class StyleComponent {
|
||||
classExp: string[]|string = 'some-name';
|
||||
styleExp: {[name: string]: string} = {'background-color': 'red'};
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: StyleComponent,
|
||||
selectors: [['style-comp']],
|
||||
factory: function StyleComponent_Factory(t) { return new (t || StyleComponent)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function StyleComponent_Template(rf: $RenderFlags$, ctx: $StyleComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'div');
|
||||
$r3$.ɵelementStyling();
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementStylingMap(0, ctx.classExp, ctx.styleExp);
|
||||
$r3$.ɵelementStylingApply(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
const styleFixture = new ComponentFixture(StyleComponent);
|
||||
expect(styleFixture.html)
|
||||
.toEqual(`<div class="some-name" style="background-color: red;"></div>`);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,147 +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 {NgForOfContext} from '@angular/common';
|
||||
import {Component} from '../../../src/core';
|
||||
import * as $r3$ from '../../../src/core_render3_private_export';
|
||||
import {NgForOf} from '../common_with_def';
|
||||
|
||||
/// See: `normative.md`
|
||||
describe('i18n', () => {
|
||||
type $RenderFlags$ = $r3$.ɵRenderFlags;
|
||||
|
||||
it('should support html', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
const $msg_1$ = `{$START_P}contenu{$END_P}`;
|
||||
const $i18n_1$ = $r3$.ɵi18nMapping($msg_1$, [{START_P: 1}]);
|
||||
|
||||
@Component({selector: 'my-app', template: `<div i18n><p>content</p></div>`})
|
||||
class MyApp {
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 2,
|
||||
vars: 0,
|
||||
template: function(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'div');
|
||||
$r3$.ɵelementStart(1, 'p');
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵi18nApply(1, $i18n_1$[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should support expressions', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
const $msg_1$ = `contenu: {$EXP_1}`;
|
||||
const $i18n_1$ = $r3$.ɵi18nMapping($msg_1$, null, [{EXP_1: 1}]);
|
||||
|
||||
@Component({selector: 'my-app', template: `<div i18n>content: {{exp1}}</div>`})
|
||||
class MyApp {
|
||||
exp1 = '1';
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 2,
|
||||
vars: 1,
|
||||
template: function(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'div');
|
||||
$r3$.ɵtext(1);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵi18nApply(1, $i18n_1$[0]);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵtextBinding(3, $r3$.ɵbind(ctx.exp1));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should support expressions in attributes', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
const $msg_1$ = `titre: {$EXP_1}`;
|
||||
const $i18n_1$ = $r3$.ɵi18nExpMapping($msg_1$, {EXP_1: 1});
|
||||
|
||||
@Component({selector: 'my-app', template: `<div i18n><p title="title: {{exp1}}"></p></div>`})
|
||||
class MyApp {
|
||||
exp1 = '1';
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 2,
|
||||
vars: 1,
|
||||
template: function(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'div');
|
||||
$r3$.ɵelementStart(1, 'p');
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, 'title', $r3$.ɵi18nInterpolation1($i18n_1$, ctx.exp1));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should support embedded templates', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
const $msg_1$ = `{$START_LI}valeur: {$EXP_1}!{$END_LI}`;
|
||||
const $i18n_1$ = $r3$.ɵi18nMapping(
|
||||
$msg_1$, [{START_LI: 1}, {START_LI: 0}], [null, {EXP_1: 1}], ['START_LI']);
|
||||
|
||||
function liTemplate(rf1: $RenderFlags$, row: NgForOfContext<string>) {
|
||||
if (rf1 & 1) {
|
||||
$r3$.ɵelementStart(0, 'li');
|
||||
$r3$.ɵtext(1);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵi18nApply(0, $i18n_1$[1]);
|
||||
}
|
||||
if (rf1 & 2) {
|
||||
$r3$.ɵtextBinding(1, $r3$.ɵbind(row.$implicit));
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `<ul i18n><li *ngFor="let item of items">value: {{item}}</li></ul>`
|
||||
})
|
||||
class MyApp {
|
||||
items: string[] = ['1', '2'];
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
selectors: [['my-app']],
|
||||
consts: 2,
|
||||
vars: 1,
|
||||
template: (rf: $RenderFlags$, myApp: $MyApp$) => {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'ul');
|
||||
$r3$.ɵtemplate(1, liTemplate, 2, 1, null, ['ngForOf', '']);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵi18nApply(1, $i18n_1$[0]);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(1, 'ngForOf', $r3$.ɵbind(myApp.items));
|
||||
}
|
||||
},
|
||||
directives: () => [NgForOf]
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,205 +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 {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';
|
||||
|
||||
|
||||
|
||||
/// See: `normative.md`
|
||||
describe('injection', () => {
|
||||
type $RenderFlags$ = $r3$.ɵRenderFlags;
|
||||
|
||||
describe('directives', () => {
|
||||
// Directives (and Components) should use `directiveInject`
|
||||
it('should inject ChangeDetectorRef', () => {
|
||||
type $MyComp$ = MyComp;
|
||||
type $MyApp$ = MyApp;
|
||||
|
||||
@Component({selector: 'my-comp', template: `{{ value }}`})
|
||||
class MyComp {
|
||||
value: string;
|
||||
constructor(public cdr: ChangeDetectorRef) { this.value = (cdr.constructor as any).name; }
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComp,
|
||||
selectors: [['my-comp']],
|
||||
factory: function MyComp_Factory(t) {
|
||||
return new (t || MyComp)($r3$.ɵdirectiveInject(ChangeDetectorRef as any));
|
||||
},
|
||||
consts: 1,
|
||||
vars: 1,
|
||||
template: function MyComp_Template(rf: $RenderFlags$, ctx: $MyComp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtext(0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵtextBinding(0, $r3$.ɵbind(ctx.value));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
class MyApp {
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
/** <my-comp></my-comp> */
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'my-comp');
|
||||
}
|
||||
},
|
||||
directives: () => [MyComp]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const app = renderComponent(MyApp);
|
||||
// ChangeDetectorRef is the token, ViewRef is historically the constructor
|
||||
expect(toHtml(app)).toEqual('<my-comp>ViewRef</my-comp>');
|
||||
});
|
||||
|
||||
it('should inject attributes', () => {
|
||||
type $MyComp$ = MyComp;
|
||||
type $MyApp$ = MyApp;
|
||||
|
||||
@Component({selector: 'my-comp', template: `{{ title }}`})
|
||||
class MyComp {
|
||||
constructor(@Attribute('title') public title: string|undefined) {}
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComp,
|
||||
selectors: [['my-comp']],
|
||||
factory: function MyComp_Factory(t) {
|
||||
return new (t || MyComp)($r3$.ɵinjectAttribute('title'));
|
||||
},
|
||||
consts: 1,
|
||||
vars: 1,
|
||||
template: function MyComp_Template(rf: $RenderFlags$, ctx: $MyComp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtext(0);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵtextBinding(0, $r3$.ɵbind(ctx.title));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
class MyApp {
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
/** <my-comp></my-comp> */
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'my-comp', e0_attrs);
|
||||
}
|
||||
},
|
||||
directives: () => [MyComp]
|
||||
});
|
||||
}
|
||||
const e0_attrs = ['title', 'WORKS'];
|
||||
const app = renderComponent(MyApp);
|
||||
// ChangeDetectorRef is the token, ViewRef is historically the constructor
|
||||
expect(toHtml(app)).toEqual('<my-comp title="WORKS">WORKS</my-comp>');
|
||||
});
|
||||
|
||||
// TODO(misko): enable once `providers` and `viewProvdires` are implemented.
|
||||
xit('should inject into an injectable', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
|
||||
@Injectable()
|
||||
class ServiceA {
|
||||
// NORMATIVE
|
||||
static ngInjectableDef = defineInjectable({
|
||||
factory: function ServiceA_Factory() { return new ServiceA(); },
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class ServiceB {
|
||||
// NORMATIVE
|
||||
static ngInjectableDef = defineInjectable({
|
||||
factory: function ServiceA_Factory() { return new ServiceB(); },
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: '',
|
||||
providers: [ServiceA],
|
||||
viewProviders: [ServiceB],
|
||||
})
|
||||
class MyApp {
|
||||
constructor(serviceA: ServiceA, serviceB: ServiceB, injector: Injector) {}
|
||||
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
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$) {},
|
||||
features: [ProvidersFeature([ServiceA], [ServiceB])]
|
||||
});
|
||||
}
|
||||
const e0_attrs = ['title', 'WORKS'];
|
||||
const app = renderComponent(MyApp);
|
||||
// ChangeDetectorRef is the token, ViewRef is historically the constructor
|
||||
expect(toHtml(app)).toEqual('<my-comp title="WORKS">WORKS</my-comp>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('services', () => {
|
||||
// Services should use `inject`
|
||||
@Injectable()
|
||||
class ServiceA {
|
||||
constructor(@Inject(String) name: String, injector: Injector) {}
|
||||
|
||||
// NORMATIVE
|
||||
static ngInjectableDef = defineInjectable({
|
||||
factory: function ServiceA_Factory() {
|
||||
return new ServiceA(inject(String), inject(INJECTOR));
|
||||
},
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class ServiceB {
|
||||
constructor(serviceA: ServiceA, @SkipSelf() injector: Injector) {}
|
||||
// NORMATIVE
|
||||
static ngInjectableDef = defineInjectable({
|
||||
factory: function ServiceA_Factory() {
|
||||
return new ServiceB(inject(ServiceA), inject(INJECTOR, InjectFlags.SkipSelf) !);
|
||||
},
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -1,115 +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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
|
||||
import * as $r3$ from '../../../src/core_render3_private_export';
|
||||
import {ComponentDef} from '../../../src/render3/interfaces/definition';
|
||||
import {renderComponent, toHtml} from '../render_util';
|
||||
|
||||
|
||||
/// See: `normative.md`
|
||||
describe('lifecycle hooks', () => {
|
||||
let events: string[] = [];
|
||||
let simpleLayout: SimpleLayout;
|
||||
|
||||
type $RenderFlags$ = $r3$.ɵRenderFlags;
|
||||
type $LifecycleComp$ = LifecycleComp;
|
||||
type $SimpleLayout$ = SimpleLayout;
|
||||
|
||||
beforeEach(() => { events = []; });
|
||||
|
||||
@Component({selector: 'lifecycle-comp', template: ``})
|
||||
class LifecycleComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input('name') nameMin !: string;
|
||||
|
||||
ngOnChanges() { events.push('changes' + this.nameMin); }
|
||||
|
||||
ngOnInit() { events.push('init' + this.nameMin); }
|
||||
ngDoCheck() { events.push('check' + this.nameMin); }
|
||||
|
||||
ngAfterContentInit() { events.push('content init' + this.nameMin); }
|
||||
ngAfterContentChecked() { events.push('content check' + this.nameMin); }
|
||||
|
||||
ngAfterViewInit() { events.push('view init' + this.nameMin); }
|
||||
ngAfterViewChecked() { events.push('view check' + this.nameMin); }
|
||||
|
||||
ngOnDestroy() { events.push(this.nameMin); }
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: LifecycleComp,
|
||||
selectors: [['lifecycle-comp']],
|
||||
factory: function LifecycleComp_Factory(t) { return new (t || LifecycleComp)(); },
|
||||
consts: 0,
|
||||
vars: 0,
|
||||
template: function LifecycleComp_Template(rf: $RenderFlags$, ctx: $LifecycleComp$) {},
|
||||
inputs: {nameMin: ['name', 'nameMin']},
|
||||
features: [$r3$.ɵNgOnChangesFeature]
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'simple-layout',
|
||||
template: `
|
||||
<lifecycle-comp [name]="name1"></lifecycle-comp>
|
||||
<lifecycle-comp [name]="name2"></lifecycle-comp>
|
||||
`
|
||||
})
|
||||
class SimpleLayout {
|
||||
name1 = '1';
|
||||
name2 = '2';
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: SimpleLayout,
|
||||
selectors: [['simple-layout']],
|
||||
factory: function SimpleLayout_Factory(t) {
|
||||
return simpleLayout = new (t || SimpleLayout)();
|
||||
},
|
||||
consts: 2,
|
||||
vars: 2,
|
||||
template: function SimpleLayout_Template(rf: $RenderFlags$, ctx: $SimpleLayout$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'lifecycle-comp');
|
||||
$r3$.ɵelement(1, 'lifecycle-comp');
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, 'name', $r3$.ɵbind(ctx.name1));
|
||||
$r3$.ɵelementProperty(1, 'name', $r3$.ɵbind(ctx.name2));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE
|
||||
(SimpleLayout.ngComponentDef as ComponentDef<any>).directiveDefs = [LifecycleComp.ngComponentDef];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
it('should gen hooks with a few simple components', () => {
|
||||
expect(toHtml(renderComponent(SimpleLayout)))
|
||||
.toEqual(`<lifecycle-comp></lifecycle-comp><lifecycle-comp></lifecycle-comp>`);
|
||||
expect(events).toEqual([
|
||||
'changes1', 'init1', 'check1', 'changes2', 'init2', 'check2', 'content init1',
|
||||
'content check1', 'content init2', 'content check2', 'view init1', 'view check1',
|
||||
'view init2', 'view check2'
|
||||
]);
|
||||
|
||||
events = [];
|
||||
simpleLayout.name1 = '-one';
|
||||
simpleLayout.name2 = '-two';
|
||||
$r3$.ɵdetectChanges(simpleLayout);
|
||||
expect(events).toEqual([
|
||||
'changes-one', 'check-one', 'changes-two', 'check-two', 'content check-one',
|
||||
'content check-two', 'view check-one', 'view check-two'
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
|
@ -1,92 +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 {ElementRef, TemplateRef} from '@angular/core';
|
||||
|
||||
import {Component} from '../../../src/core';
|
||||
import * as $r3$ from '../../../src/core_render3_private_export';
|
||||
import {ComponentFixture} from '../render_util';
|
||||
|
||||
|
||||
/// See: `normative.md`
|
||||
describe('local references', () => {
|
||||
type $RenderFlags$ = $r3$.ɵRenderFlags;
|
||||
|
||||
it('should translate DOM structure', () => {
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
@Component(
|
||||
{selector: 'my-component', template: `<input #user value="World">Hello, {{user.value}}!`})
|
||||
class MyComponent {
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-component']],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 3,
|
||||
vars: 1,
|
||||
template: function(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
let l1_user: any;
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(0, 'input', ['value', 'World'], ['user', '']);
|
||||
$r3$.ɵtext(2);
|
||||
}
|
||||
if (rf & 2) {
|
||||
l1_user = $r3$.ɵreference<any>(1);
|
||||
$r3$.ɵtextBinding(2, $r3$.ɵinterpolation1('Hello, ', l1_user.value, '!'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// NORMATIVE
|
||||
}
|
||||
|
||||
const fixture = new ComponentFixture(MyComponent);
|
||||
expect(fixture.html).toEqual(`<input value="World">Hello, World!`);
|
||||
});
|
||||
|
||||
it('should expose TemplateRef when a local ref is placed on ng-template', () => {
|
||||
type $MyComponent$ = MyComponent;
|
||||
type $any$ = any;
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: `<ng-template #tpl></ng-template>{{isTemplateRef(tpl)}}`
|
||||
})
|
||||
class MyComponent {
|
||||
isTemplateRef(tplRef: any): boolean { return tplRef.createEmbeddedView != null; }
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-component']],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 3,
|
||||
vars: 1,
|
||||
template: function(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
let l1_tpl: any;
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtemplate(
|
||||
0, MyComponent_Template_0, 0, 0, null, null, ['tpl', ''],
|
||||
$r3$.ɵtemplateRefExtractor);
|
||||
$r3$.ɵtext(2);
|
||||
}
|
||||
if (rf & 2) {
|
||||
l1_tpl = $r3$.ɵreference<any>(1);
|
||||
$r3$.ɵtextBinding(2, $r3$.ɵinterpolation1('', ctx.isTemplateRef(l1_tpl), ''));
|
||||
}
|
||||
|
||||
function MyComponent_Template_0(rf1: $RenderFlags$, ctx1: $any$) {}
|
||||
}
|
||||
});
|
||||
// NORMATIVE
|
||||
}
|
||||
|
||||
const fixture = new ComponentFixture(MyComponent);
|
||||
expect(fixture.html).toEqual(`true`);
|
||||
});
|
||||
});
|
|
@ -1,83 +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 * as $core$ from '../../../index';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
|
||||
import * as $r3$ from '../../../src/core_render3_private_export';
|
||||
import {renderComponent, toHtml} from '../render_util';
|
||||
|
||||
|
||||
/// See: `normative.md`
|
||||
xdescribe('NgModule', () => {
|
||||
|
||||
interface Injectable {
|
||||
providedIn?: /*InjectorDefType<any>*/ any;
|
||||
factory: Function;
|
||||
}
|
||||
|
||||
function defineInjectable(opts: Injectable): Injectable {
|
||||
// This class should be imported from https://github.com/angular/angular/pull/20850
|
||||
return opts;
|
||||
}
|
||||
function defineInjector(opts: any): any {
|
||||
// This class should be imported from https://github.com/angular/angular/pull/20850
|
||||
return opts;
|
||||
}
|
||||
it('should convert module', () => {
|
||||
@Injectable()
|
||||
class Toast {
|
||||
constructor(name: String) {}
|
||||
// NORMATIVE
|
||||
static ngInjectableDef = defineInjectable({
|
||||
factory: function Toast_Factory() { return new Toast($r3$.ɵdirectiveInject(String)); },
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
class CommonModule {
|
||||
// NORMATIVE
|
||||
static ngInjectorDef = defineInjector({});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
providers: [Toast, {provide: String, useValue: 'Hello'}],
|
||||
imports: [CommonModule],
|
||||
})
|
||||
class MyModule {
|
||||
constructor(toast: Toast) {}
|
||||
// NORMATIVE
|
||||
static ngInjectorDef = defineInjector({
|
||||
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.
|
||||
{provide: String, useValue: 'Hello'}
|
||||
],
|
||||
imports: [CommonModule]
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
@Injectable(/*{MyModule}*/)
|
||||
class BurntToast {
|
||||
constructor(@Optional() toast: Toast|null, name: String) {}
|
||||
// NORMATIVE
|
||||
static ngInjectableDef = defineInjectable({
|
||||
providedIn: MyModule,
|
||||
factory: function BurntToast_Factory() {
|
||||
return new BurntToast(
|
||||
$r3$.ɵdirectiveInject(Toast, $core$.InjectFlags.Optional),
|
||||
$r3$.ɵdirectiveInject(String));
|
||||
},
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
});
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
This folder contains canonical examples of how the Ivy compiler translates annotations into code
|
||||
|
||||
- The specs are marked with `NORMATIVE` => `/NORMATIVE` comments which designates what the compiler is expected to generate.
|
||||
- All local variable names are considered non-normative (informative). They should be wrapped in `$` on each end to simplify testing on the compiler side.
|
||||
|
||||
A common trick in spec files is to map types to `$x$` (such as `boolean` => `$boolean$`, etc) to simplify testing for compiler, as types aren't saved. (See bullet above).
|
||||
```
|
||||
type $boolean$ = boolean;
|
||||
type $any$ = any;
|
||||
type $number$ = number;
|
||||
```
|
|
@ -1,219 +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 {Component, Directive, Input, OnDestroy, Pipe, PipeTransform, TemplateRef, ViewContainerRef} from '../../../src/core';
|
||||
import * as $r3$ from '../../../src/core_render3_private_export';
|
||||
import {ComponentDef} from '../../../src/render3/interfaces/definition';
|
||||
import {containerEl, renderComponent, toHtml} from '../render_util';
|
||||
|
||||
|
||||
|
||||
/// See: `normative.md`
|
||||
describe('pipes', () => {
|
||||
type $any$ = any;
|
||||
type $RenderFlags$ = $r3$.ɵRenderFlags;
|
||||
|
||||
let myPipeTransformCalls = 0;
|
||||
let myPurePipeTransformCalls = 0;
|
||||
|
||||
@Pipe({
|
||||
name: 'myPipe',
|
||||
pure: false,
|
||||
})
|
||||
class MyPipe implements PipeTransform,
|
||||
OnDestroy {
|
||||
private numberOfBang = 1;
|
||||
|
||||
transform(value: string, size: number): string {
|
||||
let result = value.substring(size);
|
||||
for (let i = 0; i < this.numberOfBang; i++) result += '!';
|
||||
this.numberOfBang++;
|
||||
myPipeTransformCalls++;
|
||||
return result;
|
||||
}
|
||||
|
||||
ngOnDestroy() { this.numberOfBang = 1; }
|
||||
|
||||
// NORMATIVE
|
||||
static ngPipeDef = $r3$.ɵdefinePipe({
|
||||
name: 'myPipe',
|
||||
type: MyPipe,
|
||||
factory: function MyPipe_Factory(t) { return new (t || MyPipe)(); },
|
||||
pure: false,
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
@Pipe({
|
||||
name: 'myPurePipe',
|
||||
pure: true,
|
||||
})
|
||||
class MyPurePipe implements PipeTransform {
|
||||
transform(value: string, size: number): string {
|
||||
myPurePipeTransformCalls++;
|
||||
return value.substring(size);
|
||||
}
|
||||
|
||||
// NORMATIVE
|
||||
static ngPipeDef = $r3$.ɵdefinePipe({
|
||||
name: 'myPurePipe',
|
||||
type: MyPurePipe,
|
||||
factory: function MyPurePipe_Factory(t) { return new (t || MyPurePipe)(); },
|
||||
pure: true,
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
it('should render pipes', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
myPipeTransformCalls = 0;
|
||||
myPurePipeTransformCalls = 0;
|
||||
|
||||
@Component({template: `{{name | myPipe:size | myPurePipe:size }}`})
|
||||
class MyApp {
|
||||
name = '12World';
|
||||
size = 1;
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 3,
|
||||
vars: 7,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtext(0);
|
||||
$r3$.ɵpipe(1, 'myPipe');
|
||||
$r3$.ɵpipe(2, 'myPurePipe');
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵtextBinding(
|
||||
0,
|
||||
$r3$.ɵinterpolation1(
|
||||
'', $r3$.ɵpipeBind2(1, 4, $r3$.ɵpipeBind2(2, 1, ctx.name, ctx.size), ctx.size),
|
||||
''));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).pipeDefs =
|
||||
() => [MyPurePipe.ngPipeDef, MyPipe.ngPipeDef];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
let myApp: MyApp = renderComponent(MyApp);
|
||||
expect(toHtml(containerEl)).toEqual('World!');
|
||||
expect(myPurePipeTransformCalls).toEqual(1);
|
||||
expect(myPipeTransformCalls).toEqual(1);
|
||||
|
||||
$r3$.ɵdetectChanges(myApp);
|
||||
expect(toHtml(containerEl)).toEqual('World!!');
|
||||
expect(myPurePipeTransformCalls).toEqual(1);
|
||||
expect(myPipeTransformCalls).toEqual(2);
|
||||
|
||||
myApp.name = '34WORLD';
|
||||
$r3$.ɵdetectChanges(myApp);
|
||||
expect(toHtml(containerEl)).toEqual('WORLD!!!');
|
||||
expect(myPurePipeTransformCalls).toEqual(2);
|
||||
expect(myPipeTransformCalls).toEqual(3);
|
||||
});
|
||||
|
||||
it('should render many pipes and forward the first instance (pure or impure pipe)', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
myPipeTransformCalls = 0;
|
||||
myPurePipeTransformCalls = 0;
|
||||
|
||||
@Directive({
|
||||
selector: '[oneTimeIf]',
|
||||
})
|
||||
class OneTimeIf {
|
||||
@Input() oneTimeIf: any;
|
||||
constructor(private view: ViewContainerRef, private template: TemplateRef<any>) {}
|
||||
ngDoCheck(): void {
|
||||
if (this.oneTimeIf) {
|
||||
this.view.createEmbeddedView(this.template);
|
||||
}
|
||||
}
|
||||
// NORMATIVE
|
||||
static ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
type: OneTimeIf,
|
||||
selectors: [['', 'oneTimeIf', '']],
|
||||
factory: function OneTimeIf_Factory(t) {
|
||||
return new (t || OneTimeIf)(
|
||||
$r3$.ɵdirectiveInject(ViewContainerRef as any),
|
||||
$r3$.ɵdirectiveInject(TemplateRef as any));
|
||||
},
|
||||
inputs: {oneTimeIf: 'oneTimeIf'}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
function MyApp_div_Template_4(rf: $RenderFlags$, ctx: any) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'div');
|
||||
$r3$.ɵtext(1);
|
||||
$r3$.ɵpipe(2, 'myPurePipe');
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
const $comp$ = $r3$.ɵnextContext();
|
||||
$r3$.ɵtextBinding(
|
||||
1, $r3$.ɵinterpolation1('', $r3$.ɵpipeBind2(2, 1, $comp$.name, $comp$.size), ''));
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `{{name | myPurePipe:size}}{{name | myPurePipe:size}}
|
||||
<div *oneTimeIf="more">{{name | myPurePipe:size}}</div>`
|
||||
})
|
||||
class MyApp {
|
||||
name = '1World';
|
||||
size = 1;
|
||||
more = true;
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 5,
|
||||
vars: 9,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵtext(0);
|
||||
$r3$.ɵpipe(1, 'myPurePipe');
|
||||
$r3$.ɵtext(2);
|
||||
$r3$.ɵpipe(3, 'myPurePipe');
|
||||
$r3$.ɵtemplate(4, MyApp_div_Template_4, 3, 4, '', ['oneTimeIf', '']);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵtextBinding(
|
||||
0, $r3$.ɵinterpolation1('', $r3$.ɵpipeBind2(1, 3, ctx.name, ctx.size), ''));
|
||||
$r3$.ɵtextBinding(
|
||||
2, $r3$.ɵinterpolation1('', $r3$.ɵpipeBind2(3, 6, ctx.name, ctx.size), ''));
|
||||
$r3$.ɵelementProperty(4, 'oneTimeIf', $r3$.ɵbind(ctx.more));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs = [OneTimeIf.ngDirectiveDef];
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).pipeDefs = [MyPurePipe.ngPipeDef];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
let myApp: MyApp = renderComponent(MyApp);
|
||||
expect(toHtml(containerEl)).toEqual('WorldWorld<div>World</div>');
|
||||
expect(myPurePipeTransformCalls).toEqual(3);
|
||||
expect(myPipeTransformCalls).toEqual(0);
|
||||
});
|
||||
});
|
|
@ -1,188 +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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
|
||||
import * as $r3$ from '../../../src/core_render3_private_export';
|
||||
import {ComponentDef} from '../../../src/render3/interfaces/definition';
|
||||
import {getDirectiveOnNode, renderComponent, toHtml} from '../render_util';
|
||||
|
||||
|
||||
/// See: `normative.md`
|
||||
describe('queries', () => {
|
||||
type $RenderFlags$ = $r3$.ɵRenderFlags;
|
||||
type $number$ = number;
|
||||
let someDir: SomeDirective;
|
||||
|
||||
@Directive({
|
||||
selector: '[someDir]',
|
||||
})
|
||||
class SomeDirective {
|
||||
static ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
type: SomeDirective,
|
||||
selectors: [['', 'someDir', '']],
|
||||
factory: function SomeDirective_Factory(t) { return someDir = new (t || SomeDirective)(); }
|
||||
});
|
||||
}
|
||||
|
||||
it('should support view queries', () => {
|
||||
type $ViewQueryComponent$ = ViewQueryComponent;
|
||||
|
||||
// NORMATIVE
|
||||
const $e1_attrs$ = ['someDir', ''];
|
||||
// /NORMATIVE
|
||||
|
||||
@Component({
|
||||
selector: 'view-query-component',
|
||||
template: `
|
||||
<div someDir></div>
|
||||
`
|
||||
})
|
||||
class ViewQueryComponent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChild(SomeDirective) someDir !: SomeDirective;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChildren(SomeDirective) someDirList !: QueryList<SomeDirective>;
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: ViewQueryComponent,
|
||||
selectors: [['view-query-component']],
|
||||
factory: function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); },
|
||||
consts: 3,
|
||||
vars: 0,
|
||||
template: function ViewQueryComponent_Template(
|
||||
rf: $RenderFlags$, ctx: $ViewQueryComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelement(2, 'div', $e1_attrs$);
|
||||
}
|
||||
},
|
||||
viewQuery: function ViewQueryComponent_Query(rf: $RenderFlags$, ctx: $ViewQueryComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵquery(0, SomeDirective, false);
|
||||
$r3$.ɵquery(1, SomeDirective, false);
|
||||
}
|
||||
if (rf & 2) {
|
||||
let $tmp$: any;
|
||||
$r3$.ɵqueryRefresh($tmp$ = $r3$.ɵload<QueryList<any>>(0)) &&
|
||||
(ctx.someDir = $tmp$.first);
|
||||
$r3$.ɵqueryRefresh($tmp$ = $r3$.ɵload<QueryList<any>>(1)) &&
|
||||
(ctx.someDirList = $tmp$ as QueryList<any>);
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE
|
||||
(ViewQueryComponent.ngComponentDef as ComponentDef<any>).directiveDefs =
|
||||
[SomeDirective.ngDirectiveDef];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
const viewQueryComp = renderComponent(ViewQueryComponent);
|
||||
expect(viewQueryComp.someDir).toEqual(someDir);
|
||||
expect((viewQueryComp.someDirList as QueryList<SomeDirective>).toArray()).toEqual([someDir !]);
|
||||
});
|
||||
|
||||
it('should support content queries', () => {
|
||||
type $MyApp$ = MyApp;
|
||||
type $ContentQueryComponent$ = ContentQueryComponent;
|
||||
|
||||
let contentQueryComp: ContentQueryComponent;
|
||||
|
||||
@Component({
|
||||
selector: 'content-query-component',
|
||||
template: `
|
||||
<div><ng-content></ng-content></div>
|
||||
`
|
||||
})
|
||||
class ContentQueryComponent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild(SomeDirective) someDir !: SomeDirective;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren(SomeDirective) someDirList !: QueryList<SomeDirective>;
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: ContentQueryComponent,
|
||||
selectors: [['content-query-component']],
|
||||
factory: function ContentQueryComponent_Factory(t) {
|
||||
return new (t || ContentQueryComponent)();
|
||||
},
|
||||
consts: 2,
|
||||
vars: 0,
|
||||
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$) {
|
||||
let $tmp$: any;
|
||||
const $instance$ = $r3$.ɵload<ContentQueryComponent>(dirIndex);
|
||||
$r3$.ɵqueryRefresh($tmp$ = $r3$.ɵloadQueryList<any>(queryStartIndex)) &&
|
||||
($instance$.someDir = $tmp$.first);
|
||||
$r3$.ɵqueryRefresh($tmp$ = $r3$.ɵloadQueryList<any>(queryStartIndex + 1)) &&
|
||||
($instance$.someDirList = $tmp$);
|
||||
},
|
||||
template: function ContentQueryComponent_Template(
|
||||
rf: $number$, ctx: $ContentQueryComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵprojectionDef();
|
||||
$r3$.ɵelementStart(0, 'div');
|
||||
$r3$.ɵprojection(1);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
const $e2_attrs$ = ['someDir', ''];
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<content-query-component>
|
||||
<div someDir></div>
|
||||
</content-query-component>
|
||||
`
|
||||
})
|
||||
class MyApp {
|
||||
// NON-NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyApp,
|
||||
selectors: [['my-app']],
|
||||
factory: function MyApp_Factory(t) { return new (t || MyApp)(); },
|
||||
consts: 2,
|
||||
vars: 0,
|
||||
template: function MyApp_Template(rf: $RenderFlags$, ctx: $MyApp$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'content-query-component');
|
||||
contentQueryComp = getDirectiveOnNode(0);
|
||||
$r3$.ɵelement(1, 'div', $e2_attrs$);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NON-NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE
|
||||
(MyApp.ngComponentDef as ComponentDef<any>).directiveDefs =
|
||||
[ContentQueryComponent.ngComponentDef, SomeDirective.ngDirectiveDef];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
expect(toHtml(renderComponent(MyApp)))
|
||||
.toEqual(
|
||||
`<content-query-component><div><div somedir=""></div></div></content-query-component>`);
|
||||
expect(contentQueryComp !.someDir).toEqual(someDir !);
|
||||
expect((contentQueryComp !.someDirList as QueryList<SomeDirective>).toArray()).toEqual([
|
||||
someDir !
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
|
@ -1,81 +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 {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
|
||||
import * as $r3$ from '../../../src/core_render3_private_export';
|
||||
import {getHostElement} from '../../../src/render3/index';
|
||||
import {renderComponent, toHtml} from '../render_util';
|
||||
|
||||
/**
|
||||
* NORMATIVE => /NORMATIVE: Designates what the compiler is expected to generate.
|
||||
*
|
||||
* All local variable names are considered non-normative (informative). They should be
|
||||
* wrapped in $ on each end to simplify testing on the compiler side.
|
||||
*/
|
||||
|
||||
describe('compiler sanitization', () => {
|
||||
type $RenderFlags$ = $r3$.ɵRenderFlags;
|
||||
|
||||
it('should translate DOM structure', () => {
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: `<div [innerHTML]="innerHTML" [hidden]="hidden"></div>` +
|
||||
`<img [style.background-image]="style" [src]="src">` +
|
||||
`<script [attr.src]=src></script>`
|
||||
})
|
||||
class MyComponent {
|
||||
innerHTML: string = '<frame></frame>';
|
||||
hidden: boolean = true;
|
||||
style: string = `url("http://evil")`;
|
||||
url: string = 'javascript:evil()';
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-component']],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 2,
|
||||
vars: 4,
|
||||
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'div');
|
||||
$r3$.ɵelementStyling(['background-image']);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵelement(1, 'img');
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, 'innerHTML', $r3$.ɵbind(ctx.innerHTML), $r3$.ɵsanitizeHtml);
|
||||
$r3$.ɵelementProperty(0, 'hidden', $r3$.ɵbind(ctx.hidden));
|
||||
$r3$.ɵelementStyleProp(0, 0, ctx.style);
|
||||
$r3$.ɵelementStylingApply(0);
|
||||
$r3$.ɵelementProperty(1, 'src', $r3$.ɵbind(ctx.url), $r3$.ɵsanitizeUrl);
|
||||
$r3$.ɵelementAttribute(1, 'srcset', $r3$.ɵbind(ctx.url), $r3$.ɵsanitizeUrl);
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
const myComponent = renderComponent(MyComponent);
|
||||
const div = getHostElement(myComponent).querySelector('div') !;
|
||||
// because sanitizer removed it is working.
|
||||
expect(div.innerHTML).toEqual('');
|
||||
expect(div.hidden).toEqual(true);
|
||||
|
||||
const img = getHostElement(myComponent).querySelector('img') !;
|
||||
// because sanitizer removed it is working.
|
||||
expect(img.getAttribute('src')).toEqual('unsafe:javascript:evil()');
|
||||
// because sanitizer removed it is working.
|
||||
expect(img.style.getPropertyValue('background-image')).toEqual('');
|
||||
// because sanitizer removed it is working.
|
||||
expect(img.getAttribute('srcset')).toEqual('unsafe:javascript:evil()');
|
||||
});
|
||||
|
||||
});
|
|
@ -1,225 +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 {Component, Directive, Input, SimpleChanges, TemplateRef, ViewContainerRef, inject} from '../../../src/core';
|
||||
import * as $r3$ from '../../../src/core_render3_private_export';
|
||||
import {ComponentDef} from '../../../src/render3/interfaces/definition';
|
||||
import {renderComponent, toHtml} from '../render_util';
|
||||
|
||||
|
||||
|
||||
/// See: `normative.md`
|
||||
describe('template variables', () => {
|
||||
type $any$ = any;
|
||||
type $number$ = number;
|
||||
type $RenderFlags$ = $r3$.ɵRenderFlags;
|
||||
|
||||
interface ForOfContext {
|
||||
$implicit: any;
|
||||
index: number;
|
||||
even: boolean;
|
||||
odd: boolean;
|
||||
}
|
||||
|
||||
@Directive({selector: '[forOf]'})
|
||||
class ForOfDirective {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
private previous !: any[];
|
||||
|
||||
constructor(private view: ViewContainerRef, private template: TemplateRef<any>) {}
|
||||
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() forOf !: any[];
|
||||
|
||||
ngOnChanges(simpleChanges: SimpleChanges) {
|
||||
if ('forOf' in simpleChanges) {
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck(): void {
|
||||
const previous = this.previous;
|
||||
const current = this.forOf;
|
||||
if (!previous || previous.length != current.length ||
|
||||
previous.some((value: any, index: number) => current[index] !== previous[index])) {
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
|
||||
private update() {
|
||||
// TODO(chuckj): Not implemented yet
|
||||
// this.view.clear();
|
||||
if (this.forOf) {
|
||||
const current = this.forOf;
|
||||
for (let i = 0; i < current.length; i++) {
|
||||
const context = {$implicit: current[i], index: i, even: i % 2 == 0, odd: i % 2 == 1};
|
||||
// TODO(chuckj): Not implemented yet
|
||||
// this.view.createEmbeddedView(this.template, context);
|
||||
}
|
||||
this.previous = [...this.forOf];
|
||||
}
|
||||
}
|
||||
|
||||
// NORMATIVE
|
||||
static ngDirectiveDef = $r3$.ɵdefineDirective({
|
||||
type: ForOfDirective,
|
||||
selectors: [['', 'forOf', '']],
|
||||
factory: function ForOfDirective_Factory(t) {
|
||||
return new (t || ForOfDirective)(
|
||||
$r3$.ɵdirectiveInject(ViewContainerRef as any),
|
||||
$r3$.ɵdirectiveInject(TemplateRef as any));
|
||||
},
|
||||
// TODO(chuckj): Enable when ngForOf enabling lands.
|
||||
// features: [NgOnChangesFeature],
|
||||
inputs: {forOf: 'forOf'}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
it('should support a let variable and reference', () => {
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
interface Item {
|
||||
name: string;
|
||||
}
|
||||
|
||||
function MyComponent_ForOfDirective_Template_1(rf: $RenderFlags$, ctx1: $any$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'li');
|
||||
$r3$.ɵtext(1);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
const $l0_item$ = ctx1.$implicit;
|
||||
$r3$.ɵtextBinding(1, $r3$.ɵinterpolation1('', $l0_item$.name, ''));
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: `<ul><li *for="let item of items">{{item.name}}</li></ul>`
|
||||
})
|
||||
class MyComponent {
|
||||
items = [{name: 'one'}, {name: 'two'}];
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-component']],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 2,
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'ul');
|
||||
$r3$.ɵtemplate(1, MyComponent_ForOfDirective_Template_1, 2, 1, '', ['forOf', '']);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(1, 'forOf', $r3$.ɵbind(ctx.items));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
// NON-NORMATIVE
|
||||
(MyComponent.ngComponentDef as ComponentDef<any>).directiveDefs =
|
||||
[ForOfDirective.ngDirectiveDef];
|
||||
// /NON-NORMATIVE
|
||||
|
||||
// TODO(chuckj): update when the changes to enable ngForOf lands.
|
||||
expect(toHtml(renderComponent(MyComponent))).toEqual('<ul></ul>');
|
||||
});
|
||||
|
||||
it('should support accessing parent template variables', () => {
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
interface Info {
|
||||
description: string;
|
||||
}
|
||||
interface Item {
|
||||
name: string;
|
||||
infos: Info[];
|
||||
}
|
||||
|
||||
function MyComponent_ForOfDirective_Template_1(rf: $RenderFlags$, ctx1: $any$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'li');
|
||||
$r3$.ɵelementStart(1, 'div');
|
||||
$r3$.ɵtext(2);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵelementStart(3, 'ul');
|
||||
$r3$.ɵtemplate(
|
||||
4, MyComponent_ForOfDirective_ForOfDirective_Template_3, 2, 1, '', ['forOf', '']);
|
||||
$r3$.ɵelementEnd();
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
const $l0_item$ = ctx1.$implicit;
|
||||
$r3$.ɵelementProperty(4, 'forOf', $r3$.ɵbind($l0_item$.infos));
|
||||
$r3$.ɵtextBinding(2, $r3$.ɵinterpolation1('', $l0_item$.name, ''));
|
||||
}
|
||||
}
|
||||
|
||||
function MyComponent_ForOfDirective_ForOfDirective_Template_3(rf: $number$, ctx2: $any$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'li');
|
||||
$r3$.ɵtext(1);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
const $l0_info$ = ctx2.$implicit;
|
||||
const $l0_item$ = $r3$.ɵnextContext();
|
||||
$r3$.ɵtextBinding(
|
||||
1, $r3$.ɵinterpolation2(' ', $l0_item$.name, ': ', $l0_info$.description, ' '));
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: `
|
||||
<ul>
|
||||
<li *for="let item of items">
|
||||
<div>{{item.name}}</div>
|
||||
<ul>
|
||||
<li *for="let info of item.infos">
|
||||
{{item.name}}: {{info.description}}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>`
|
||||
})
|
||||
class MyComponent {
|
||||
items: Item[] = [
|
||||
{name: 'one', infos: [{description: '11'}, {description: '12'}]},
|
||||
{name: 'two', infos: [{description: '21'}, {description: '22'}]}
|
||||
];
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [['my-component']],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 2,
|
||||
vars: 1,
|
||||
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵelementStart(0, 'ul');
|
||||
$r3$.ɵtemplate(1, MyComponent_ForOfDirective_Template_1, 5, 2, '', ['forOf', '']);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(1, 'forOf', $r3$.ɵbind(ctx.items));
|
||||
}
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
});
|
||||
});
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component, ElementRef, InjectFlags, Injectable, InjectionToken, InjectorType, Provider, ViewEncapsulation, createInjector, defineInjectable, defineInjector, inject} from '../../src/core';
|
||||
import {Component as _Component, ElementRef, InjectFlags, Injectable as _Injectable, InjectionToken, InjectorType, Provider, ViewEncapsulation, createInjector, defineInjectable, defineInjector, inject} from '../../src/core';
|
||||
import {forwardRef} from '../../src/di/forward_ref';
|
||||
import {getRenderedText} from '../../src/render3/component';
|
||||
|
||||
|
@ -18,6 +18,15 @@ import {NgIf} from './common_with_def';
|
|||
import {getRendererFactory2} from './imported_renderer2';
|
||||
import {ComponentFixture, containerEl, createComponent, renderComponent, renderToHtml, requestAnimationFrame, toHtml} from './render_util';
|
||||
|
||||
const Component: typeof _Component = function(...args: any[]): any {
|
||||
// In test we use @Component for documentation only so it's safe to mock out the implementation.
|
||||
return () => undefined;
|
||||
} as any;
|
||||
const Injectable: typeof _Injectable = function(...args: any[]): any {
|
||||
// In test we use @Injectable for documentation only so it's safe to mock out the implementation.
|
||||
return () => undefined;
|
||||
} as any;
|
||||
|
||||
describe('component', () => {
|
||||
class CounterComponent {
|
||||
count = 0;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import {NgForOfContext} from '@angular/common';
|
||||
import {Component} from '../../src/core';
|
||||
import {Component as _Component} from '../../src/core';
|
||||
import {defineComponent} from '../../src/render3/definition';
|
||||
import {I18nExpInstruction, I18nInstruction, i18nApply, i18nExpMapping, i18nInterpolation1, i18nInterpolation2, i18nInterpolation3, i18nInterpolation4, i18nInterpolation5, i18nInterpolation6, i18nInterpolation7, i18nInterpolation8, i18nInterpolationV, i18nMapping} from '../../src/render3/i18n';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, nextContext, projection, projectionDef, template, text, textBinding} from '../../src/render3/instructions';
|
||||
|
@ -15,6 +15,11 @@ import {RenderFlags} from '../../src/render3/interfaces/definition';
|
|||
import {NgForOf} from './common_with_def';
|
||||
import {ComponentFixture, TemplateFixture} from './render_util';
|
||||
|
||||
const Component: typeof _Component = function(...args: any[]): any {
|
||||
// In test we use @Component for documentation only so it's safe to mock out the implementation.
|
||||
return () => undefined;
|
||||
} as any;
|
||||
|
||||
describe('Runtime i18n', () => {
|
||||
it('should support html elements', () => {
|
||||
// Html tags are replaced by placeholders.
|
||||
|
|
|
@ -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, ɵdefineComponent as defineComponent, ɵdirectiveInject as directiveInject} from '@angular/core';
|
||||
import {Directive as _Directive, InjectionToken, OnChanges, OnDestroy, Pipe as _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';
|
||||
|
@ -17,6 +17,14 @@ import {pipe, pipeBind1, pipeBind3, pipeBind4, pipeBindV} from '../../src/render
|
|||
import {RenderLog, getRendererFactory2, patchLoggingRenderer2} from './imported_renderer2';
|
||||
import {ComponentFixture, createComponent, getDirectiveOnNode, renderToHtml} from './render_util';
|
||||
|
||||
const Directive: typeof _Directive = function(...args: any[]): any {
|
||||
// In test we use @Directive for documentation only so it's safe to mock out the implementation.
|
||||
return () => undefined;
|
||||
} as any;
|
||||
const Pipe: typeof _Pipe = function(...args: any[]): any {
|
||||
// In test we use @Pipe for documentation only so it's safe to mock out the implementation.
|
||||
return () => undefined;
|
||||
} as any;
|
||||
|
||||
let log: string[] = [];
|
||||
let person: Person;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component, ComponentFactoryResolver, ElementRef, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, QueryList, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core';
|
||||
import {Component as _Component, ComponentFactoryResolver, ElementRef, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, QueryList, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core';
|
||||
import {ViewEncapsulation} from '../../src/metadata';
|
||||
import {AttributeMarker, NO_CHANGE, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, load, query, queryRefresh} from '../../src/render3/index';
|
||||
|
||||
|
@ -19,10 +19,17 @@ import {pipe, pipeBind1} from '../../src/render3/pipe';
|
|||
import {getViewData} from '../../src/render3/state';
|
||||
import {getNativeByIndex} from '../../src/render3/util';
|
||||
import {NgForOf} from '../../test/render3/common_with_def';
|
||||
import {fixmeIvy} from '@angular/private/testing';
|
||||
|
||||
import {getRendererFactory2} from './imported_renderer2';
|
||||
import {ComponentFixture, TemplateFixture, createComponent, getDirectiveOnNode} from './render_util';
|
||||
|
||||
const Component: typeof _Component = function(...args: any[]): any {
|
||||
// In test we use @Component for documentation only so it's safe to mock out the implementation.
|
||||
return () => undefined;
|
||||
} as any;
|
||||
|
||||
|
||||
describe('ViewContainerRef', () => {
|
||||
let directiveInstance: DirectiveWithVCRef|null;
|
||||
|
||||
|
@ -1465,7 +1472,7 @@ describe('ViewContainerRef', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('life cycle hooks', () => {
|
||||
fixmeIvy(`Hooks don't run`) && describe('life cycle hooks', () => {
|
||||
|
||||
// Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
|
||||
const log: string[] = [];
|
||||
|
|
|
@ -15,3 +15,17 @@ ng_module(
|
|||
"//packages/core",
|
||||
],
|
||||
)
|
||||
|
||||
## Controls if Ivy is enabled. (Temporary target until we permanently switch over to Ivy)
|
||||
##
|
||||
## This file generates `src/bazel_define_compile_value.ts` file which reexports
|
||||
## `--define=compile` value as `bazelDefineCompileValue` symbols so that runtime can detect
|
||||
## which mode it is running in.
|
||||
##
|
||||
## See: `//.bazelrc` where `--define=ivy=legacy` is defined as default.
|
||||
## See: `./src/bazel_define_compile_value.ts` for more details.
|
||||
genrule(
|
||||
name = "bazel_define_compile_value",
|
||||
outs = ["src/bazel_define_compile_value.ts"],
|
||||
cmd = "echo export const bazelDefineCompileValue = \"'$(compile)'\"\; > $@",
|
||||
)
|
||||
|
|
|
@ -7,3 +7,4 @@
|
|||
*/
|
||||
|
||||
export * from './src/render3';
|
||||
export * from './src/fixme';
|
||||
|
|
Loading…
Reference in New Issue