Compare commits

..

3 Commits

100 changed files with 924 additions and 2956 deletions

View File

@ -62,13 +62,6 @@ merge:
# list of checks that will determine if the merge label can be added # list of checks that will determine if the merge label can be added
checks: checks:
# require that the PR has reviews from all requested reviewers
#
# This enables us to request reviews from both eng and tech writers, or multiple eng folks, and prevents accidental merges.
# Rather than merging PRs with pending reviews, if all PullApprove requirements are satisfied and additional reviews are not needed pending reviewers should be removed via GitHub UI (this also leaves an audit trail behind these decisions).
requireReviews: true,
# whether the PR shouldn't have a conflict with the base branch # whether the PR shouldn't have a conflict with the base branch
noConflict: true noConflict: true
# list of labels that a PR needs to have, checked with a regexp (e.g. "PR target:" will work for the label "PR target: master") # list of labels that a PR needs to have, checked with a regexp (e.g. "PR target:" will work for the label "PR target: master")

View File

@ -1,14 +1,3 @@
<a name="7.0.0-beta.2"></a>
# [7.0.0-beta.2](https://github.com/angular/angular/compare/7.0.0-beta.1...7.0.0-beta.2) (2018-08-15)
### Bug Fixes
* **bazel:** correct type concatenated to devmode_js ([#25467](https://github.com/angular/angular/issues/25467)) ([fb2c524](https://github.com/angular/angular/commit/fb2c524))
* **service-worker:** `Cache-Control: no-cache` on assets breaks service worker ([#25408](https://github.com/angular/angular/issues/25408)) ([01ec5fd](https://github.com/angular/angular/commit/01ec5fd)), closes [#25442](https://github.com/angular/angular/issues/25442)
<a name="7.0.0-beta.1"></a> <a name="7.0.0-beta.1"></a>
# [7.0.0-beta.1](https://github.com/angular/angular/compare/7.0.0-beta.0...7.0.0-beta.1) (2018-08-08) # [7.0.0-beta.1](https://github.com/angular/angular/compare/7.0.0-beta.0...7.0.0-beta.1) (2018-08-08)

View File

@ -20,16 +20,16 @@ http_archive(
http_archive( http_archive(
name = "io_bazel_rules_webtesting", name = "io_bazel_rules_webtesting",
url = "https://github.com/bazelbuild/rules_webtesting/archive/0.2.1.zip", url = "https://github.com/bazelbuild/rules_webtesting/archive/7ffe970bbf380891754487f66c3d680c087d67f2.zip",
strip_prefix = "rules_webtesting-0.2.1", strip_prefix = "rules_webtesting-7ffe970bbf380891754487f66c3d680c087d67f2",
sha256 = "7d490aadff9b5262e5251fa69427ab2ffd1548422467cb9f9e1d110e2c36f0fa", sha256 = "4fb0dca8c9a90547891b7ef486592775a523330fc4555c88cd8f09270055c2ce",
) )
http_archive( http_archive(
name = "build_bazel_rules_typescript", name = "build_bazel_rules_typescript",
url = "https://github.com/bazelbuild/rules_typescript/archive/0.16.0.zip", url = "https://github.com/bazelbuild/rules_typescript/archive/1d9a4b0087f307e31af91e2b221a6447288994c6.zip",
strip_prefix = "rules_typescript-0.16.0", strip_prefix = "rules_typescript-1d9a4b0087f307e31af91e2b221a6447288994c6",
sha256 = "e65c5639a42e2f6d3f9d2bda62487d6b42734830dda45be1620c3e2b1115070c", sha256 = "e17ac3f33d5d3cd2a0c385c4fd28b814d0ad46c6c67ccaef97160be99d7a24eb",
) )
http_archive( http_archive(

View File

@ -8,7 +8,7 @@ Everything in this folder is part of the documentation project. This includes
## Developer tasks ## Developer tasks
We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks. We use `yarn` to manage the dependencies and to run build tasks.
You should run all these tasks from the `angular/aio` folder. You should run all these tasks from the `angular/aio` folder.
Here are the most important tasks you might need to use: Here are the most important tasks you might need to use:

View File

@ -16,7 +16,6 @@ describe('Form Validation Tests', function () {
tests('Template-Driven Form'); tests('Template-Driven Form');
bobTests(); bobTests();
asyncValidationTests();
crossValidationTests(); crossValidationTests();
}); });
@ -27,7 +26,6 @@ describe('Form Validation Tests', function () {
tests('Reactive Form'); tests('Reactive Form');
bobTests(); bobTests();
asyncValidationTests();
crossValidationTests(); crossValidationTests();
}); });
}); });
@ -47,7 +45,6 @@ let page: {
errorMessages: ElementArrayFinder, errorMessages: ElementArrayFinder,
heroFormButtons: ElementArrayFinder, heroFormButtons: ElementArrayFinder,
heroSubmitted: ElementFinder, heroSubmitted: ElementFinder,
alterEgoErrors: ElementFinder,
crossValidationErrorMessage: ElementFinder, crossValidationErrorMessage: ElementFinder,
}; };
@ -66,7 +63,6 @@ function getPage(sectionTag: string) {
errorMessages: section.all(by.css('div.alert')), errorMessages: section.all(by.css('div.alert')),
heroFormButtons: buttons, heroFormButtons: buttons,
heroSubmitted: section.element(by.css('.submitted-message')), heroSubmitted: section.element(by.css('.submitted-message')),
alterEgoErrors: section.element(by.css('.alter-ego-errors')),
crossValidationErrorMessage: section.element(by.css('.cross-validation-error-message')), crossValidationErrorMessage: section.element(by.css('.cross-validation-error-message')),
}; };
} }
@ -160,16 +156,6 @@ function expectFormIsInvalid() {
expect(page.form.getAttribute('class')).toMatch('ng-invalid'); expect(page.form.getAttribute('class')).toMatch('ng-invalid');
} }
function triggerAlterEgoValidation() {
// alterEgo has updateOn set to 'blur', click outside of the input to trigger the blur event
element(by.css('app-root')).click()
}
function waitForAlterEgoValidation() {
// alterEgo async validation will be performed in 400ms
browser.sleep(400);
}
function bobTests() { function bobTests() {
const emsg = 'Name cannot be Bob.'; const emsg = 'Name cannot be Bob.';
@ -191,32 +177,6 @@ function bobTests() {
}); });
} }
function asyncValidationTests() {
const emsg = 'Alter ego is already taken.';
it(`should produce "${emsg}" error after setting alterEgo to Eric`, function () {
page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('Eric');
triggerAlterEgoValidation();
waitForAlterEgoValidation();
expectFormIsInvalid();
expect(page.alterEgoErrors.getText()).toBe(emsg);
});
it('should be ok again with different values', function () {
page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('John');
triggerAlterEgoValidation();
waitForAlterEgoValidation();
expectFormIsValid();
expect(page.alterEgoErrors.isPresent()).toBe(false);
});
}
function crossValidationTests() { function crossValidationTests() {
const emsg = 'Name cannot match alter ego.'; const emsg = 'Name cannot match alter ego.';
@ -227,9 +187,6 @@ function crossValidationTests() {
page.alterEgoInput.clear(); page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('Batman'); page.alterEgoInput.sendKeys('Batman');
triggerAlterEgoValidation();
waitForAlterEgoValidation();
expectFormIsInvalid(); expectFormIsInvalid();
expect(page.crossValidationErrorMessage.getText()).toBe(emsg); expect(page.crossValidationErrorMessage.getText()).toBe(emsg);
}); });
@ -241,9 +198,6 @@ function crossValidationTests() {
page.alterEgoInput.clear(); page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('Superman'); page.alterEgoInput.sendKeys('Superman');
triggerAlterEgoValidation();
waitForAlterEgoValidation();
expectFormIsValid(); expectFormIsValid();
expect(page.crossValidationErrorMessage.isPresent()).toBe(false); expect(page.crossValidationErrorMessage.isPresent()).toBe(false);
}); });

View File

@ -8,7 +8,6 @@ import { HeroFormTemplateComponent } from './template/hero-form-template.compone
import { HeroFormReactiveComponent } from './reactive/hero-form-reactive.component'; import { HeroFormReactiveComponent } from './reactive/hero-form-reactive.component';
import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive'; import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive';
import { IdentityRevealedValidatorDirective } from './shared/identity-revealed.directive'; import { IdentityRevealedValidatorDirective } from './shared/identity-revealed.directive';
import { UniqueAlterEgoValidatorDirective } from './shared/alter-ego.directive';
@NgModule({ @NgModule({
imports: [ imports: [
@ -21,8 +20,7 @@ import { UniqueAlterEgoValidatorDirective } from './shared/alter-ego.directive';
HeroFormTemplateComponent, HeroFormTemplateComponent,
HeroFormReactiveComponent, HeroFormReactiveComponent,
ForbiddenValidatorDirective, ForbiddenValidatorDirective,
IdentityRevealedValidatorDirective, IdentityRevealedValidatorDirective
UniqueAlterEgoValidatorDirective
], ],
bootstrap: [ AppComponent ] bootstrap: [ AppComponent ]
}) })

View File

@ -1,48 +0,0 @@
/* tslint:disable: member-ordering forin */
// #docplaster
// #docregion
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
import { UniqueAlterEgoValidator } from '../shared/alter-ego.directive';
@Component({
selector: 'app-hero-form-reactive',
templateUrl: './hero-form-reactive.component.html',
styleUrls: ['./hero-form-reactive.component.css'],
})
export class HeroFormReactiveComponent implements OnInit {
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
hero = { name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0] };
heroForm: FormGroup;
ngOnInit(): void {
// #docregion async-validation
this.heroForm = new FormGroup({
'name': new FormControl(this.hero.name, [
Validators.required,
Validators.minLength(4),
forbiddenNameValidator(/bob/i)
]),
'alterEgo': new FormControl(this.hero.alterEgo, {
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
updateOn: 'blur'
}),
'power': new FormControl(this.hero.power, Validators.required)
});
// #enddocregion async-validation
}
get name() { return this.heroForm.get('name'); }
get power() { return this.heroForm.get('power'); }
get alterEgo() { return this.heroForm.get('alterEgo'); }
// #docregion async-validation
constructor(private alterEgoValidator: UniqueAlterEgoValidator) {}
// #enddocregion async-validation
}

View File

@ -35,13 +35,6 @@
<label for="alterEgo">Alter Ego</label> <label for="alterEgo">Alter Ego</label>
<input id="alterEgo" class="form-control" <input id="alterEgo" class="form-control"
formControlName="alterEgo" > formControlName="alterEgo" >
<div *ngIf="alterEgo.pending">Validating...</div>
<div *ngIf="alterEgo.invalid" class="alert alert-danger alter-ego-errors">
<div *ngIf="alterEgo.errors?.uniqueAlterEgo">
Alter ego is already taken.
</div>
</div>
</div> </div>
<!-- #docregion cross-validation-error-message --> <!-- #docregion cross-validation-error-message -->

View File

@ -4,7 +4,6 @@ import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms'; import { FormControl, FormGroup, Validators } from '@angular/forms';
import { forbiddenNameValidator } from '../shared/forbidden-name.directive'; import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
import { identityRevealedValidator } from '../shared/identity-revealed.directive'; import { identityRevealedValidator } from '../shared/identity-revealed.directive';
import { UniqueAlterEgoValidator } from '../shared/alter-ego.directive';
@Component({ @Component({
selector: 'app-hero-form-reactive', selector: 'app-hero-form-reactive',
@ -26,10 +25,7 @@ export class HeroFormReactiveComponent implements OnInit {
Validators.minLength(4), Validators.minLength(4),
forbiddenNameValidator(/bob/i) forbiddenNameValidator(/bob/i)
]), ]),
'alterEgo': new FormControl(this.hero.alterEgo, { 'alterEgo': new FormControl(this.hero.alterEgo),
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
updateOn: 'blur'
}),
'power': new FormControl(this.hero.power, Validators.required) 'power': new FormControl(this.hero.power, Validators.required)
}, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level }, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level
} }
@ -37,8 +33,4 @@ export class HeroFormReactiveComponent implements OnInit {
get name() { return this.heroForm.get('name'); } get name() { return this.heroForm.get('name'); }
get power() { return this.heroForm.get('power'); } get power() { return this.heroForm.get('power'); }
get alterEgo() { return this.heroForm.get('alterEgo'); }
constructor(private alterEgoValidator: UniqueAlterEgoValidator) { }
} }

View File

@ -1,46 +0,0 @@
import { Directive, forwardRef, Injectable } from '@angular/core';
import {
AsyncValidator,
AbstractControl,
NG_ASYNC_VALIDATORS,
ValidationErrors
} from '@angular/forms';
import { catchError, map } from 'rxjs/operators';
import { HeroesService } from './heroes.service';
import { Observable } from 'rxjs';
// #docregion async-validator
@Injectable({ providedIn: 'root' })
export class UniqueAlterEgoValidator implements AsyncValidator {
constructor(private heroesService: HeroesService) {}
validate(
ctrl: AbstractControl
): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
return this.heroesService.isAlterEgoTaken(ctrl.value).pipe(
map(isTaken => (isTaken ? { uniqueAlterEgo: true } : null)),
catchError(() => null)
);
}
}
// #enddocregion async-validator
// #docregion async-validator-directive
@Directive({
selector: '[appUniqueAlterEgo]',
providers: [
{
provide: NG_ASYNC_VALIDATORS,
useExisting: forwardRef(() => UniqueAlterEgoValidator),
multi: true
}
]
})
export class UniqueAlterEgoValidatorDirective {
constructor(private validator: UniqueAlterEgoValidator) {}
validate(control: AbstractControl) {
this.validator.validate(control);
}
}
// #enddocregion async-validator-directive

View File

@ -1,14 +0,0 @@
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
const ALTER_EGOS = ['Eric'];
@Injectable({ providedIn: 'root' })
export class HeroesService {
isAlterEgoTaken(alterEgo: string): Observable<boolean> {
const isTaken = ALTER_EGOS.includes(alterEgo);
return of(isTaken).pipe(delay(400));
}
}

View File

@ -35,20 +35,8 @@
<div class="form-group"> <div class="form-group">
<label for="alterEgo">Alter Ego</label> <label for="alterEgo">Alter Ego</label>
<!-- #docregion async-validation --> <input id="alterEgo" class="form-control"
<input id="alterEgo" class="form-control" name="alterEgo" name="alterEgo" [(ngModel)]="hero.alterEgo" >
#alterEgo="ngModel"
[(ngModel)]="hero.alterEgo"
[ngModelOptions]="{ updateOn: 'blur' }"
appUniqueAlterEgo>
<!-- #enddocregion async-validation -->
<div *ngIf="alterEgo.pending">Validating...</div>
<div *ngIf="alterEgo.invalid" class="alert alert-danger alter-ego-errors">
<div *ngIf="alterEgo.errors?.uniqueAlterEgo">
Alter ego is already taken.
</div>
</div>
</div> </div>
<!-- #docregion cross-validation-error-message --> <!-- #docregion cross-validation-error-message -->

View File

@ -51,7 +51,7 @@ The `@Component()` decorator identifies the class immediately below it as a comp
A template combines HTML with Angular markup that can modify HTML elements before they are displayed. A template combines HTML with Angular markup that can modify HTML elements before they are displayed.
Template *directives* provide program logic, and *binding markup* connects your application data and the DOM. Template *directives* provide program logic, and *binding markup* connects your application data and the DOM.
There are two types of data binding: There are tow types of data binding:
* *Event binding* lets your app respond to user input in the target environment by updating your application data. * *Event binding* lets your app respond to user input in the target environment by updating your application data.
* *Property binding* lets you interpolate values that are computed from your application data into the HTML. * *Property binding* lets you interpolate values that are computed from your application data into the HTML.

View File

@ -284,71 +284,4 @@ This completes the cross validation example. We managed to:
- validate the form based on the values of two sibling controls, - validate the form based on the values of two sibling controls,
- show a descriptive error message after the user interacted with the form and the validation failed. - show a descriptive error message after the user interacted with the form and the validation failed.
## Async Validation
This section shows how to create asynchronous validators. It assumes some basic knowledge of creating [custom validators](guide/form-validation#custom-validators).
### The Basics
Just like synchronous validators have the `ValidatorFn` and `Validator` interfaces, asynchronous validators have their own counterparts: `AsyncValidatorFn` and `AsyncValidator`.
They are very similar with the only difference being:
* They must return a Promise or an Observable,
* The observable returned must be finite, meaning it must complete at some point. To convert an infinite observable into a finite one, pipe the observable through a filtering operator such as `first`, `last`, `take`, or `takeUntil`.
It is important to note that the asynchronous validation happens after the synchronous validation, and is performed only if the synchronous validation is successful. This check allows forms to avoid potentially expensive async validation processes such as an HTTP request if more basic validation methods fail.
After asynchronous validation begins, the form control enters a `pending` state. You can inspect the control's `pending` property and use it to give visual feedback about the ongoing validation.
A common UI pattern is to show a spinner while the async validation is being performed. The following example presents how to achieve this with template-driven forms:
```html
<input [(ngModel)}="name" #model="ngModel" appSomeAsyncValidator>
<app-spinner *ngIf="model.pending"></app-spinner>
```
### Implementing Custom Async Validator
In the following section, validation is performed asynchronously to ensure that our heroes pick an alter ego that is not already taken. New heroes are constantly enlisting and old heroes are leaving the service. That means that we do not have the list of available alter egos ahead of time.
To validate the potential alter ego, we need to consult a central database of all currently enlisted heroes. The process is asynchronous, so we need a special validator for that.
Let's start by creating the validator class.
<code-example path="form-validation/src/app/shared/alter-ego.directive.ts" region="async-validator" linenums="false"></code-example>
As you can see, the `UniqueAlterEgoValidator` class implements the `AsyncValidator` interface. In the constructor, we inject the `HeroesService` that has the following interface:
```typescript
interface HeroesService {
isAlterEgoTaken: (alterEgo: string) => Observable<boolean>;
}
```
In a real world application, the `HeroesService` is responsible for making an HTTP request to the hero database to check if the alter ego is available. From the validator's point of view, the actual implementation of the service is not important, so we can just code against the `HeroesService` interface.
As the validation begins, the `UniqueAlterEgoValidator` delegates to the `HeroesService` `isAlterEgoTaken()` method with the current control value. At this point the control is marked as `pending` and remains in this state until the observable chain returned from the `validate()` method completes.
The `isAlterEgoTaken()` method dispatches an HTTP request that checks if the alter ego is available, and returns `Observable<boolean>` as the result. We pipe the response through the `map` operator and transform it into a validation result. As always, we return `null` if the form is valid, and `ValidationErrors` if it is not. We make sure to handle any potential errors with the `catchError` operator.
Here we decided that `isAlterEgoTaken()` error is treated as a successful validation, because failure to make a validation request does not necessarily mean that the alter ego is invalid. You could handle the error differently and return the `ValidationError` object instead.
After some time passes, the observable chain completes and the async validation is done. The `pending` flag is set to `false`, and the form validity is updated.
### Note on performance
By default, all validators are run after every form value change. With synchronous validators, this will not likely have a noticeable impact on application performance. However, it's common for async validators to perform some kind of HTTP request to validate the control. Dispatching an HTTP request after every keystroke could put a strain on the backend API, and should be avoided if possible.
We can delay updating the form validity by changing the `updateOn` property from `change` (default) to `submit` or `blur`.
With template-driven forms:
```html
<input [(ngModel)]="name" [ngModelOptions]="{updateOn: 'blur'}">
```
With reactive forms:
```typescript
new FormControl('', {updateOn: 'blur'});
```
**You can run the <live-example></live-example> to see the complete reactive and template-driven example code.** **You can run the <live-example></live-example> to see the complete reactive and template-driven example code.**

View File

@ -1263,7 +1263,7 @@ But it is occasionally necessary.
For example, you can't call `async` or `fakeAsync` when testing For example, you can't call `async` or `fakeAsync` when testing
code that involves the `intervalTimer()` or the RxJS `delay()` operator. code that involves the `intervalTimer()` or the RxJS `delay()` operator.
Here are two more versions of the previous test, written with `done()`. Here are two mover versions of the previous test, written with `done()`.
The first one subscribes to the `Observable` exposed to the template by the component's `quote` property. The first one subscribes to the `Observable` exposed to the template by the component's `quote` property.
<code-example <code-example

View File

@ -661,12 +661,6 @@
"Workshops & Onsite Training": { "Workshops & Onsite Training": {
"order": 2, "order": 2,
"resources": { "resources": {
"-acceleb": {
"desc": "Customized, Instructor-Led Angular Training",
"rev": true,
"title": "Accelebrate",
"url": "https://www.accelebrate.com/angular-training"
},
"-KLIBoFWStce29UCwkvY": { "-KLIBoFWStce29UCwkvY": {
"desc": "Private Angular Training and Mentoring", "desc": "Private Angular Training and Mentoring",
"rev": true, "rev": true,

View File

@ -8,27 +8,11 @@ module.exports = function computeApiBreadCrumbs(API_DOC_TYPES_TO_RENDER) {
if (API_DOC_TYPES_TO_RENDER.indexOf(doc.docType) !== -1) { if (API_DOC_TYPES_TO_RENDER.indexOf(doc.docType) !== -1) {
doc.breadCrumbs = []; doc.breadCrumbs = [];
doc.breadCrumbs.push({ text: 'API', path: '/api' }); doc.breadCrumbs.push({ text: 'API', path: '/api' });
if (isSecondaryEntryPoint(doc)) { if (doc.moduleDoc) doc.breadCrumbs.push({ text: '@angular/' + doc.moduleDoc.id, path: doc.moduleDoc.path });
doc.breadCrumbs.push(createPackageBreadcrumb(doc));
}
if (doc.moduleDoc) {
if (isSecondaryEntryPoint(doc.moduleDoc)) {
doc.breadCrumbs.push(createPackageBreadcrumb(doc.moduleDoc));
}
doc.breadCrumbs.push({ text: '@angular/' + doc.moduleDoc.id, path: doc.moduleDoc.path });
}
doc.breadCrumbs.push({ text: doc.name, path: doc.path }); doc.breadCrumbs.push({ text: doc.name, path: doc.path });
} }
}); });
} }
}; };
}; };
function isSecondaryEntryPoint(doc) {
return doc.docType === 'package' && !doc.isPrimaryPackage;
}
function createPackageBreadcrumb(doc) {
return { text: doc.packageInfo.primary.name, path: doc.packageInfo.primary.path };
}

View File

@ -17,17 +17,11 @@ describe('angular-api-package: computeApiBreadCrumbs processor', () => {
const API_DOC_TYPES_TO_RENDER = ['class', 'interface', 'package']; const API_DOC_TYPES_TO_RENDER = ['class', 'interface', 'package'];
const processor = processorFactory(API_DOC_TYPES_TO_RENDER); const processor = processorFactory(API_DOC_TYPES_TO_RENDER);
const httpPackage = { docType: 'package', name: '@angular/http', id: 'http', path: 'http', isPrimaryPackage: true };
const httpTestingPackage = { docType: 'package', name: '@angular/http/testing', id: 'http/testing', path: 'http/testing', packageInfo: { primary: httpPackage } };
const testRequestClass = { docType: 'class', name: 'TestRequest', path: 'http/testing/test-request', moduleDoc: httpTestingPackage };
const docs = [ const docs = [
{ docType: 'class', name: 'ClassA', path: 'module-1/class-a', moduleDoc: { id: 'moduleOne', path: 'module-1' } }, { docType: 'class', name: 'ClassA', path: 'module-1/class-a', moduleDoc: { id: 'moduleOne', path: 'module-1' } },
{ docType: 'interface', name: 'InterfaceB', path: 'module-2/interface-b', moduleDoc: { id: 'moduleTwo', path: 'module-2' } }, { docType: 'interface', name: 'InterfaceB', path: 'module-2/interface-b', moduleDoc: { id: 'moduleTwo', path: 'module-2' } },
{ docType: 'guide', name: 'Guide One', path: 'guide/guide-1' }, { docType: 'guide', name: 'Guide One', path: 'guide/guide-1' },
httpPackage, { docType: 'package', name: 'testing', id: 'http/testing', path: 'http/testing' },
httpTestingPackage,
testRequestClass
]; ];
processor.$process(docs); processor.$process(docs);
@ -44,18 +38,7 @@ describe('angular-api-package: computeApiBreadCrumbs processor', () => {
expect(docs[2].breadCrumbs).toBeUndefined(); expect(docs[2].breadCrumbs).toBeUndefined();
expect(docs[3].breadCrumbs).toEqual([ expect(docs[3].breadCrumbs).toEqual([
{ text: 'API', path: '/api' }, { text: 'API', path: '/api' },
{ text: '@angular/http', path: 'http' }, { text: 'testing', path: 'http/testing' },
]);
expect(docs[4].breadCrumbs).toEqual([
{ text: 'API', path: '/api' },
{ text: '@angular/http', path: 'http' },
{ text: '@angular/http/testing', path: 'http/testing' },
]);
expect(docs[5].breadCrumbs).toEqual([
{ text: 'API', path: '/api' },
{ text: '@angular/http', path: 'http' },
{ text: '@angular/http/testing', path: 'http/testing' },
{ text: 'TestRequest', path: 'http/testing/test-request' },
]); ]);
}); });
}); });

View File

@ -1,6 +1,6 @@
{% import "lib/githubLinks.html" as github -%} {% import "lib/githubLinks.html" as github -%}
{% set comma = joiner(',') %} {% set comma = joiner(',') %}
{% set breadcrumbDelimiter = joiner('&gt;') %} {% set slash = joiner('/') %}
<article> <article>
{$ github.githubLinks(doc, versionInfo) $} {$ github.githubLinks(doc, versionInfo) $}
<div class="breadcrumb"> <div class="breadcrumb">
@ -14,11 +14,14 @@
] ]
} }
</script> </script>
{% for crumb in doc.breadCrumbs %}{% if not loop.last %} {$ breadcrumbDelimiter() $} {% if crumb.path %}<a href="{$ crumb.path $}">{$ crumb.text $}</a>{% else %}{$ crumb.text $}{% endif %}{% endif %}{% endfor %} {% for crumb in doc.breadCrumbs %}{% if not loop.last %} {$ slash() $} {% if crumb.path %}<a href="{$ crumb.path $}">{$ crumb.text $}</a>{% else %}{$ crumb.text $}{% endif %}{% endif %}{% endfor %}
</div> </div>
<header class="api-header"> <header class="api-header">
<h1>{$ doc.name $}</h1> <h1>{$ doc.name $}</h1>
<label class="api-type-label {$ doc.docType $}">{$ doc.docType $}</label> <label class="api-type-label {$ doc.docType $}">{$ doc.docType $}</label>
{% if doc.deprecated !== undefined %}<label class="api-status-label deprecated">deprecated</label>{% endif %}
{% if doc.experimental !== undefined %}<label class="api-status-label experimental">experimental</label>{% endif %}
{% if doc.stable !== undefined %}<label class="api-status-label stable">stable</label>{% endif %}
{% if doc.pipeOptions.pure === 'false' %}<label class="api-status-label impure-pipe">impure</label>{% endif %} {% if doc.pipeOptions.pure === 'false' %}<label class="api-status-label impure-pipe">impure</label>{% endif %}
</header> </header>
<aio-toc class="embedded"></aio-toc> <aio-toc class="embedded"></aio-toc>

View File

@ -1,6 +1,6 @@
{% extends 'base.template.html' -%} {% extends 'base.template.html' -%}
{% macro listItems(items, title, overridePath) %} {% macro listItems(items, title) %}
{% if items.length %} {% if items.length %}
<section class="export-list"> <section class="export-list">
<h3>{$ title $}</h3> <h3>{$ title $}</h3>
@ -8,7 +8,7 @@
{% for item in items %} {% for item in items %}
<tr> <tr>
<td><code class="code-anchor"> <td><code class="code-anchor">
<a href="{$ overridePath or item.path $}">{$ item.name $}</a></code></td> <a href="{$ item.path $}">{$ item.name $}</a></code></td>
<td>{% if item.shortDescription %}{$ item.shortDescription | marked $}{% endif %}</td> <td>{% if item.shortDescription %}{$ item.shortDescription | marked $}{% endif %}</td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -24,13 +24,12 @@
{% include "includes/see-also.html" %} {% include "includes/see-also.html" %}
{% if doc.isPrimaryPackage %}
<h2>Entry points</h2> <h2>Entry points</h2>
{$ listItems([doc.packageInfo.primary], 'Primary', '#primary-entry-point-exports') $} {$ listItems([doc.packageInfo.primary], 'Primary') $}
{$ listItems(doc.packageInfo.secondary, 'Secondary') $} {$ listItems(doc.packageInfo.secondary, 'Secondary') $}
{% endif %}
<h2>{% if doc.isPrimaryPackage %}Primary entry{% else %}Entry{% endif %} point exports</h2>
<h2>Exports</h2>
{$ listItems(doc.classes, 'Classes') $} {$ listItems(doc.classes, 'Classes') $}
{$ listItems(doc.decorators, 'Decorators') $} {$ listItems(doc.decorators, 'Decorators') $}
{$ listItems(doc.functions, 'Functions') $} {$ listItems(doc.functions, 'Functions') $}

View File

@ -8,8 +8,8 @@ source ${currentDir}/scripts/ci/_travis-fold.sh
# TODO(i): wrap into subshell, so that we don't pollute CWD, but not yet to minimize diff collision with Jason # TODO(i): wrap into subshell, so that we don't pollute CWD, but not yet to minimize diff collision with Jason
cd ${currentDir} cd ${currentDir}
PACKAGES=(compiler PACKAGES=(core
core compiler
common common
animations animations
platform-browser platform-browser
@ -27,8 +27,7 @@ PACKAGES=(compiler
service-worker service-worker
elements) elements)
TSC_PACKAGES=(compiler TSC_PACKAGES=(compiler-cli
compiler-cli
language-service language-service
benchpress) benchpress)
@ -240,13 +239,7 @@ compilePackage() {
# For TSC_PACKAGES items # For TSC_PACKAGES items
if containsElement "${3}" "${TSC_PACKAGES[@]}"; then if containsElement "${3}" "${TSC_PACKAGES[@]}"; then
echo "====== [${3}]: COMPILING: ${TSC} -p ${1}/tsconfig-build.json" echo "====== [${3}]: COMPILING: ${TSC} -p ${1}/tsconfig-build.json"
local package_name=$(basename "${2}")
$TSC -p ${1}/tsconfig-build.json $TSC -p ${1}/tsconfig-build.json
if [[ "${3}" = "compiler" ]]; then
if [[ "${package_name}" = "testing" ]]; then
echo "$(cat ${LICENSE_BANNER}) ${N} export * from './${package_name}/${package_name}'" > ${2}/../${package_name}.d.ts
fi
fi
else else
echo "====== [${3}]: COMPILING: ${NGC} -p ${1}/tsconfig-build.json" echo "====== [${3}]: COMPILING: ${NGC} -p ${1}/tsconfig-build.json"
local package_name=$(basename "${2}") local package_name=$(basename "${2}")

View File

@ -68,26 +68,7 @@ new as of May 2017 and not very stable yet.
You can use [ibazel] to get a "watch mode" that continuously You can use [ibazel] to get a "watch mode" that continuously
keeps the outputs up-to-date as you save sources. keeps the outputs up-to-date as you save sources.
### Various Flags Used For Tests
If you're experiencing problems with seemingly unrelated tests failing, it may be because you're not using the proper flags with your Bazel test runs in Angular.
See also: [`//tools/bazel.rc`](https://github.com/angular/angular/blob/master/tools/bazel.rc) where `--define=ivy=false` is defined as default.
- `--config=debug`: build and launch in debug mode (see [debugging](#debugging) instructions below)
- `--define=compile=<option>` Controls if ivy or legacy mode is enabled. This is done by generating the [`src/ivy_switch.ts`](https://github.com/angular/angular/blob/master/packages/core/src/ivy_switch.ts) file from [`ivy_switch_legacy.ts`](https://github.com/angular/angular/blob/master/packages/core/src/ivy_switch_legacy.ts) (default), [`ivy_switch_jit.ts`](https://github.com/angular/angular/blob/master/packages/core/src/ivy_switch_jit.ts), or [`ivy_switch_local.ts`](https://github.com/angular/angular/blob/master/packages/core/src/ivy_switch_local.ts).
- `legacy`: (default behavior) compile against View Engine, e.g. `--define=compile=legacy`
- `jit`: Compile in ivy JIT mode, e.g. `--define=compile=jit`
- `local`: Compile in ivy AOT move, e.g. `--define=compile=local`
- `--test_tag_filters=<tag>`: filter tests down to tags defined in the `tag` config
of your rules in any given `BUILD.bazel`.
- `ivy-jit`: This flag should be set for tests that should be excuted with ivy JIT, e.g. `--test_tag_filters=ivy-jit`. For this, you may have to include `--define=compile=jit`.
- `ivy-local`: Only run tests that have to do with ivy AOT. For this, you may have to include `--define=compile=local`, e.g. `--test_tag_filters=ivy-local`..
- `ivy-only`: Only run ivy related tests, e.g. `--test_tag_filters=ivy-only`.
### Debugging a Node Test ### Debugging a Node Test
<a id="debugging"></a>
- Open chrome at: [chrome://inspect](chrome://inspect) - Open chrome at: [chrome://inspect](chrome://inspect)
- Click on `Open dedicated DevTools for Node` to launch a debugger. - Click on `Open dedicated DevTools for Node` to launch a debugger.
@ -101,7 +82,7 @@ First time setup:
- Go to Debug > Add configuration (in the menu bar) to open `launch.json` - Go to Debug > Add configuration (in the menu bar) to open `launch.json`
- Add the following to the `configurations` array: - Add the following to the `configurations` array:
```json ```
{ {
"name": "Attach (inspect)", "name": "Attach (inspect)",
"type": "node", "type": "node",
@ -126,7 +107,6 @@ First time setup:
}, },
``` ```
**Setting breakpoints directly in your code files may not work in VSCode**. This is because the files you're actually debugging are built files that exist in a `./private/...` folder.
The easiest way to debug a test for now is to add a `debugger` statement in the code The easiest way to debug a test for now is to add a `debugger` statement in the code
and launch the bazel corresponding test (`bazel test <target> --config=debug`). and launch the bazel corresponding test (`bazel test <target> --config=debug`).

View File

@ -14,17 +14,11 @@ Ctrl + Shift + j.
By default the debug tools are disabled. You can enable debug tools as follows: By default the debug tools are disabled. You can enable debug tools as follows:
```typescript ```typescript
import {ApplicationRef} from '@angular/core';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {enableDebugTools} from '@angular/platform-browser'; import {enableDebugTools} from '@angular/platform-browser';
bootstrap(Application).then((appRef) => {
platformBrowserDynamic().bootstrapModule(AppModule) enableDebugTools(appRef);
.then(moduleRef => { });
const applicationRef = moduleRef.injector.get(ApplicationRef);
const appComponent = applicationRef.components[0];
enableDebugTools(appComponent);
})
``` ```
### Using debug tools ### Using debug tools

View File

@ -24,8 +24,9 @@ filegroup(
ANGULAR_TESTING = [ ANGULAR_TESTING = [
"node_modules/@angular/*/bundles/*-testing.umd.js", "node_modules/@angular/*/bundles/*-testing.umd.js",
# We use AOT, so the dynamic platform-browser should be visible only in tests # We use AOT, so the compiler and the dynamic platform-browser should be
# NOTE that we still need to include the compiler because the core depends on it # visible only in tests
"node_modules/@angular/compiler/bundles/*.umd.js",
"node_modules/@angular/platform-browser-dynamic/bundles/*.umd.js", "node_modules/@angular/platform-browser-dynamic/bundles/*.umd.js",
] ]

View File

@ -20,16 +20,16 @@ http_archive(
http_archive( http_archive(
name = "io_bazel_rules_webtesting", name = "io_bazel_rules_webtesting",
url = "https://github.com/bazelbuild/rules_webtesting/archive/0.2.1.zip", url = "https://github.com/bazelbuild/rules_webtesting/archive/8fd9ce0fd9254bde251da0bc373d6cd08e811434.zip",
strip_prefix = "rules_webtesting-0.2.1", strip_prefix = "rules_webtesting-8fd9ce0fd9254bde251da0bc373d6cd08e811434",
sha256 = "7d490aadff9b5262e5251fa69427ab2ffd1548422467cb9f9e1d110e2c36f0fa", sha256 = "4baee95fcfadfbaf868707af8accfd1cb98c5d13f808908e0152468bfb47f0f7",
) )
http_archive( http_archive(
name = "build_bazel_rules_typescript", name = "build_bazel_rules_typescript",
url = "https://github.com/bazelbuild/rules_typescript/archive/0.16.0.zip", url = "https://github.com/bazelbuild/rules_typescript/archive/1d9a4b0087f307e31af91e2b221a6447288994c6.zip",
strip_prefix = "rules_typescript-0.16.0", strip_prefix = "rules_typescript-1d9a4b0087f307e31af91e2b221a6447288994c6",
sha256 = "e65c5639a42e2f6d3f9d2bda62487d6b42734830dda45be1620c3e2b1115070c", sha256 = "e17ac3f33d5d3cd2a0c385c4fd28b814d0ad46c6c67ccaef97160be99d7a24eb",
) )
http_archive( http_archive(

View File

@ -19,9 +19,6 @@ node_modules/zone.js/dist/zone_externs.js
--js node_modules/rxjs/operators/package.json --js node_modules/rxjs/operators/package.json
--js node_modules/rxjs/_esm2015/operators/index.js --js node_modules/rxjs/_esm2015/operators/index.js
--js node_modules/@angular/compiler/package.json
--js node_modules/@angular/compiler/fesm2015/compiler.js
--js node_modules/@angular/core/package.json --js node_modules/@angular/core/package.json
--js node_modules/@angular/core/fesm2015/core.js --js node_modules/@angular/core/fesm2015/core.js
--js node_modules/@angular/core/src/testability/testability.externs.js --js node_modules/@angular/core/src/testability/testability.externs.js

View File

@ -17,9 +17,6 @@ node_modules/zone.js/dist/zone_externs.js
--module_resolution=node --module_resolution=node
--package_json_entry_names es2015 --package_json_entry_names es2015
--js node_modules/@angular/compiler/package.json
--js node_modules/@angular/compiler/fesm2015/compiler.js
--js node_modules/@angular/core/package.json --js node_modules/@angular/core/package.json
--js node_modules/@angular/core/fesm2015/core.js --js node_modules/@angular/core/fesm2015/core.js
--js node_modules/@angular/core/src/testability/testability.externs.js --js node_modules/@angular/core/src/testability/testability.externs.js

View File

@ -9,7 +9,6 @@
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli", "@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
"@angular/core": "file:../../dist/packages-dist/core", "@angular/core": "file:../../dist/packages-dist/core",
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser", "@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
"@angular/platform-server": "file:../../dist/packages-dist/platform-server", "@angular/platform-server": "file:../../dist/packages-dist/platform-server",
"google-closure-compiler": "git+https://github.com/alexeagle/closure-compiler.git#packagejson.dist", "google-closure-compiler": "git+https://github.com/alexeagle/closure-compiler.git#packagejson.dist",
"rxjs": "file:../../node_modules/rxjs", "rxjs": "file:../../node_modules/rxjs",

View File

@ -3,48 +3,40 @@
"@angular/animations@file:../../dist/packages-dist/animations": "@angular/animations@file:../../dist/packages-dist/animations":
version "7.0.0-beta.1-d2d510089c" version "6.0.0-beta.7-8203e0365a"
dependencies: dependencies:
tslib "^1.9.0" tslib "^1.9.0"
"@angular/common@file:../../dist/packages-dist/common": "@angular/common@file:../../dist/packages-dist/common":
version "7.0.0-beta.1-d2d510089c" version "6.0.0-beta.7-8203e0365a"
dependencies: dependencies:
tslib "^1.9.0" tslib "^1.9.0"
"@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli": "@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli":
version "7.0.0-beta.1-d2d510089c" version "6.0.0-beta.7-8203e0365a"
dependencies: dependencies:
chokidar "^1.4.2" chokidar "^1.4.2"
convert-source-map "^1.5.1"
magic-string "^0.25.0"
minimist "^1.2.0" minimist "^1.2.0"
reflect-metadata "^0.1.2" reflect-metadata "^0.1.2"
source-map "^0.6.1" tsickle "^0.27.2"
tsickle "^0.32.1"
"@angular/compiler@file:../../dist/packages-dist/compiler": "@angular/compiler@file:../../dist/packages-dist/compiler":
version "7.0.0-beta.1-d2d510089c" version "6.0.0-beta.7-8203e0365a"
dependencies: dependencies:
tslib "^1.9.0" tslib "^1.9.0"
"@angular/core@file:../../dist/packages-dist/core": "@angular/core@file:../../dist/packages-dist/core":
version "7.0.0-beta.1-d2d510089c" version "6.0.0-beta.7-8203e0365a"
dependencies:
tslib "^1.9.0"
"@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic":
version "7.0.0-beta.1-d2d510089c"
dependencies: dependencies:
tslib "^1.9.0" tslib "^1.9.0"
"@angular/platform-browser@file:../../dist/packages-dist/platform-browser": "@angular/platform-browser@file:../../dist/packages-dist/platform-browser":
version "7.0.0-beta.1-d2d510089c" version "6.0.0-beta.7-8203e0365a"
dependencies: dependencies:
tslib "^1.9.0" tslib "^1.9.0"
"@angular/platform-server@file:../../dist/packages-dist/platform-server": "@angular/platform-server@file:../../dist/packages-dist/platform-server":
version "7.0.0-beta.1-d2d510089c" version "6.0.0-beta.7-8203e0365a"
dependencies: dependencies:
domino "^2.0.1" domino "^2.0.1"
tslib "^1.9.0" tslib "^1.9.0"
@ -518,10 +510,6 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
convert-source-map@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
cookie@0.3.1: cookie@0.3.1:
version "0.3.1" version "0.3.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
@ -624,10 +612,6 @@ dev-ip@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0" resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0"
diff@^3.2.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
domino@^2.0.1: domino@^2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/domino/-/domino-2.0.1.tgz#9e1d63215d0fe8dcb8202bff07effa1a216db504" resolved "https://registry.yarnpkg.com/domino/-/domino-2.0.1.tgz#9e1d63215d0fe8dcb8202bff07effa1a216db504"
@ -1219,12 +1203,6 @@ jasmine-core@~2.8.0:
version "2.8.0" version "2.8.0"
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e"
jasmine-diff@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/jasmine-diff/-/jasmine-diff-0.1.3.tgz#93ccc2dcc41028c5ddd4606558074839f2deeaa8"
dependencies:
diff "^3.2.0"
jasmine@^2.5.3: jasmine@^2.5.3:
version "2.8.0" version "2.8.0"
resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-2.8.0.tgz#6b089c0a11576b1f16df11b80146d91d4e8b8a3e" resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-2.8.0.tgz#6b089c0a11576b1f16df11b80146d91d4e8b8a3e"
@ -1341,12 +1319,6 @@ lodash@^4.11.1, lodash@^4.5.1:
version "4.17.4" version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
magic-string@^0.25.0:
version "0.25.0"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.0.tgz#1f3696f9931ff0a1ed4c132250529e19cad6759b"
dependencies:
sourcemap-codec "^1.4.1"
micromatch@2.3.11, micromatch@^2.1.5: micromatch@2.3.11, micromatch@^2.1.5:
version "2.3.11" version "2.3.11"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
@ -1863,7 +1835,7 @@ rx@4.1.0:
resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
"rxjs@file:../../node_modules/rxjs": "rxjs@file:../../node_modules/rxjs":
version "6.0.0" version "6.0.0-alpha.4"
dependencies: dependencies:
tslib "^1.9.0" tslib "^1.9.0"
@ -2044,14 +2016,10 @@ source-map@^0.5.1, source-map@^0.5.6:
version "0.5.7" version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
source-map@^0.6.0, source-map@^0.6.1: source-map@^0.6.0:
version "0.6.1" version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
sourcemap-codec@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz#c8fd92d91889e902a07aee392bdd2c5863958ba2"
spawn-command@^0.0.2-1: spawn-command@^0.0.2-1:
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e" resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e"
@ -2212,11 +2180,10 @@ tree-kill@^1.1.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36"
tsickle@^0.32.1: tsickle@^0.27.2:
version "0.32.1" version "0.27.2"
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.32.1.tgz#f16e94ba80b32fc9ebe320dc94fbc2ca7f3521a5" resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.27.2.tgz#f33d46d046f73dd5c155a37922e422816e878736"
dependencies: dependencies:
jasmine-diff "^0.1.3"
minimist "^1.2.0" minimist "^1.2.0"
mkdirp "^0.5.1" mkdirp "^0.5.1"
source-map "^0.6.0" source-map "^0.6.0"
@ -2237,7 +2204,7 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
"typescript@file:../../node_modules/typescript": "typescript@file:../../node_modules/typescript":
version "2.9.2" version "2.7.2"
ua-parser-js@0.7.12: ua-parser-js@0.7.12:
version "0.7.12" version "0.7.12"
@ -2457,4 +2424,4 @@ yeast@0.1.2:
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
"zone.js@file:../../node_modules/zone.js": "zone.js@file:../../node_modules/zone.js":
version "0.8.26" version "0.8.20"

View File

@ -1,6 +1,6 @@
{ {
"name": "angular-srcs", "name": "angular-srcs",
"version": "7.0.0-beta.2", "version": "7.0.0-beta.1",
"private": true, "private": true,
"branchPattern": "2.0.*", "branchPattern": "2.0.*",
"description": "Angular - a web framework for modern web apps", "description": "Angular - a web framework for modern web apps",

View File

@ -9,42 +9,31 @@ import {AnimationMetadata, AnimationOptions} from './animation_metadata';
import {AnimationPlayer} from './players/animation_player'; import {AnimationPlayer} from './players/animation_player';
/** /**
* An injectable service that produces an animation sequence programmatically within an * AnimationBuilder is an injectable service that is available when the {@link
* Angular component or directive. * BrowserAnimationsModule BrowserAnimationsModule} or {@link NoopAnimationsModule
* Provided by the `BrowserAnimationsModule` or `NoopAnimationsModule`. * NoopAnimationsModule} modules are used within an application.
* *
* @usageNotes * The purpose of this service is to produce an animation sequence programmatically within an
* angular component or directive.
* *
* To use this service, add it to your component or directive as a dependency. * Programmatic animations are first built and then a player is created when the build animation is
* The service is instantiated along with your component. * attached to an element.
*
* Apps do not typically need to create their own animation players, but if you
* do need to, follow these steps:
*
* 1. Use the `build()` method to create a programmatic animation using the
* `animate()` function. The method returns an `AnimationFactory` instance.
*
* 2. Use the factory object to create an `AnimationPlayer` and attach it to a DOM element.
*
* 3. Use the player object to control the animation programmatically.
*
* For example:
* *
* ```ts * ```ts
* // import the service from BrowserAnimationsModule * // remember to include the BrowserAnimationsModule module for this to work...
* import {AnimationBuilder} from '@angular/animations'; * import {AnimationBuilder} from '@angular/animations';
* // require the service as a dependency *
* class MyCmp { * class MyCmp {
* constructor(private _builder: AnimationBuilder) {} * constructor(private _builder: AnimationBuilder) {}
* *
* makeAnimation(element: any) { * makeAnimation(element: any) {
* // first define a reusable animation * // first build the animation
* const myAnimation = this._builder.build([ * const myAnimation = this._builder.build([
* style({ width: 0 }), * style({ width: 0 }),
* animate(1000, style({ width: '100px' })) * animate(1000, style({ width: '100px' }))
* ]); * ]);
* *
* // use the returned factory object to create a player * // then create a player from it
* const player = myAnimation.create(element); * const player = myAnimation.create(element);
* *
* player.play(); * player.play();
@ -52,29 +41,22 @@ import {AnimationPlayer} from './players/animation_player';
* } * }
* ``` * ```
* *
* When an animation is built an instance of {@link AnimationFactory AnimationFactory} will be
* returned. Using that an {@link AnimationPlayer AnimationPlayer} can be created which can then be
* used to start the animation.
*
* @experimental Animation support is experimental.
*/ */
export abstract class AnimationBuilder { export abstract class AnimationBuilder {
/**
* Builds a factory for producing a defined animation.
* @param animation A reusable animation definition.
* @returns A factory object that can create a player for the defined animation.
* @see `animate()`
*/
abstract build(animation: AnimationMetadata|AnimationMetadata[]): AnimationFactory; abstract build(animation: AnimationMetadata|AnimationMetadata[]): AnimationFactory;
} }
/** /**
* A factory object returned from the `AnimationBuilder`.`build()` method. * An instance of `AnimationFactory` is returned from {@link AnimationBuilder#build
* AnimationBuilder.build}.
* *
* @experimental Animation support is experimental.
*/ */
export abstract class AnimationFactory { export abstract class AnimationFactory {
/**
* Creates an `AnimationPlayer` instance for the reusable animation defined by
* the `AnimationBuilder`.`build()` method that created this factory.
* Attaches the new player a DOM element.
* @param element The DOM element to which to attach the animation.
* @param options A set of options that can include a time delay and
* additional developer-defined parameters.
*/
abstract create(element: any, options?: AnimationOptions): AnimationPlayer; abstract create(element: any, options?: AnimationOptions): AnimationPlayer;
} }

View File

@ -38,33 +38,11 @@
* @experimental Animation support is experimental. * @experimental Animation support is experimental.
*/ */
export interface AnimationEvent { export interface AnimationEvent {
/**
* The name of the state from which the animation is triggered.
*/
fromState: string; fromState: string;
/**
* The name of the state in which the animation completes.
*/
toState: string; toState: string;
/**
* The time it takes the animation to complete, in milliseconds.
*/
totalTime: number; totalTime: number;
/**
* The animation phase in which the callback was invoked, one of
* "start" or "done".
*/
phaseName: string; phaseName: string;
/**
* The element to which the animation is attached.
*/
element: any; element: any;
/**
* Internal.
*/
triggerName: string; triggerName: string;
/**
* Internal.
*/
disabled: boolean; disabled: boolean;
} }

View File

@ -9,14 +9,6 @@
import {scheduleMicroTask} from '../util'; import {scheduleMicroTask} from '../util';
import {AnimationPlayer} from './animation_player'; import {AnimationPlayer} from './animation_player';
/**
* A programmatic controller for a group of reusable animations.
* Used internally to control animations.
*
* @see `AnimationPlayer`
* @see `{@link animations/group group()}`
*
*/
export class AnimationGroupPlayer implements AnimationPlayer { export class AnimationGroupPlayer implements AnimationPlayer {
private _onDoneFns: Function[] = []; private _onDoneFns: Function[] = [];
private _onStartFns: Function[] = []; private _onStartFns: Function[] = [];

View File

@ -8,110 +8,37 @@
import {scheduleMicroTask} from '../util'; import {scheduleMicroTask} from '../util';
/** /**
* Provides programmatic control of a reusable animation sequence, * AnimationPlayer controls an animation sequence that was produced from a programmatic animation.
* built using the `build()` method of `AnimationBuilder`. The `build()` method * (see {@link AnimationBuilder AnimationBuilder} for more information on how to create programmatic
* returns a factory, whose `create()` method instantiates and initializes this interface. * animations.)
*
* @see `AnimationBuilder`
* @see `AnimationFactory`
* @see `animate()`
* *
* @experimental Animation support is experimental.
*/ */
export interface AnimationPlayer { export interface AnimationPlayer {
/**
* Provides a callback to invoke when the animation finishes.
* @param fn The callback function.
* @see `finish()`
*/
onDone(fn: () => void): void; onDone(fn: () => void): void;
/**
* Provides a callback to invoke when the animation starts.
* @param fn The callback function.
* @see `run()`
*/
onStart(fn: () => void): void; onStart(fn: () => void): void;
/**
* Provides a callback to invoke after the animation is destroyed.
* @param fn The callback function.
* @see `destroy()`
* @see `beforeDestroy()`
*/
onDestroy(fn: () => void): void; onDestroy(fn: () => void): void;
/**
* Initializes the animation.
*/
init(): void; init(): void;
/**
* Reports whether the animation has started.
* @returns True if the animation has started, false otherwise.
*/
hasStarted(): boolean; hasStarted(): boolean;
/**
* Runs the animation, invoking the `onStart()` callback.
*/
play(): void; play(): void;
/**
* Pauses the animation.
*/
pause(): void; pause(): void;
/**
* Restarts the paused animation.
*/
restart(): void; restart(): void;
/**
* Ends the animation, invoking the `onDone()` callback.
*/
finish(): void; finish(): void;
/**
* Destroys the animation, after invoking the `beforeDestroy()` callback.
* Calls the `onDestroy()` callback when destruction is completed.
*/
destroy(): void; destroy(): void;
/**
* Resets the animation to its initial state.
*/
reset(): void; reset(): void;
/**
* Sets the position of the animation.
* @param position A 0-based offset into the duration, in milliseconds.
*/
setPosition(position: any /** TODO #9100 */): void; setPosition(position: any /** TODO #9100 */): void;
/**
* Reports the current position of the animation.
* @returns A 0-based offset into the duration, in milliseconds.
*/
getPosition(): number; getPosition(): number;
/**
* The parent of this player, if any.
*/
parentPlayer: AnimationPlayer|null; parentPlayer: AnimationPlayer|null;
/**
* The total run time of the animation, in milliseconds.
*/
readonly totalTime: number; readonly totalTime: number;
/**
* Provides a callback to invoke before the animation is destroyed.
*/
beforeDestroy?: () => any; beforeDestroy?: () => any;
/** @internal /** @internal */
* Internal
*/
triggerCallback?: (phaseName: string) => void; triggerCallback?: (phaseName: string) => void;
/** @internal /** @internal */
* Internal
*/
disabled?: boolean; disabled?: boolean;
} }
/** /**
* An empty programmatic controller for reusable animations. * @experimental Animation support is experimental.
* Used internally when animations are disabled, to avoid
* checking for the null case when an animation player is expected.
*
* @see `animate()`
* @see `AnimationPlayer`
* @see `GroupPlayer`
*
*/ */
export class NoopAnimationPlayer implements AnimationPlayer { export class NoopAnimationPlayer implements AnimationPlayer {
private _onDoneFns: Function[] = []; private _onDoneFns: Function[] = [];

View File

@ -176,7 +176,7 @@ def _expected_outs(ctx):
else: else:
devmode_js = [".js"] devmode_js = [".js"]
if not _is_bazel(): if not _is_bazel():
devmode_js += [".ngfactory.js"] devmode_js += ".ngfactory.js"
summaries = [] summaries = []
metadata = [] metadata = []
elif include_ng_files and short_path.endswith(".css"): elif include_ng_files and short_path.endswith(".css"):

View File

@ -6,7 +6,6 @@
"Install toolchain dependencies" "Install toolchain dependencies"
load("@build_bazel_rules_nodejs//:defs.bzl", "yarn_install") load("@build_bazel_rules_nodejs//:defs.bzl", "yarn_install")
load("@build_bazel_rules_typescript//:defs.bzl", "check_rules_typescript_version")
def ng_setup_workspace(): def ng_setup_workspace():
"""This repository rule should be called from your WORKSPACE file. """This repository rule should be called from your WORKSPACE file.
@ -19,6 +18,3 @@ def ng_setup_workspace():
package_json = "@angular//packages/bazel/src/ng_package:package.json", package_json = "@angular//packages/bazel/src/ng_package:package.json",
yarn_lock = "@angular//packages/bazel/src/ng_package:yarn.lock", yarn_lock = "@angular//packages/bazel/src/ng_package:yarn.lock",
) )
# 0.16.0: minimal version required to work with ng_module
check_rules_typescript_version("0.16.0")

View File

@ -13,6 +13,10 @@ load(
_compile_ts = "compile_ts", _compile_ts = "compile_ts",
_ts_providers_dict_to_struct = "ts_providers_dict_to_struct", _ts_providers_dict_to_struct = "ts_providers_dict_to_struct",
) )
load(
"@build_bazel_rules_typescript//internal:common/json_marshal.bzl",
_json_marshal = "json_marshal",
)
tsc_wrapped_tsconfig = _tsc_wrapped_tsconfig tsc_wrapped_tsconfig = _tsc_wrapped_tsconfig
COMMON_ATTRIBUTES = _COMMON_ATTRIBUTES COMMON_ATTRIBUTES = _COMMON_ATTRIBUTES
@ -20,3 +24,4 @@ COMMON_OUTPUTS = _COMMON_OUTPUTS
compile_ts = _compile_ts compile_ts = _compile_ts
DEPS_ASPECTS = _DEPS_ASPECTS DEPS_ASPECTS = _DEPS_ASPECTS
ts_providers_dict_to_struct = _ts_providers_dict_to_struct ts_providers_dict_to_struct = _ts_providers_dict_to_struct
json_marshal = _json_marshal

View File

@ -229,7 +229,13 @@ describe('@angular/core ng_package', () => {
expect(shx.cat('testing.metadata.json')) expect(shx.cat('testing.metadata.json'))
.toContain(`"exports":[{"from":"./testing/testing"}],"flatModuleIndexRedirect":true`); .toContain(`"exports":[{"from":"./testing/testing"}],"flatModuleIndexRedirect":true`);
}); });
it('should have an \'actual\' metadata.json file', () => {
expect(shx.cat('testing/testing.metadata.json'))
.toContain(`"metadata":{"async":{"__symbolic":"function"},`);
}); });
});
describe('fesm2015', () => { describe('fesm2015', () => {
it('should have a fesm15 file in the /fesm2015 directory', it('should have a fesm15 file in the /fesm2015 directory',

View File

@ -77,7 +77,8 @@ export class Analyzer {
protected analyzeClass(file: ts.SourceFile, pool: ConstantPool, clazz: ParsedClass): AnalyzedClass protected analyzeClass(file: ts.SourceFile, pool: ConstantPool, clazz: ParsedClass): AnalyzedClass
|undefined { |undefined {
const matchingHandlers = this.handlers const matchingHandlers =
this.handlers
.map(handler => ({ .map(handler => ({
handler, handler,
match: handler.detect(clazz.declaration, clazz.decorators), match: handler.detect(clazz.declaration, clazz.decorators),

View File

@ -168,7 +168,7 @@ export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMe
return { return {
name: 'ngComponentDef', name: 'ngComponentDef',
initializer: res.expression, initializer: res.expression,
statements: res.statements, statements: [],
type: res.type, type: res.type,
}; };
} }

View File

@ -157,7 +157,7 @@ export class SelectorScopeRegistry {
// Process the declaration scope of the module, and lookup the selector of every declared type. // Process the declaration scope of the module, and lookup the selector of every declared type.
// The initial value of ngModuleImportedFrom is 'null' which signifies that the NgModule // The initial value of ngModuleImportedFrom is 'null' which signifies that the NgModule
// was not imported from a .d.ts source. // was not imported from a .d.ts source.
this.lookupScopesOrDie(module !, /* ngModuleImportedFrom */ null).compilation.forEach(ref => { this.lookupScopes(module !, /* ngModuleImportedFrom */ null).compilation.forEach(ref => {
const node = ts.getOriginalNode(ref.node) as ts.Declaration; const node = ts.getOriginalNode(ref.node) as ts.Declaration;
// Either the node represents a directive or a pipe. Look for both. // Either the node represents a directive or a pipe. Look for both.
@ -183,15 +183,6 @@ export class SelectorScopeRegistry {
return convertScopeToExpressions(scope, node); return convertScopeToExpressions(scope, node);
} }
private lookupScopesOrDie(node: ts.Declaration, ngModuleImportedFrom: string|null):
SelectorScopes {
const result = this.lookupScopes(node, ngModuleImportedFrom);
if (result === null) {
throw new Error(`Module not found: ${reflectIdentifierOfDeclaration(node)}`);
}
return result;
}
/** /**
* Lookup `SelectorScopes` for a given module. * Lookup `SelectorScopes` for a given module.
* *
@ -199,8 +190,7 @@ export class SelectorScopeRegistry {
* (`ngModuleImportedFrom`) then all of its declarations are exported at that same path, as well * (`ngModuleImportedFrom`) then all of its declarations are exported at that same path, as well
* as imports and exports from other modules that are relatively imported. * as imports and exports from other modules that are relatively imported.
*/ */
private lookupScopes(node: ts.Declaration, ngModuleImportedFrom: string|null): SelectorScopes private lookupScopes(node: ts.Declaration, ngModuleImportedFrom: string|null): SelectorScopes {
|null {
let data: ModuleData|null = null; let data: ModuleData|null = null;
// Either this module was analyzed directly, or has a precompiled ngModuleDef. // Either this module was analyzed directly, or has a precompiled ngModuleDef.
@ -216,7 +206,7 @@ export class SelectorScopeRegistry {
} }
if (data === null) { if (data === null) {
return null; throw new Error(`Module not registered: ${reflectNameOfDeclaration(node)}`);
} }
return { return {
@ -224,19 +214,18 @@ export class SelectorScopeRegistry {
...data.declarations, ...data.declarations,
// Expand imports to the exported scope of those imports. // Expand imports to the exported scope of those imports.
...flatten(data.imports.map( ...flatten(data.imports.map(
ref => this.lookupScopesOrDie(ref.node as ts.Declaration, absoluteModuleName(ref)) ref =>
.exported)), this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref)).exported)),
// And include the compilation scope of exported modules. // And include the compilation scope of exported modules.
...flatten( ...flatten(
data.exports data.exports.filter(ref => this._moduleToData.has(ref.node as ts.Declaration))
.map(ref => this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref))) .map(
.filter((scope: SelectorScopes | null): scope is SelectorScopes => scope !== null) ref => this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref))
.map(scope => scope.exported)) .exported))
], ],
exported: flatten(data.exports.map(ref => { exported: flatten(data.exports.map(ref => {
const scope = this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref)); if (this._moduleToData.has(ref.node as ts.Declaration)) {
if (scope !== null) { return this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref)).exported;
return scope.exported;
} else { } else {
return [ref]; return [ref];
} }

View File

@ -27,22 +27,22 @@ describe('SelectorScopeRegistry', () => {
{ {
name: 'node_modules/some_library/index.d.ts', name: 'node_modules/some_library/index.d.ts',
contents: ` contents: `
import {NgModuleDef} from '@angular/core'; import {NgComponentDef, NgModuleDef} from '@angular/core';
import * as i0 from './component'; import * as i0 from './component';
export declare class SomeModule { export declare class SomeModule {
static ngModuleDef: NgModuleDef<SomeModule, [typeof i0.SomeCmp], never, [typeof i0.SomeCmp]>; static ngModuleDef: NgModuleDef<SomeModule, [typeof i0.SomeCmp], never, [typeof i0.SomeCmp]>;
} }
export declare class SomeCmp {
static ngComponentDef: NgComponentDef<SomeCmp, 'some-cmp'>;
}
` `
}, },
{ {
name: 'node_modules/some_library/component.d.ts', name: 'node_modules/some_library/component.d.ts',
contents: ` contents: `
import {NgComponentDef} from '@angular/core'; export declare class SomeCmp {}
export declare class SomeCmp {
static ngComponentDef: NgComponentDef<SomeCmp, 'some-cmp'>;
}
` `
}, },
{ {
@ -76,64 +76,6 @@ describe('SelectorScopeRegistry', () => {
const scope = registry.lookupCompilationScope(ProgramCmp) !; const scope = registry.lookupCompilationScope(ProgramCmp) !;
expect(scope).toBeDefined(); expect(scope).toBeDefined();
expect(scope.directives).toBeDefined(); expect(scope.directives).toBeDefined();
expect(scope.directives.size).toBe(2); expect(scope.directives.size).toBe(1);
});
it('exports of third-party libs work', () => {
const {program} = makeProgram([
{
name: 'node_modules/@angular/core/index.d.ts',
contents: `
export interface NgComponentDef<A, B> {}
export interface NgModuleDef<A, B, C, D> {}
`
},
{
name: 'node_modules/some_library/index.d.ts',
contents: `
import {NgComponentDef, NgModuleDef} from '@angular/core';
export declare class SomeModule {
static ngModuleDef: NgModuleDef<SomeModule, [typeof SomeCmp], never, [typeof SomeCmp]>;
}
export declare class SomeCmp {
static ngComponentDef: NgComponentDef<SomeCmp, 'some-cmp'>;
}
`
},
{
name: 'entry.ts',
contents: `
export class ProgramCmp {}
export class ProgramModule {}
`
},
]);
const checker = program.getTypeChecker();
const host = new TypeScriptReflectionHost(checker);
const ProgramModule =
getDeclaration(program, 'entry.ts', 'ProgramModule', ts.isClassDeclaration);
const ProgramCmp = getDeclaration(program, 'entry.ts', 'ProgramCmp', ts.isClassDeclaration);
const SomeModule = getDeclaration(
program, 'node_modules/some_library/index.d.ts', 'SomeModule', ts.isClassDeclaration);
expect(ProgramModule).toBeDefined();
expect(SomeModule).toBeDefined();
const registry = new SelectorScopeRegistry(checker, host);
registry.registerModule(ProgramModule, {
declarations: [new ResolvedReference(ProgramCmp, ProgramCmp.name !)],
exports: [new AbsoluteReference(SomeModule, SomeModule.name !, 'some_library', 'SomeModule')],
imports: [],
});
registry.registerSelector(ProgramCmp, 'program-cmp');
debugger;
const scope = registry.lookupCompilationScope(ProgramCmp) !;
expect(scope).toBeDefined();
expect(scope.directives).toBeDefined();
expect(scope.directives.size).toBe(2);
}); });
}); });

View File

@ -16,7 +16,7 @@ import {NgtscProgram} from '../../src/ngtsc/program';
const IDENTIFIER = /[A-Za-z_$ɵ][A-Za-z0-9_$]*/; const IDENTIFIER = /[A-Za-z_$ɵ][A-Za-z0-9_$]*/;
const OPERATOR = const OPERATOR =
/!|\?|%|\*|\/|\^|&&?|\|\|?|\(|\)|\{|\}|\[|\]|:|;|<=?|>=?|={1,3}|!==?|=>|\+\+?|--?|@|,|\.|\.\.\./; /!|%|\*|\/|\^|&&?|\|\|?|\(|\)|\{|\}|\[|\]|:|;|<=?|>=?|={1,3}|!==?|=>|\+\+?|--?|@|,|\.|\.\.\./;
const STRING = /'[^']*'|"[^"]*"|`[\s\S]*?`/; const STRING = /'[^']*'|"[^"]*"|`[\s\S]*?`/;
const NUMBER = /\d+/; const NUMBER = /\d+/;

View File

@ -367,10 +367,10 @@ describe('compiler compliance', () => {
$r3$.ɵrS(10); $r3$.ɵrS(10);
} }
if (rf & 2) { if (rf & 2) {
$r3$.ɵp(0, "ternary", $r3$.ɵb((ctx.cond ? $r3$.ɵf1(6, _c0, ctx.a): _c1))); $r3$.ɵp(0, "ternary", $r3$.ɵb((ctx.cond ? $r3$.ɵf1(2, _c0, ctx.a): _c1)));
$r3$.ɵp(0, "pipe", $r3$.ɵb($r3$.ɵpb3(1, 4, ctx.value, 1, 2))); $r3$.ɵp(0, "pipe", $r3$.ɵb($r3$.ɵpb3(6, 1, ctx.value, 1, 2)));
$r3$.ɵp(0, "and", $r3$.ɵb((ctx.cond && $r3$.ɵf1(8, _c0, ctx.b)))); $r3$.ɵp(0, "and", $r3$.ɵb((ctx.cond && $r3$.ɵf1(4, _c0, ctx.b))));
$r3$.ɵp(0, "or", $r3$.ɵb((ctx.cond || $r3$.ɵf1(10, _c0, ctx.c)))); $r3$.ɵp(0, "or", $r3$.ɵb((ctx.cond || $r3$.ɵf1(6, _c0, ctx.c))));
} }
} }
`; `;

View File

@ -52,12 +52,5 @@ function getHookName(hook: LifecycleHooks): string {
return 'ngAfterViewInit'; return 'ngAfterViewInit';
case LifecycleHooks.AfterViewChecked: case LifecycleHooks.AfterViewChecked:
return 'ngAfterViewChecked'; return 'ngAfterViewChecked';
default:
// This default case is not needed by TypeScript compiler, as the switch is exhaustive.
// However Closure Compiler does not understand that and reports an error in typed mode.
// The `throw new Error` below works around the problem, and the unexpected: never variable
// makes sure tsc still checks this code is unreachable.
const unexpected: never = hook;
throw new Error(`unexpected ${unexpected}`);
} }
} }

View File

@ -115,13 +115,16 @@ export class CompileMetadataResolver {
return this.getGeneratedClass(dirType, cpl.hostViewClassName(dirType)); return this.getGeneratedClass(dirType, cpl.hostViewClassName(dirType));
} }
getHostComponentType(dirType: any): StaticSymbol|cpl.ProxyClass { getHostComponentType(dirType: any): StaticSymbol|Type {
const name = `${cpl.identifierName({reference: dirType})}_Host`; const name = `${cpl.identifierName({reference: dirType})}_Host`;
if (dirType instanceof StaticSymbol) { if (dirType instanceof StaticSymbol) {
return this._staticSymbolCache.get(dirType.filePath, name); return this._staticSymbolCache.get(dirType.filePath, name);
} } else {
const HostClass = <any>function HostClass() {};
HostClass.overriddenName = name;
return this._createProxyClass(dirType, name); return HostClass;
}
} }
private getRendererType(dirType: any): StaticSymbol|object { private getRendererType(dirType: any): StaticSymbol|object {

View File

@ -181,8 +181,7 @@ export function compileFactoryFunction(meta: R3FactoryMetadata):
} else { } else {
const baseFactory = o.variable(`ɵ${meta.name}_BaseFactory`); const baseFactory = o.variable(`ɵ${meta.name}_BaseFactory`);
const getInheritedFactory = o.importExpr(R3.getInheritedFactory); const getInheritedFactory = o.importExpr(R3.getInheritedFactory);
const baseFactoryStmt = baseFactory.set(getInheritedFactory.callFn([meta.type])) const baseFactoryStmt = baseFactory.set(getInheritedFactory.callFn([meta.type])).toDeclStmt();
.toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]);
statements.push(baseFactoryStmt); statements.push(baseFactoryStmt);
// There is no constructor, use the base class' factory to construct typeForCtor. // There is no constructor, use the base class' factory to construct typeForCtor.
@ -206,8 +205,8 @@ export function compileFactoryFunction(meta: R3FactoryMetadata):
if (meta.delegate.isEquivalent(meta.type)) { if (meta.delegate.isEquivalent(meta.type)) {
throw new Error(`Illegal state: compiling factory that delegates to itself`); throw new Error(`Illegal state: compiling factory that delegates to itself`);
} }
const delegateFactoryStmt = delegateFactory.set(getFactoryOf.callFn([meta.delegate])) const delegateFactoryStmt =
.toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]); delegateFactory.set(getFactoryOf.callFn([meta.delegate])).toDeclStmt();
statements.push(delegateFactoryStmt); statements.push(delegateFactoryStmt);
const r = makeConditionalFactory(delegateFactory.callFn([])); const r = makeConditionalFactory(delegateFactory.callFn([]));

View File

@ -940,8 +940,7 @@ function needsAdditionalRootNode(astNodes: TemplateAst[]): boolean {
function elementBindingDef(inputAst: BoundElementPropertyAst, dirAst: DirectiveAst): o.Expression { function elementBindingDef(inputAst: BoundElementPropertyAst, dirAst: DirectiveAst): o.Expression {
const inputType = inputAst.type; switch (inputAst.type) {
switch (inputType) {
case PropertyBindingType.Attribute: case PropertyBindingType.Attribute:
return o.literalArr([ return o.literalArr([
o.literal(BindingFlags.TypeElementAttribute), o.literal(inputAst.name), o.literal(BindingFlags.TypeElementAttribute), o.literal(inputAst.name),
@ -966,13 +965,6 @@ function elementBindingDef(inputAst: BoundElementPropertyAst, dirAst: DirectiveA
return o.literalArr([ return o.literalArr([
o.literal(BindingFlags.TypeElementStyle), o.literal(inputAst.name), o.literal(inputAst.unit) o.literal(BindingFlags.TypeElementStyle), o.literal(inputAst.name), o.literal(inputAst.unit)
]); ]);
default:
// This default case is not needed by TypeScript compiler, as the switch is exhaustive.
// However Closure Compiler does not understand that and reports an error in typed mode.
// The `throw new Error` below works around the problem, and the unexpected: never variable
// makes sure tsc still checks this code is unreachable.
const unexpected: never = inputType;
throw new Error(`unexpected ${unexpected}`);
} }
} }

View File

@ -16,8 +16,6 @@ export {
detectChanges as ɵdetectChanges, detectChanges as ɵdetectChanges,
renderComponent as ɵrenderComponent, renderComponent as ɵrenderComponent,
ComponentType as ɵComponentType, ComponentType as ɵComponentType,
ComponentFactory as ɵRender3ComponentFactory,
ComponentRef as ɵRender3ComponentRef,
DirectiveType as ɵDirectiveType, DirectiveType as ɵDirectiveType,
RenderFlags as ɵRenderFlags, RenderFlags as ɵRenderFlags,
directiveInject as ɵdirectiveInject, directiveInject as ɵdirectiveInject,
@ -32,7 +30,6 @@ export {
InheritDefinitionFeature as ɵInheritDefinitionFeature, InheritDefinitionFeature as ɵInheritDefinitionFeature,
NgOnChangesFeature as ɵNgOnChangesFeature, NgOnChangesFeature as ɵNgOnChangesFeature,
NgModuleType as ɵNgModuleType, NgModuleType as ɵNgModuleType,
NgModuleRef as ɵRender3NgModuleRef,
CssSelectorList as ɵCssSelectorList, CssSelectorList as ɵCssSelectorList,
markDirty as ɵmarkDirty, markDirty as ɵmarkDirty,
NgModuleFactory as ɵNgModuleFactory, NgModuleFactory as ɵNgModuleFactory,
@ -100,7 +97,6 @@ export {
Pp as ɵPp, Pp as ɵPp,
BaseDef as ɵBaseDef, BaseDef as ɵBaseDef,
ComponentDef as ɵComponentDef, ComponentDef as ɵComponentDef,
ComponentDefInternal as ɵComponentDefInternal,
DirectiveDef as ɵDirectiveDef, DirectiveDef as ɵDirectiveDef,
PipeDef as ɵPipeDef, PipeDef as ɵPipeDef,
whenRendered as ɵwhenRendered, whenRendered as ɵwhenRendered,
@ -118,38 +114,14 @@ export {
iM as ɵiM, iM as ɵiM,
I18nInstruction as ɵI18nInstruction, I18nInstruction as ɵI18nInstruction,
I18nExpInstruction as ɵI18nExpInstruction, I18nExpInstruction as ɵI18nExpInstruction,
WRAP_RENDERER_FACTORY2 as ɵWRAP_RENDERER_FACTORY2,
Render3DebugRendererFactory2 as ɵRender3DebugRendererFactory2,
} from './render3/index'; } from './render3/index';
export {NgModuleDef as ɵNgModuleDef} from './metadata/ng_module';
export {
compileNgModuleDefs as ɵcompileNgModuleDefs,
patchComponentDefWithScope as ɵpatchComponentDefWithScope,
} from './render3/jit/module';
export {
compileComponent as ɵcompileComponent,
compileDirective as ɵcompileDirective,
} from './render3/jit/directive';
export {
compilePipe as ɵcompilePipe,
} from './render3/jit/pipe';
export {
NgModuleDef as ɵNgModuleDef,
NgModuleDefInternal as ɵNgModuleDefInternal,
NgModuleTransitiveScopes as ɵNgModuleTransitiveScopes,
} from './metadata/ng_module';
export { export {
sanitizeHtml as ɵsanitizeHtml, sanitizeHtml as ɵsanitizeHtml,
sanitizeStyle as ɵsanitizeStyle, sanitizeStyle as ɵsanitizeStyle,
sanitizeUrl as ɵsanitizeUrl, sanitizeUrl as ɵsanitizeUrl,
sanitizeResourceUrl as ɵsanitizeResourceUrl, sanitizeResourceUrl as ɵsanitizeResourceUrl,
} from './sanitization/sanitization'; } from './sanitization/sanitization';
export { export {
bypassSanitizationTrustHtml as ɵbypassSanitizationTrustHtml, bypassSanitizationTrustHtml as ɵbypassSanitizationTrustHtml,
bypassSanitizationTrustStyle as ɵbypassSanitizationTrustStyle, bypassSanitizationTrustStyle as ɵbypassSanitizationTrustStyle,

View File

@ -9,8 +9,27 @@
/** /**
* This file is used to control if the default rendering pipeline should be `ViewEngine` or `Ivy`. * This file is used to control if the default rendering pipeline should be `ViewEngine` or `Ivy`.
* *
* For more information on how to run and debug tests with either Ivy or View Engine (legacy), * Reexport from:
* please see [BAZEL.md](./docs/BAZEL.md). * - `./ivy_switch_false` => Use `ViewEngine`.
* - `./ivy_switch_true` => Use `Ivy`.
*
* This file is here for your IDE as well as for `google3`. The `bazel` build system
* specifically excludes this file and instead generates a new file which is controlled by
* command line:
*
* - `bazel build packages/core` => Use `ViewEngine`
* - `bazel build packages/core --define=ivy=true` => Use `Ivy`
*
* See: `bazel build packages/core:ivy_switch` for more details.
*
* ## How to use this
*
* Use this mechanism to have the same symbol be aliased to different implementation.
* 1) Create two implementations of a symbol (most likely a `function` or a `class`).
* 2) Export the two implementation under same name in `./ivy_switch_false` and `./ivy_switch_false`
* respectively.
* 3) Import the symbol from `./ivy_switch`. The imported symbol will that point to either the
* symbol in `./ivy_switch_false` and `./ivy_switch_false` depending on the compilation mode.
*/ */
export * from './ivy_switch_legacy'; export * from './ivy_switch_legacy';

View File

@ -27,7 +27,7 @@ export interface DirectiveDecorator {
* runtime. * runtime.
* *
* Directive classes, like component classes, can implement * Directive classes, like component classes, can implement
* [life-cycle hooks](guide/lifecycle-hooks) to influence their configuration and behavior. * [life-cycle hoooks](guide/lifecycle-hooks) to influence their configuration and behavior.
* *
* *
* @usageNotes * @usageNotes

View File

@ -106,7 +106,7 @@ export function renderComponent<T>(
const rootContext = createRootContext(opts.scheduler || requestAnimationFrame.bind(window)); const rootContext = createRootContext(opts.scheduler || requestAnimationFrame.bind(window));
const rootView: LViewData = createLViewData( const rootView: LViewData = createLViewData(
rendererFactory.createRenderer(hostNode, componentDef), rendererFactory.createRenderer(hostNode, componentDef.rendererType),
createTView(-1, null, null, null, null), rootContext, createTView(-1, null, null, null, null), rootContext,
componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways); componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways);
rootView[INJECTOR] = opts.injector || null; rootView[INJECTOR] = opts.injector || null;
@ -123,6 +123,7 @@ export function renderComponent<T>(
// Create directive instance with factory() and store at index 0 in directives array // Create directive instance with factory() and store at index 0 in directives array
component = baseDirectiveCreate(0, componentDef.factory() as T, componentDef); component = baseDirectiveCreate(0, componentDef.factory() as T, componentDef);
rootContext.components.push(component); rootContext.components.push(component);
(elementNode.data as LViewData)[CONTEXT] = component; (elementNode.data as LViewData)[CONTEXT] = component;
initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !); initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !);

View File

@ -6,12 +6,12 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ChangeDetectorRef as ViewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref'; import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {InjectionToken} from '../di/injection_token'; import {InjectionToken} from '../di/injection_token';
import {Injector, inject} from '../di/injector'; import {Injector, inject} from '../di/injector';
import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory'; import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver'; import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
import {ElementRef as viewEngine_ElementRef} from '../linker/element_ref'; import {ElementRef} from '../linker/element_ref';
import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory'; import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory';
import {RendererFactory2} from '../render/api'; import {RendererFactory2} from '../render/api';
import {Type} from '../type'; import {Type} from '../type';
@ -21,7 +21,7 @@ import {LifecycleHooksFeature, createRootContext} from './component';
import {baseDirectiveCreate, createLNode, createLViewData, createTView, elementCreate, enterView, hostElement, initChangeDetectorIfExisting, locateHostElement, renderEmbeddedTemplate} from './instructions'; import {baseDirectiveCreate, createLNode, createLViewData, createTView, elementCreate, enterView, hostElement, initChangeDetectorIfExisting, locateHostElement, renderEmbeddedTemplate} from './instructions';
import {ComponentDefInternal, ComponentType, RenderFlags} from './interfaces/definition'; import {ComponentDefInternal, ComponentType, RenderFlags} from './interfaces/definition';
import {LElementNode, TNode, TNodeType} from './interfaces/node'; import {LElementNode, TNode, TNodeType} from './interfaces/node';
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer'; import {RElement, domRendererFactory3} from './interfaces/renderer';
import {CONTEXT, FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view'; import {CONTEXT, FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
import {RootViewRef, ViewRef} from './view_ref'; import {RootViewRef, ViewRef} from './view_ref';
@ -55,20 +55,8 @@ export const ROOT_CONTEXT = new InjectionToken<RootContext>(
* A change detection scheduler token for {@link RootContext}. This token is the default value used * A change detection scheduler token for {@link RootContext}. This token is the default value used
* for the default `RootContext` found in the {@link ROOT_CONTEXT} token. * for the default `RootContext` found in the {@link ROOT_CONTEXT} token.
*/ */
export const SCHEDULER = new InjectionToken<((fn: () => void) => void)>('SCHEDULER_TOKEN', { export const SCHEDULER = new InjectionToken<((fn: () => void) => void)>(
providedIn: 'root', 'SCHEDULER_TOKEN', {providedIn: 'root', factory: () => requestAnimationFrame.bind(window)});
factory: () => {
const useRaf = typeof requestAnimationFrame !== 'undefined' && typeof window !== 'undefined';
return useRaf ? requestAnimationFrame.bind(window) : setTimeout;
},
});
/**
* A function used to wrap the `RendererFactory2`.
* Used in tests to change the `RendererFactory2` into a `DebugRendererFactory2`.
*/
export const WRAP_RENDERER_FACTORY2 =
new InjectionToken<(rf: RendererFactory2) => RendererFactory2>('WRAP_RENDERER_FACTORY2');
/** /**
* Render3 implementation of {@link viewEngine_ComponentFactory}. * Render3 implementation of {@link viewEngine_ComponentFactory}.
@ -77,11 +65,9 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
selector: string; selector: string;
componentType: Type<any>; componentType: Type<any>;
ngContentSelectors: string[]; ngContentSelectors: string[];
get inputs(): {propName: string; templateName: string;}[] { get inputs(): {propName: string; templateName: string;}[] {
return toRefArray(this.componentDef.inputs); return toRefArray(this.componentDef.inputs);
} }
get outputs(): {propName: string; templateName: string;}[] { get outputs(): {propName: string; templateName: string;}[] {
return toRefArray(this.componentDef.outputs); return toRefArray(this.componentDef.outputs);
} }
@ -98,17 +84,11 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
ngModule?: viewEngine_NgModuleRef<any>|undefined): viewEngine_ComponentRef<T> { ngModule?: viewEngine_NgModuleRef<any>|undefined): viewEngine_ComponentRef<T> {
const isInternalRootView = rootSelectorOrNode === undefined; const isInternalRootView = rootSelectorOrNode === undefined;
let rendererFactory: RendererFactory3; const rendererFactory =
ngModule ? ngModule.injector.get(RendererFactory2) : domRendererFactory3;
if (ngModule) {
const wrapper = ngModule.injector.get(WRAP_RENDERER_FACTORY2, (v: RendererFactory2) => v);
rendererFactory = wrapper(ngModule.injector.get(RendererFactory2)) as RendererFactory3;
} else {
rendererFactory = domRendererFactory3;
}
const hostNode = isInternalRootView ? const hostNode = isInternalRootView ?
elementCreate(this.selector, rendererFactory.createRenderer(null, this.componentDef)) : elementCreate(
this.selector, rendererFactory.createRenderer(null, this.componentDef.rendererType)) :
locateHostElement(rendererFactory, rootSelectorOrNode); locateHostElement(rendererFactory, rootSelectorOrNode);
// The first index of the first selector is the tag name. // The first index of the first selector is the tag name.
@ -120,7 +100,7 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
// Create the root view. Uses empty TView and ContentTemplate. // Create the root view. Uses empty TView and ContentTemplate.
const rootView: LViewData = createLViewData( const rootView: LViewData = createLViewData(
rendererFactory.createRenderer(hostNode, this.componentDef), rendererFactory.createRenderer(hostNode, this.componentDef.rendererType),
createTView(-1, null, null, null, null), rootContext, createTView(-1, null, null, null, null), rootContext,
this.componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways); this.componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways);
rootView[INJECTOR] = ngModule && ngModule.injector || null; rootView[INJECTOR] = ngModule && ngModule.injector || null;
@ -137,10 +117,11 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
elementNode = hostElement(componentTag, hostNode, this.componentDef); elementNode = hostElement(componentTag, hostNode, this.componentDef);
// Create directive instance with factory() and store at index 0 in directives array // Create directive instance with factory() and store at index 0 in directives array
component = baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef); rootContext.components.push(
rootContext.components.push(component); component = baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef) as T);
initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !); initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !);
(elementNode.data as LViewData)[CONTEXT] = component; (elementNode.data as LViewData)[CONTEXT] = component;
// TODO: should LifecycleHooksFeature and other host features be generated by the compiler and // TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
// executed here? // executed here?
// Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
@ -197,11 +178,11 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
*/ */
export class ComponentRef<T> extends viewEngine_ComponentRef<T> { export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
destroyCbs: (() => void)[]|null = []; destroyCbs: (() => void)[]|null = [];
location: viewEngine_ElementRef<any>; location: ElementRef<any>;
injector: Injector; injector: Injector;
instance: T; instance: T;
hostView: ViewRef<T>; hostView: ViewRef<T>;
changeDetectorRef: ViewEngine_ChangeDetectorRef; changeDetectorRef: ChangeDetectorRef;
componentType: Type<T>; componentType: Type<T>;
constructor( constructor(
@ -212,7 +193,7 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
this.hostView = this.changeDetectorRef = new RootViewRef<T>(rootView); this.hostView = this.changeDetectorRef = new RootViewRef<T>(rootView);
this.hostView._lViewNode = createLNode(-1, TNodeType.View, null, null, null, rootView); this.hostView._lViewNode = createLNode(-1, TNodeType.View, null, null, null, rootView);
this.injector = injector; this.injector = injector;
this.location = new viewEngine_ElementRef(hostNode); this.location = new ElementRef(hostNode);
this.componentType = componentType; this.componentType = componentType;
} }

View File

@ -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 {Injector} from '../di/injector';
import {Renderer2, RendererType2} from '../render/api';
import {DebugContext} from '../view';
import {DebugRenderer2, DebugRendererFactory2} from '../view/services';
import * as di from './di';
import {NG_HOST_SYMBOL, _getViewData} from './instructions';
import {LElementNode} from './interfaces/node';
import {CONTEXT, DIRECTIVES, LViewData, TVIEW} from './interfaces/view';
/**
* Adapts the DebugRendererFactory2 to create a DebugRenderer2 specific for IVY.
*
* The created DebugRenderer know how to create a Debug Context specific to IVY.
*/
export class Render3DebugRendererFactory2 extends DebugRendererFactory2 {
createRenderer(element: any, renderData: RendererType2|null): Renderer2 {
const renderer = super.createRenderer(element, renderData) as DebugRenderer2;
renderer.debugContextFactory = () => new Render3DebugContext(_getViewData());
return renderer;
}
}
/**
* Stores context information about view nodes.
*
* Used in tests to retrieve information those nodes.
*/
class Render3DebugContext implements DebugContext {
readonly nodeIndex: number|null;
constructor(private viewData: LViewData) {
// The LNode will be created next and appended to viewData
this.nodeIndex = viewData ? viewData.length : null;
}
get view(): any { return this.viewData; }
get injector(): Injector {
if (this.nodeIndex !== null) {
const lElementNode: LElementNode = this.view[this.nodeIndex];
const nodeInjector = lElementNode.nodeInjector;
if (nodeInjector) {
return new di.NodeInjector(nodeInjector);
}
}
return Injector.NULL;
}
get component(): any {
// TODO(vicb): why/when
if (this.nodeIndex === null) {
return null;
}
const tView = this.view[TVIEW];
const components: number[]|null = tView.components;
return (components && components.indexOf(this.nodeIndex) == -1) ?
null :
this.view[this.nodeIndex].data[CONTEXT];
}
// TODO(vicb): add view providers when supported
get providerTokens(): any[] {
const matchedDirectives: any[] = [];
// TODO(vicb): why/when
if (this.nodeIndex === null) {
return matchedDirectives;
}
const directives = this.view[DIRECTIVES];
if (directives) {
const currentNode = this.view[this.nodeIndex];
for (let dirIndex = 0; dirIndex < directives.length; dirIndex++) {
const directive = directives[dirIndex];
if (directive[NG_HOST_SYMBOL] === currentNode) {
matchedDirectives.push(directive.constructor);
}
}
}
return matchedDirectives;
}
get references(): {[key: string]: any} {
// TODO(vicb): implement retrieving references
throw new Error('Not implemented yet in ivy');
}
get context(): any {
if (this.nodeIndex === null) {
return null;
}
const lNode = this.view[this.nodeIndex];
return lNode.view[CONTEXT];
}
get componentRenderElement(): any { throw new Error('Not implemented in ivy'); }
get renderNode(): any { throw new Error('Not implemented in ivy'); }
// TODO(vicb): check previous implementation
logError(console: Console, ...values: any[]): void { console.error(...values); }
}

View File

@ -6,23 +6,17 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import './ng_dev_mode';
import {ChangeDetectionStrategy} from '../change_detection/constants'; import {ChangeDetectionStrategy} from '../change_detection/constants';
import {Provider, ViewEncapsulation} from '../core'; import {Provider} from '../core';
import {NgModuleDef, NgModuleDefInternal} from '../metadata/ng_module'; import {NgModuleDef, NgModuleDefInternal} from '../metadata/ng_module';
import {RendererType2} from '../render/api';
import {Type} from '../type'; import {Type} from '../type';
import {resolveRendererType2} from '../view/util';
import {BaseDef, ComponentDefFeature, ComponentDefInternal, ComponentQuery, ComponentTemplate, ComponentType, DirectiveDefFeature, DirectiveDefInternal, DirectiveType, DirectiveTypesOrFactory, PipeDefInternal, PipeType, PipeTypesOrFactory} from './interfaces/definition'; import {BaseDef, ComponentDefFeature, ComponentDefInternal, ComponentQuery, ComponentTemplate, ComponentType, DirectiveDefFeature, DirectiveDefInternal, DirectiveType, DirectiveTypesOrFactory, PipeDefInternal, PipeType, PipeTypesOrFactory} from './interfaces/definition';
import {CssSelectorList, SelectorFlags} from './interfaces/projection'; import {CssSelectorList, SelectorFlags} from './interfaces/projection';
const EMPTY: {} = {};
const EMPTY_ARRAY: any[] = [];
if (typeof ngDevMode !== 'undefined' && ngDevMode) {
Object.freeze(EMPTY);
Object.freeze(EMPTY_ARRAY);
}
let _renderCompCount = 0;
/** /**
* Create a component definition object. * Create a component definition object.
@ -186,28 +180,8 @@ export function defineComponent<T>(componentDefinition: {
*/ */
features?: ComponentDefFeature[]; features?: ComponentDefFeature[];
/** rendererType?: RendererType2;
* Defines template and style encapsulation options available for Component's {@link Component}.
*/
encapsulation?: ViewEncapsulation;
/**
* Defines arbitrary developer-defined data to be stored on a renderer instance.
* This is useful for renderers that delegate to other renderers.
*
* see: animation
*/
data?: {[kind: string]: any};
/**
* A set of styles that the component needs to be present for component to render correctly.
*/
styles?: string[];
/**
* The strategy that the default change detector uses to detect changes.
* When set, takes effect the next time change detection is triggered.
*/
changeDetection?: ChangeDetectionStrategy; changeDetection?: ChangeDetectionStrategy;
/** /**
@ -241,7 +215,6 @@ export function defineComponent<T>(componentDefinition: {
const pipeTypes = componentDefinition.pipes !; const pipeTypes = componentDefinition.pipes !;
const directiveTypes = componentDefinition.directives !; const directiveTypes = componentDefinition.directives !;
const declaredInputs: {[key: string]: string} = {} as any; const declaredInputs: {[key: string]: string} = {} as any;
const encapsulation = componentDefinition.encapsulation;
const def: ComponentDefInternal<any> = { const def: ComponentDefInternal<any> = {
type: type, type: type,
diPublic: null, diPublic: null,
@ -254,6 +227,7 @@ export function defineComponent<T>(componentDefinition: {
inputs: invertObject(componentDefinition.inputs, declaredInputs), inputs: invertObject(componentDefinition.inputs, declaredInputs),
declaredInputs: declaredInputs, declaredInputs: declaredInputs,
outputs: invertObject(componentDefinition.outputs), outputs: invertObject(componentDefinition.outputs),
rendererType: resolveRendererType2(componentDefinition.rendererType) || null,
exportAs: componentDefinition.exportAs || null, exportAs: componentDefinition.exportAs || null,
onInit: type.prototype.ngOnInit || null, onInit: type.prototype.ngOnInit || null,
doCheck: type.prototype.ngDoCheck || null, doCheck: type.prototype.ngDoCheck || null,
@ -273,12 +247,6 @@ export function defineComponent<T>(componentDefinition: {
selectors: componentDefinition.selectors, selectors: componentDefinition.selectors,
viewQuery: componentDefinition.viewQuery || null, viewQuery: componentDefinition.viewQuery || null,
features: componentDefinition.features || null, features: componentDefinition.features || null,
data: componentDefinition.data || EMPTY,
// TODO(misko): convert ViewEncapsulation into const enum so that it can be used directly in the
// next line. Also `None` should be 0 not 2.
encapsulation: encapsulation == null ? 2 /* ViewEncapsulation.None */ : encapsulation,
id: `c${_renderCompCount++}`,
styles: EMPTY_ARRAY,
}; };
const feature = componentDefinition.features; const feature = componentDefinition.features;
feature && feature.forEach((fn) => fn(def)); feature && feature.forEach((fn) => fn(def));
@ -305,15 +273,17 @@ export function extractPipeDef(type: PipeType<any>): PipeDefInternal<any> {
export function defineNgModule<T>(def: {type: T} & Partial<NgModuleDef<T, any, any, any>>): never { export function defineNgModule<T>(def: {type: T} & Partial<NgModuleDef<T, any, any, any>>): never {
const res: NgModuleDefInternal<T> = { const res: NgModuleDefInternal<T> = {
type: def.type, type: def.type,
bootstrap: def.bootstrap || EMPTY_ARRAY, bootstrap: def.bootstrap || [],
declarations: def.declarations || EMPTY_ARRAY, declarations: def.declarations || [],
imports: def.imports || EMPTY_ARRAY, imports: def.imports || [],
exports: def.exports || EMPTY_ARRAY, exports: def.exports || [],
transitiveCompileScopes: null, transitiveCompileScopes: null,
}; };
return res as never; return res as never;
} }
const EMPTY = {};
/** /**
* Inverts an inputs or outputs lookup such that the keys, which were the * Inverts an inputs or outputs lookup such that the keys, which were the
* minified keys, are part of the values, and the values are parsed so that * minified keys, are part of the values, and the values are parsed so that

View File

@ -584,10 +584,15 @@ export function getOrCreateContainerRef(di: LInjector): viewEngine.ViewContainer
const hostParent = getParentLNode(vcRefHost) !; const hostParent = getParentLNode(vcRefHost) !;
const lContainer = createLContainer(hostParent, vcRefHost.view, true); const lContainer = createLContainer(hostParent, vcRefHost.view, true);
const comment = vcRefHost.view[RENDERER].createComment(ngDevMode ? 'container' : ''); const comment = vcRefHost.view[RENDERER].createComment(ngDevMode ? 'container' : '');
const lContainerNode: LContainerNode = const lContainerNode: LContainerNode = createLNodeObject(
createLNodeObject(TNodeType.Container, vcRefHost.view, hostParent, comment, lContainer); TNodeType.Container, vcRefHost.view, hostParent, comment, lContainer, null);
appendChild(hostParent, comment, vcRefHost.view); appendChild(hostParent, comment, vcRefHost.view);
if (vcRefHost.queries) {
lContainerNode.queries = vcRefHost.queries.container();
}
const hostTNode = vcRefHost.tNode as TElementNode | TContainerNode; const hostTNode = vcRefHost.tNode as TElementNode | TContainerNode;
if (!hostTNode.dynamicContainerNode) { if (!hostTNode.dynamicContainerNode) {
hostTNode.dynamicContainerNode = hostTNode.dynamicContainerNode =
@ -605,7 +610,7 @@ export function getOrCreateContainerRef(di: LInjector): viewEngine.ViewContainer
return di.viewContainerRef; return di.viewContainerRef;
} }
export class NodeInjector implements Injector { class NodeInjector implements Injector {
constructor(private _lInjector: LInjector) {} constructor(private _lInjector: LInjector) {}
get(token: any): any { get(token: any): any {
@ -777,17 +782,13 @@ export function getFactoryOf<T>(type: Type<any>): ((type?: Type<T>) => T)|null {
} }
export function getInheritedFactory<T>(type: Type<any>): (type: Type<T>) => T { export function getInheritedFactory<T>(type: Type<any>): (type: Type<T>) => T {
debugger;
const proto = Object.getPrototypeOf(type.prototype).constructor as Type<any>; const proto = Object.getPrototypeOf(type.prototype).constructor as Type<any>;
const factory = getFactoryOf<T>(proto); const factory = getFactoryOf<T>(proto);
if (factory !== null) { if (factory === null) {
return factory; throw new Error(`Type ${proto.name} does not support inheritance`);
} else {
// There is no factory defined. Either this was improper usage of inheritance
// (no Angular decorator on the superclass) or there is no constructor at all
// in the inheritance chain. Since the two cases cannot be distinguished, the
// latter has to be assumed.
return (t) => new t();
} }
return factory;
} }
class TemplateRef<T> implements viewEngine.TemplateRef<T> { class TemplateRef<T> implements viewEngine.TemplateRef<T> {

View File

@ -89,9 +89,7 @@ export function NgOnChangesFeature<T>(definition: DirectiveDefInternal<T>): void
} }
if (setter) setter.call(this, value); if (setter) setter.call(this, value);
}, }
// Make the property configurable in dev mode to allow overriding in tests
configurable: !!ngDevMode
}); });
} }
} }

View File

@ -13,12 +13,13 @@ import {NgOnChangesFeature} from './features/ng_onchanges_feature';
import {PublicFeature} from './features/public_feature'; import {PublicFeature} from './features/public_feature';
import {BaseDef, ComponentDef, ComponentDefInternal, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveDefInternal, DirectiveType, PipeDef} from './interfaces/definition'; import {BaseDef, ComponentDef, ComponentDefInternal, ComponentTemplate, ComponentType, DirectiveDef, DirectiveDefFlags, DirectiveDefInternal, DirectiveType, PipeDef} from './interfaces/definition';
export {ComponentFactory, ComponentFactoryResolver, ComponentRef, WRAP_RENDERER_FACTORY2} from './component_ref'; export {ComponentFactory, ComponentFactoryResolver, ComponentRef} from './component_ref';
export {Render3DebugRendererFactory2} from './debug';
export {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, directiveInject, getFactoryOf, getInheritedFactory, injectAttribute, injectChangeDetectorRef, injectComponentFactoryResolver, injectElementRef, injectTemplateRef, injectViewContainerRef} from './di'; export {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, directiveInject, getFactoryOf, getInheritedFactory, injectAttribute, injectChangeDetectorRef, injectComponentFactoryResolver, injectElementRef, injectTemplateRef, injectViewContainerRef} from './di';
export {RenderFlags} from './interfaces/definition'; export {RenderFlags} from './interfaces/definition';
export {CssSelectorList} from './interfaces/projection'; export {CssSelectorList} from './interfaces/projection';
// Naming scheme: // Naming scheme:
// - Capital letters are for creating things: T(Text), E(Element), D(Directive), V(View), // - Capital letters are for creating things: T(Text), E(Element), D(Directive), V(View),
// C(Container), L(Listener) // C(Container), L(Listener)

View File

@ -22,7 +22,7 @@ import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LEleme
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection'; import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
import {LQueries} from './interfaces/query'; import {LQueries} from './interfaces/query';
import {ProceduralRenderer3, RComment, RElement, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer'; import {ProceduralRenderer3, RComment, RElement, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
import {BINDING_INDEX, CLEANUP, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view'; import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getChildLNode, getLViewChild, getNextLNode, getParentLNode, insertView, removeView} from './node_manipulation'; import {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getChildLNode, getLViewChild, getNextLNode, getParentLNode, insertView, removeView} from './node_manipulation';
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher'; import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
@ -30,6 +30,8 @@ import {StylingContext, allocStylingContext, createStylingContextTemplate, rende
import {assertDataInRangeInternal, isDifferent, loadElementInternal, loadInternal, stringify} from './util'; import {assertDataInRangeInternal, isDifferent, loadElementInternal, loadInternal, stringify} from './util';
import {ViewRef} from './view_ref'; import {ViewRef} from './view_ref';
/** /**
* Directive (D) sets a property on all component instances using this constant as a key and the * Directive (D) sets a property on all component instances using this constant as a key and the
* component's host node (LElement) as the value. This is used in methods like detectChanges to * component's host node (LElement) as the value. This is used in methods like detectChanges to
@ -124,6 +126,16 @@ export function getCurrentView(): OpaqueViewState {
return viewData as any as OpaqueViewState; return viewData as any as OpaqueViewState;
} }
/**
* Internal function that returns the current LViewData instance.
*
* The getCurrentView() instruction should be used for anything public.
*/
export function _getViewData(): LViewData {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return viewData;
}
/** /**
* Restores `contextViewData` to the given OpaqueViewState instance. * Restores `contextViewData` to the given OpaqueViewState instance.
* *
@ -163,18 +175,12 @@ let currentQueries: LQueries|null;
* - when creating content queries (inb this previousOrParentNode points to a node on which we * - when creating content queries (inb this previousOrParentNode points to a node on which we
* create content queries). * create content queries).
*/ */
export function getOrCreateCurrentQueries( export function getCurrentQueries(QueryType: {new (): LQueries}): LQueries {
QueryType: {new (parent: null, shallow: null, deep: null): LQueries}): LQueries { // top level variables should not be exported for performance reasons (PERF_NOTES.md)
const tNode = previousOrParentNode.tNode; return currentQueries ||
(currentQueries =
// if this is the first content query on a node, any existing LQueries needs to be cloned (previousOrParentNode.queries && previousOrParentNode.queries.clone() ||
// in subsequent template passes, the cloning occurs before directive instantiation. new QueryType()));
if (previousOrParentNode.data !== viewData && !isContentQueryHost(tNode)) {
currentQueries && (currentQueries = currentQueries.clone());
tNode.flags |= TNodeFlags.hasContentQuery;
}
return currentQueries || (currentQueries = new QueryType(null, null, null));
} }
/** /**
@ -195,16 +201,6 @@ export function getCreationMode(): boolean {
*/ */
let viewData: LViewData; let viewData: LViewData;
/**
* Internal function that returns the current LViewData instance.
*
* The getCurrentView() instruction should be used for anything public.
*/
export function _getViewData(): LViewData {
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
return viewData;
}
/** /**
* The last viewData retrieved by nextContext(). * The last viewData retrieved by nextContext().
* Allows building nextContext() and reference() calls. * Allows building nextContext() and reference() calls.
@ -272,7 +268,6 @@ export function enterView(newView: LViewData, host: LElementNode | LViewNode | n
} }
viewData = contextViewData = newView; viewData = contextViewData = newView;
oldView && (oldView[QUERIES] = currentQueries);
currentQueries = newView && newView[QUERIES]; currentQueries = newView && newView[QUERIES];
return oldView; return oldView;
@ -397,13 +392,14 @@ export function createLViewData<T>(
*/ */
export function createLNodeObject( export function createLNodeObject(
type: TNodeType, currentView: LViewData, parent: LNode | null, type: TNodeType, currentView: LViewData, parent: LNode | null,
native: RText | RElement | RComment | null, native: RText | RElement | RComment | null, state: any,
state: any): LElementNode&LTextNode&LViewNode&LContainerNode&LProjectionNode { queries: LQueries | null): LElementNode&LTextNode&LViewNode&LContainerNode&LProjectionNode {
return { return {
native: native as any, native: native as any,
view: currentView, view: currentView,
nodeInjector: parent ? parent.nodeInjector : null, nodeInjector: parent ? parent.nodeInjector : null,
data: state, data: state,
queries: queries,
tNode: null !, tNode: null !,
dynamicLContainerNode: null dynamicLContainerNode: null
}; };
@ -446,9 +442,12 @@ export function createLNode(
// so it's only set if the view is the same. // so it's only set if the view is the same.
const tParent = const tParent =
parent && parent.view === viewData ? parent.tNode as TElementNode | TContainerNode : null; parent && parent.view === viewData ? parent.tNode as TElementNode | TContainerNode : null;
let queries =
(isParent ? currentQueries : previousOrParentNode && previousOrParentNode.queries) ||
parent && parent.queries && parent.queries.child();
const isState = state != null; const isState = state != null;
const node = createLNodeObject(type, viewData, parent, native, isState ? state as any : null); const node =
createLNodeObject(type, viewData, parent, native, isState ? state as any : null, queries);
if (index === -1 || type === TNodeType.View) { if (index === -1 || type === TNodeType.View) {
// View nodes are not stored in data because they can be added / removed at runtime (which // View nodes are not stored in data because they can be added / removed at runtime (which
@ -478,6 +477,7 @@ export function createLNode(
// Now link ourselves into the tree. // Now link ourselves into the tree.
if (isParent) { if (isParent) {
currentQueries = null;
if (previousOrParentNode.tNode.child == null && previousOrParentNode.view === viewData || if (previousOrParentNode.tNode.child == null && previousOrParentNode.view === viewData ||
previousOrParentNode.tNode.type === TNodeType.View) { previousOrParentNode.tNode.type === TNodeType.View) {
// We are in the same view, which means we are adding content node to the parent View. // We are in the same view, which means we are adding content node to the parent View.
@ -747,9 +747,8 @@ export function elementContainerEnd(): void {
} }
ngDevMode && assertNodeType(previousOrParentNode, TNodeType.ElementContainer); ngDevMode && assertNodeType(previousOrParentNode, TNodeType.ElementContainer);
const queries = previousOrParentNode.queries;
currentQueries && (currentQueries = currentQueries.addNode(previousOrParentNode)); queries && queries.addNode(previousOrParentNode);
queueLifecycleHooks(previousOrParentNode.tNode.flags, tView); queueLifecycleHooks(previousOrParentNode.tNode.flags, tView);
} }
@ -908,10 +907,6 @@ export function initChangeDetectorIfExisting(
} }
} }
export function isContentQueryHost(tNode: TNode): boolean {
return (tNode.flags & TNodeFlags.hasContentQuery) !== 0;
}
export function isComponent(tNode: TNode): boolean { export function isComponent(tNode: TNode): boolean {
return (tNode.flags & TNodeFlags.isComponent) === TNodeFlags.isComponent; return (tNode.flags & TNodeFlags.isComponent) === TNodeFlags.isComponent;
} }
@ -920,16 +915,9 @@ export function isComponent(tNode: TNode): boolean {
* This function instantiates the given directives. * This function instantiates the given directives.
*/ */
function instantiateDirectivesDirectly() { function instantiateDirectivesDirectly() {
ngDevMode && assertEqual(
firstTemplatePass, false,
`Directives should only be instantiated directly after first template pass`);
const tNode = previousOrParentNode.tNode; const tNode = previousOrParentNode.tNode;
const count = tNode.flags & TNodeFlags.DirectiveCountMask; const count = tNode.flags & TNodeFlags.DirectiveCountMask;
if (isContentQueryHost(tNode) && currentQueries) {
currentQueries = currentQueries.clone();
}
if (count > 0) { if (count > 0) {
const start = tNode.flags >> TNodeFlags.DirectiveStartingIndexShift; const start = tNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
const end = start + count; const end = start + count;
@ -1245,7 +1233,8 @@ export function elementEnd(): void {
previousOrParentNode = getParentLNode(previousOrParentNode) as LElementNode; previousOrParentNode = getParentLNode(previousOrParentNode) as LElementNode;
} }
ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Element); ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Element);
currentQueries && (currentQueries = currentQueries.addNode(previousOrParentNode)); const queries = previousOrParentNode.queries;
queries && queries.addNode(previousOrParentNode);
queueLifecycleHooks(previousOrParentNode.tNode.flags, tView); queueLifecycleHooks(previousOrParentNode.tNode.flags, tView);
currentElementNode = null; currentElementNode = null;
} }
@ -1666,8 +1655,9 @@ function addComponentLogic<T>(
const componentView = addToViewTree( const componentView = addToViewTree(
viewData, previousOrParentNode.tNode.index as number, viewData, previousOrParentNode.tNode.index as number,
createLViewData( createLViewData(
rendererFactory.createRenderer(previousOrParentNode.native as RElement, def), tView, rendererFactory.createRenderer(previousOrParentNode.native as RElement, def.rendererType),
instance, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, getCurrentSanitizer())); tView, instance, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways,
getCurrentSanitizer()));
// We need to set the host node/data here because when the component LNode was created, // We need to set the host node/data here because when the component LNode was created,
// we didn't yet know it was a component (just an element). // we didn't yet know it was a component (just an element).
@ -1838,8 +1828,8 @@ export function createLContainer(
* @param localRefs A set of local reference bindings on the element. * @param localRefs A set of local reference bindings on the element.
*/ */
export function container( export function container(
index: number, template?: ComponentTemplate<any>| null, tagName?: string | null, index: number, template?: ComponentTemplate<any>, tagName?: string | null, attrs?: TAttributes,
attrs?: TAttributes | null, localRefs?: string[] | null): void { localRefs?: string[] | null): void {
ngDevMode && ngDevMode &&
assertEqual( assertEqual(
viewData[BINDING_INDEX], -1, 'container nodes should be created before any bindings'); viewData[BINDING_INDEX], -1, 'container nodes should be created before any bindings');
@ -1863,17 +1853,17 @@ export function container(
// because views can be removed and re-inserted. // because views can be removed and re-inserted.
addToViewTree(viewData, index + HEADER_OFFSET, node.data); addToViewTree(viewData, index + HEADER_OFFSET, node.data);
if (currentQueries) { const queries = node.queries;
if (queries) {
// prepare place for matching nodes from views inserted into a given container // prepare place for matching nodes from views inserted into a given container
lContainer[QUERIES] = currentQueries.container(); lContainer[QUERIES] = queries.container();
} }
createDirectivesAndLocals(localRefs); createDirectivesAndLocals(localRefs);
isParent = false; isParent = false;
ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Container); ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Container);
// check if a given container node matches queries && queries.addNode(node); // check if a given container node matches
currentQueries && (currentQueries = currentQueries.addNode(node));
queueLifecycleHooks(node.tNode.flags, tView); queueLifecycleHooks(node.tNode.flags, tView);
} }

View File

@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Provider, ViewEncapsulation} from '../../core'; import {Provider} from '../../core';
import {RendererType2} from '../../render/api';
import {Type} from '../../type'; import {Type} from '../../type';
import {CssSelectorList} from './projection'; import {CssSelectorList} from './projection';
@ -186,42 +187,20 @@ export type ComponentDefInternal<T> = ComponentDef<T, string>;
* See: {@link defineComponent} * See: {@link defineComponent}
*/ */
export interface ComponentDef<T, Selector extends string> extends DirectiveDef<T, Selector> { export interface ComponentDef<T, Selector extends string> extends DirectiveDef<T, Selector> {
/**
* Runtime unique component ID.
*/
id: string;
/** /**
* The View template of the component. * The View template of the component.
*/ */
readonly template: ComponentTemplate<T>; readonly template: ComponentTemplate<T>;
/**
* A set of styles that the component needs to be present for component to render correctly.
*/
readonly styles: string[];
/** /**
* Query-related instructions for a component. * Query-related instructions for a component.
*/ */
readonly viewQuery: ComponentQuery<T>|null; readonly viewQuery: ComponentQuery<T>|null;
/** /**
* The view encapsulation type, which determines how styles are applied to * Renderer type data of the component.
* DOM elements. One of
* - `Emulated` (default): Emulate native scoping of styles.
* - `Native`: Use the native encapsulation mechanism of the renderer.
* - `ShadowDom`: Use modern [ShadowDOM](https://w3c.github.io/webcomponents/spec/shadow/) and
* create a ShadowRoot for component's host element.
* - `None`: Do not provide any template or style encapsulation.
*/ */
readonly encapsulation: ViewEncapsulation; readonly rendererType: RendererType2|null;
/**
* Defines arbitrary developer-defined data to be stored on a renderer instance.
* This is useful for renderers that delegate to other renderers.
*/
readonly data: {[kind: string]: any};
/** Whether or not this component's ChangeDetectionStrategy is OnPush */ /** Whether or not this component's ChangeDetectionStrategy is OnPush */
readonly onPush: boolean; readonly onPush: boolean;

View File

@ -42,11 +42,8 @@ export const enum TNodeFlags {
/** This bit is set if the node has been projected */ /** This bit is set if the node has been projected */
isProjected = 0b00000000000000000010000000000000, isProjected = 0b00000000000000000010000000000000,
/** This bit is set if the node has any content queries */
hasContentQuery = 0b00000000000000000100000000000000,
/** The index of the first directive on this node is encoded on the most significant bits */ /** The index of the first directive on this node is encoded on the most significant bits */
DirectiveStartingIndexShift = 15, DirectiveStartingIndexShift = 14,
} }
/** /**
@ -92,6 +89,13 @@ export interface LNode {
/** The injector associated with this node. Necessary for DI. */ /** The injector associated with this node. Necessary for DI. */
nodeInjector: LInjector|null; nodeInjector: LInjector|null;
/**
* Optional set of queries that track query-related events for this node.
*
* If present the node creation/updates are reported to the `LQueries`.
*/
queries: LQueries|null;
/** /**
* Pointer to the corresponding TNode object, which stores static * Pointer to the corresponding TNode object, which stores static
* data about this node. * data about this node.

View File

@ -13,27 +13,26 @@ import {LNode} from './node';
/** Used for tracking queries (e.g. ViewChild, ContentChild). */ /** Used for tracking queries (e.g. ViewChild, ContentChild). */
export interface LQueries { export interface LQueries {
/** /**
* The parent LQueries instance. * Ask queries to prepare copy of itself. This assures that tracking new queries on child nodes
*
* When there is a content query, a new LQueries instance is created to avoid mutating any
* existing LQueries. After we are done searching content children, the parent property allows
* us to traverse back up to the original LQueries instance to continue to search for matches
* in the main view.
*/
parent: LQueries|null;
/**
* Ask queries to prepare copy of itself. This assures that tracking new queries on content nodes
* doesn't mutate list of queries tracked on a parent node. We will clone LQueries before * doesn't mutate list of queries tracked on a parent node. We will clone LQueries before
* constructing content queries. * constructing content queries.
*/ */
clone(): LQueries; clone(): LQueries|null;
/**
* Used to ask queries if those should be cloned to the child element.
*
* For example in the case of deep queries the `child()` returns
* queries for the child node. In case of shallow queries it returns
* `null`.
*/
child(): LQueries|null;
/** /**
* Notify `LQueries` that a new `LNode` has been created and needs to be added to query results * Notify `LQueries` that a new `LNode` has been created and needs to be added to query results
* if matching query predicate. * if matching query predicate.
*/ */
addNode(node: LNode): LQueries|null; addNode(node: LNode): void;
/** /**
* Notify `LQueries` that a new LContainer was added to ivy data structures. As a result we need * Notify `LQueries` that a new LContainer was added to ivy data structures. As a result we need

View File

@ -15,6 +15,7 @@
* it will be easy to implement such API. * it will be easy to implement such API.
*/ */
import {ViewEncapsulation} from '../../metadata/view';
import {RendererStyleFlags2, RendererType2} from '../../render/api'; import {RendererStyleFlags2, RendererType2} from '../../render/api';

View File

@ -10,12 +10,13 @@ import {ConstantPool, R3DirectiveMetadata, WrappedNodeExpr, compileComponentFrom
import {Component, Directive, HostBinding, HostListener, Input, Output} from '../../metadata/directives'; import {Component, Directive, HostBinding, HostListener, Input, Output} from '../../metadata/directives';
import {componentNeedsResolution, maybeQueueResolutionOfComponentResources} from '../../metadata/resource_loading'; import {componentNeedsResolution, maybeQueueResolutionOfComponentResources} from '../../metadata/resource_loading';
import {ReflectionCapabilities} from '../../reflection/reflection_capabilities';
import {Type} from '../../type'; import {Type} from '../../type';
import {stringify} from '../../util'; import {stringify} from '../../util';
import {angularCoreEnv} from './environment'; import {angularCoreEnv} from './environment';
import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF} from './fields'; import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF} from './fields';
import {patchComponentDefWithScope, transitiveScopesFor} from './module'; import {patchComponentDefWithScope} from './module';
import {getReflect, reflectDependencies} from './util'; import {getReflect, reflectDependencies} from './util';
type StringMap = { type StringMap = {
@ -32,12 +33,12 @@ type StringMap = {
* until the global queue has been resolved with a call to `resolveComponentResources`. * until the global queue has been resolved with a call to `resolveComponentResources`.
*/ */
export function compileComponent(type: Type<any>, metadata: Component): void { export function compileComponent(type: Type<any>, metadata: Component): void {
let ngComponentDef: any = null; let def: any = null;
// Metadata may have resources which need to be resolved. // Metadata may have resources which need to be resolved.
maybeQueueResolutionOfComponentResources(metadata); maybeQueueResolutionOfComponentResources(metadata);
Object.defineProperty(type, NG_COMPONENT_DEF, { Object.defineProperty(type, NG_COMPONENT_DEF, {
get: () => { get: () => {
if (ngComponentDef === null) { if (def === null) {
if (componentNeedsResolution(metadata)) { if (componentNeedsResolution(metadata)) {
const error = [`Component '${stringify(type)}' is not resolved:`]; const error = [`Component '${stringify(type)}' is not resolved:`];
if (metadata.templateUrl) { if (metadata.templateUrl) {
@ -77,7 +78,7 @@ export function compileComponent(type: Type<any>, metadata: Component): void {
constantPool, makeBindingParser()); constantPool, makeBindingParser());
const preStatements = [...constantPool.statements, ...res.statements]; const preStatements = [...constantPool.statements, ...res.statements];
ngComponentDef = jitExpression( def = jitExpression(
res.expression, angularCoreEnv, `ng://${type.name}/ngComponentDef.js`, preStatements); res.expression, angularCoreEnv, `ng://${type.name}/ngComponentDef.js`, preStatements);
// If component compilation is async, then the @NgModule annotation which declares the // If component compilation is async, then the @NgModule annotation which declares the
@ -85,14 +86,11 @@ export function compileComponent(type: Type<any>, metadata: Component): void {
// allows the component to patch itself with directiveDefs from the module after it finishes // allows the component to patch itself with directiveDefs from the module after it finishes
// compiling. // compiling.
if (hasSelectorScope(type)) { if (hasSelectorScope(type)) {
const scopes = transitiveScopesFor(type.ngSelectorScope); patchComponentDefWithScope(def, type.ngSelectorScope);
patchComponentDefWithScope(ngComponentDef, scopes);
} }
} }
return ngComponentDef; return def;
}, },
// Make the property configurable in dev mode to allow overriding in tests
configurable: !!ngDevMode,
}); });
} }
@ -109,24 +107,23 @@ function hasSelectorScope<T>(component: Type<T>): component is Type<T>&
* will resolve when compilation completes and the directive becomes usable. * will resolve when compilation completes and the directive becomes usable.
*/ */
export function compileDirective(type: Type<any>, directive: Directive): void { export function compileDirective(type: Type<any>, directive: Directive): void {
let ngDirectiveDef: any = null; let def: any = null;
Object.defineProperty(type, NG_DIRECTIVE_DEF, { Object.defineProperty(type, NG_DIRECTIVE_DEF, {
get: () => { get: () => {
if (ngDirectiveDef === null) { if (def === null) {
const constantPool = new ConstantPool(); const constantPool = new ConstantPool();
const sourceMapUrl = `ng://${type && type.name}/ngDirectiveDef.js`; const sourceMapUrl = `ng://${type && type.name}/ngDirectiveDef.js`;
const res = compileR3Directive( const res = compileR3Directive(
directiveMetadata(type, directive), constantPool, makeBindingParser()); directiveMetadata(type, directive), constantPool, makeBindingParser());
const preStatements = [...constantPool.statements, ...res.statements]; const preStatements = [...constantPool.statements, ...res.statements];
ngDirectiveDef = jitExpression(res.expression, angularCoreEnv, sourceMapUrl, preStatements); def = jitExpression(res.expression, angularCoreEnv, sourceMapUrl, preStatements);
} }
return ngDirectiveDef; return def;
}, },
// Make the property configurable in dev mode to allow overriding in tests
configurable: !!ngDevMode,
}); });
} }
export function extendsDirectlyFromObject(type: Type<any>): boolean { export function extendsDirectlyFromObject(type: Type<any>): boolean {
return Object.getPrototypeOf(type.prototype) === Object.prototype; return Object.getPrototypeOf(type.prototype) === Object.prototype;
} }

View File

@ -18,28 +18,15 @@ import {reflectDependencies} from './util';
const EMPTY_ARRAY: Type<any>[] = []; const EMPTY_ARRAY: Type<any>[] = [];
/** export function compileNgModule(type: Type<any>, ngModule: NgModule): void {
* Compiles a module in JIT mode.
*
* This function automatically gets called when a class has a `@NgModule` decorator.
*/
export function compileNgModule(moduleType: Type<any>, ngModule: NgModule): void {
compileNgModuleDefs(moduleType, ngModule);
setScopeOnDeclaredComponents(moduleType, ngModule);
}
/**
* Compiles and adds the `ngModuleDef` and `ngInjectorDef` properties to the module class.
*/
export function compileNgModuleDefs(moduleType: Type<any>, ngModule: NgModule): void {
const declarations: Type<any>[] = flatten(ngModule.declarations || EMPTY_ARRAY); const declarations: Type<any>[] = flatten(ngModule.declarations || EMPTY_ARRAY);
let ngModuleDef: any = null; let ngModuleDef: any = null;
Object.defineProperty(moduleType, NG_MODULE_DEF, { Object.defineProperty(type, NG_MODULE_DEF, {
get: () => { get: () => {
if (ngModuleDef === null) { if (ngModuleDef === null) {
const meta: R3NgModuleMetadata = { const meta: R3NgModuleMetadata = {
type: wrap(moduleType), type: wrap(type),
bootstrap: flatten(ngModule.bootstrap || EMPTY_ARRAY).map(wrap), bootstrap: flatten(ngModule.bootstrap || EMPTY_ARRAY).map(wrap),
declarations: declarations.map(wrapReference), declarations: declarations.map(wrapReference),
imports: flatten(ngModule.imports || EMPTY_ARRAY) imports: flatten(ngModule.imports || EMPTY_ARRAY)
@ -51,23 +38,21 @@ export function compileNgModuleDefs(moduleType: Type<any>, ngModule: NgModule):
emitInline: true, emitInline: true,
}; };
const res = compileR3NgModule(meta); const res = compileR3NgModule(meta);
ngModuleDef = jitExpression( ngModuleDef =
res.expression, angularCoreEnv, `ng://${moduleType.name}/ngModuleDef.js`, []); jitExpression(res.expression, angularCoreEnv, `ng://${type.name}/ngModuleDef.js`, []);
} }
return ngModuleDef; return ngModuleDef;
}, },
// Make the property configurable in dev mode to allow overriding in tests
configurable: !!ngDevMode,
}); });
let ngInjectorDef: any = null; let ngInjectorDef: any = null;
Object.defineProperty(moduleType, NG_INJECTOR_DEF, { Object.defineProperty(type, NG_INJECTOR_DEF, {
get: () => { get: () => {
if (ngInjectorDef === null) { if (ngInjectorDef === null) {
const meta: R3InjectorMetadata = { const meta: R3InjectorMetadata = {
name: moduleType.name, name: type.name,
type: wrap(moduleType), type: wrap(type),
deps: reflectDependencies(moduleType), deps: reflectDependencies(type),
providers: new WrappedNodeExpr(ngModule.providers || EMPTY_ARRAY), providers: new WrappedNodeExpr(ngModule.providers || EMPTY_ARRAY),
imports: new WrappedNodeExpr([ imports: new WrappedNodeExpr([
ngModule.imports || EMPTY_ARRAY, ngModule.imports || EMPTY_ARRAY,
@ -76,36 +61,25 @@ export function compileNgModuleDefs(moduleType: Type<any>, ngModule: NgModule):
}; };
const res = compileInjector(meta); const res = compileInjector(meta);
ngInjectorDef = jitExpression( ngInjectorDef = jitExpression(
res.expression, angularCoreEnv, `ng://${moduleType.name}/ngInjectorDef.js`, res.expression, angularCoreEnv, `ng://${type.name}/ngInjectorDef.js`, res.statements);
res.statements);
} }
return ngInjectorDef; return ngInjectorDef;
}, },
// Make the property configurable in dev mode to allow overriding in tests
configurable: !!ngDevMode,
}); });
}
/**
* Some declared components may be compiled asynchronously, and thus may not have their
* ngComponentDef set yet. If this is the case, then a reference to the module is written into
* the `ngSelectorScope` property of the declared type.
*/
function setScopeOnDeclaredComponents(moduleType: Type<any>, ngModule: NgModule) {
const declarations: Type<any>[] = flatten(ngModule.declarations || EMPTY_ARRAY);
const transitiveScopes = transitiveScopesFor(moduleType);
declarations.forEach(declaration => { declarations.forEach(declaration => {
// Some declared components may be compiled asynchronously, and thus may not have their
// ngComponentDef set yet. If this is the case, then a reference to the module is written into
// the `ngSelectorScope` property of the declared type.
if (declaration.hasOwnProperty(NG_COMPONENT_DEF)) { if (declaration.hasOwnProperty(NG_COMPONENT_DEF)) {
// An `ngComponentDef` field exists - go ahead and patch the component directly. // An `ngComponentDef` field exists - go ahead and patch the component directly.
const component = declaration as Type<any>& {ngComponentDef: ComponentDefInternal<any>}; patchComponentDefWithScope(
const componentDef = component.ngComponentDef; (declaration as Type<any>& {ngComponentDef: ComponentDefInternal<any>}).ngComponentDef,
patchComponentDefWithScope(componentDef, transitiveScopes); type);
} else if ( } else if (
!declaration.hasOwnProperty(NG_DIRECTIVE_DEF) && !declaration.hasOwnProperty(NG_PIPE_DEF)) { !declaration.hasOwnProperty(NG_DIRECTIVE_DEF) && !declaration.hasOwnProperty(NG_PIPE_DEF)) {
// Set `ngSelectorScope` for future reference when the component compilation finishes. // Set `ngSelectorScope` for future reference when the component compilation finishes.
(declaration as Type<any>& {ngSelectorScope?: any}).ngSelectorScope = moduleType; (declaration as Type<any>& {ngSelectorScope?: any}).ngSelectorScope = type;
} }
}); });
} }
@ -114,13 +88,13 @@ function setScopeOnDeclaredComponents(moduleType: Type<any>, ngModule: NgModule)
* Patch the definition of a component with directives and pipes from the compilation scope of * Patch the definition of a component with directives and pipes from the compilation scope of
* a given module. * a given module.
*/ */
export function patchComponentDefWithScope<C>( export function patchComponentDefWithScope<C, M>(
componentDef: ComponentDefInternal<C>, transitiveScopes: NgModuleTransitiveScopes) { componentDef: ComponentDefInternal<C>, module: Type<M>) {
componentDef.directiveDefs = () => Array.from(transitiveScopes.compilation.directives) componentDef.directiveDefs = () => Array.from(transitiveScopesFor(module).compilation.directives)
.map(dir => dir.ngDirectiveDef || dir.ngComponentDef) .map(dir => dir.ngDirectiveDef || dir.ngComponentDef)
.filter(def => !!def); .filter(def => !!def);
componentDef.pipeDefs = () => componentDef.pipeDefs = () =>
Array.from(transitiveScopes.compilation.pipes).map(pipe => pipe.ngPipeDef); Array.from(transitiveScopesFor(module).compilation.pipes).map(pipe => pipe.ngPipeDef);
} }
/** /**
@ -165,7 +139,7 @@ export function transitiveScopesFor<T>(moduleType: Type<T>): NgModuleTransitiveS
}); });
def.imports.forEach(<I>(imported: Type<I>) => { def.imports.forEach(<I>(imported: Type<I>) => {
const importedTyped = imported as Type<I>& { let importedTyped = imported as Type<I>& {
// If imported is an @NgModule: // If imported is an @NgModule:
ngModuleDef?: NgModuleDefInternal<I>; ngModuleDef?: NgModuleDefInternal<I>;
}; };

View File

@ -17,10 +17,10 @@ import {NG_PIPE_DEF} from './fields';
import {reflectDependencies} from './util'; import {reflectDependencies} from './util';
export function compilePipe(type: Type<any>, meta: Pipe): void { export function compilePipe(type: Type<any>, meta: Pipe): void {
let ngPipeDef: any = null; let def: any = null;
Object.defineProperty(type, NG_PIPE_DEF, { Object.defineProperty(type, NG_PIPE_DEF, {
get: () => { get: () => {
if (ngPipeDef === null) { if (def === null) {
const sourceMapUrl = `ng://${stringify(type)}/ngPipeDef.js`; const sourceMapUrl = `ng://${stringify(type)}/ngPipeDef.js`;
const name = type.name; const name = type.name;
@ -32,11 +32,9 @@ export function compilePipe(type: Type<any>, meta: Pipe): void {
pure: meta.pure !== undefined ? meta.pure : true, pure: meta.pure !== undefined ? meta.pure : true,
}); });
ngPipeDef = jitExpression(res.expression, angularCoreEnv, sourceMapUrl, res.statements); def = jitExpression(res.expression, angularCoreEnv, sourceMapUrl, res.statements);
}
return def;
} }
return ngPipeDef;
},
// Make the property configurable in dev mode to allow overriding in tests
configurable: !!ngDevMode,
}); });
} }

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
declare global { declare global {
const ngDevMode: null|NgDevModePerfCounters; const ngDevMode: null|NgDevModePerfCounters;
interface NgDevModePerfCounters { interface NgDevModePerfCounters {
@ -32,17 +33,16 @@ declare global {
} }
} }
declare let global: any; declare let global: any;
// NOTE: The order here matters: Checking window, then global, then self is important.
// checking them in another order can result in errors in some Node environments.
const __global: {ngDevMode: NgDevModePerfCounters | boolean} = const __global: {ngDevMode: NgDevModePerfCounters | boolean} =
typeof window != 'undefined' && window || typeof global != 'undefined' && global || typeof window != 'undefined' && window || typeof self != 'undefined' && self ||
typeof self != 'undefined' && self; typeof global != 'undefined' && global;
export function ngDevModeResetPerfCounters(): NgDevModePerfCounters { export function ngDevModeResetPerfCounters(): NgDevModePerfCounters {
// Make sure to refer to ngDevMode as ['ngDevMode'] for clousre. return __global.ngDevMode = {
return __global['ngDevMode'] = {
firstTemplatePass: 0, firstTemplatePass: 0,
tNode: 0, tNode: 0,
tView: 0, tView: 0,
@ -75,6 +75,5 @@ export function ngDevModeResetPerfCounters(): NgDevModePerfCounters {
* as much early warning and errors as possible. * as much early warning and errors as possible.
*/ */
if (typeof ngDevMode === 'undefined' || ngDevMode) { if (typeof ngDevMode === 'undefined' || ngDevMode) {
// Make sure to refer to ngDevMode as ['ngDevMode'] for clousre. __global.ngDevMode = ngDevModeResetPerfCounters();
__global['ngDevMode'] = ngDevModeResetPerfCounters();
} }

View File

@ -17,7 +17,7 @@ import {getSymbolIterator} from '../util';
import {assertDefined, assertEqual} from './assert'; import {assertDefined, assertEqual} from './assert';
import {ReadFromInjectorFn, getOrCreateNodeInjectorForNode} from './di'; import {ReadFromInjectorFn, getOrCreateNodeInjectorForNode} from './di';
import {assertPreviousIsParent, getOrCreateCurrentQueries, isContentQueryHost, store, storeCleanupWithContext} from './instructions'; import {assertPreviousIsParent, getCurrentQueries, store, storeCleanupWithContext} from './instructions';
import {DirectiveDefInternal, unusedValueExportToPlacateAjd as unused1} from './interfaces/definition'; import {DirectiveDefInternal, unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
import {LInjector, unusedValueExportToPlacateAjd as unused2} from './interfaces/injector'; import {LInjector, unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
import {LContainerNode, LElementNode, LNode, TNode, TNodeFlags, unusedValueExportToPlacateAjd as unused3} from './interfaces/node'; import {LContainerNode, LElementNode, LNode, TNode, TNodeFlags, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';
@ -86,9 +86,10 @@ export interface LQuery<T> {
} }
export class LQueries_ implements LQueries { export class LQueries_ implements LQueries {
constructor( shallow: LQuery<any>|null = null;
public parent: LQueries_|null, private shallow: LQuery<any>|null, deep: LQuery<any>|null = null;
private deep: LQuery<any>|null) {}
constructor(deep?: LQuery<any>) { this.deep = deep == null ? null : deep; }
track<T>( track<T>(
queryList: viewEngine_QueryList<T>, predicate: Type<T>|string[], descend?: boolean, queryList: viewEngine_QueryList<T>, predicate: Type<T>|string[], descend?: boolean,
@ -100,94 +101,67 @@ export class LQueries_ implements LQueries {
} }
} }
clone(): LQueries { return new LQueries_(this, null, this.deep); } clone(): LQueries|null { return this.deep ? new LQueries_(this.deep) : null; }
child(): LQueries|null {
if (this.deep === null) {
// if we don't have any deep queries then no need to track anything more.
return null;
}
if (this.shallow === null) {
// DeepQuery: We can reuse the current state if the child state would be same as current
// state.
return this;
} else {
// We need to create new state
return new LQueries_(this.deep);
}
}
container(): LQueries|null { container(): LQueries|null {
const shallowResults = copyQueriesToContainer(this.shallow);
const deepResults = copyQueriesToContainer(this.deep);
return shallowResults || deepResults ? new LQueries_(this, shallowResults, deepResults) : null;
}
createView(): LQueries|null {
const shallowResults = copyQueriesToView(this.shallow);
const deepResults = copyQueriesToView(this.deep);
return shallowResults || deepResults ? new LQueries_(this, shallowResults, deepResults) : null;
}
insertView(index: number): void {
insertView(index, this.shallow);
insertView(index, this.deep);
}
addNode(node: LNode): LQueries|null {
add(this.deep, node);
if (isContentQueryHost(node.tNode)) {
add(this.shallow, node);
if (node.tNode.parent && isContentQueryHost(node.tNode.parent)) {
// if node has a content query and parent also has a content query
// both queries need to check this node for shallow matches
add(this.parent !.shallow, node);
}
return this.parent;
}
isRootNodeOfQuery(node.tNode) && add(this.shallow, node);
return this;
}
removeView(): void {
removeView(this.shallow);
removeView(this.deep);
}
}
function isRootNodeOfQuery(tNode: TNode) {
return tNode.parent === null || isContentQueryHost(tNode.parent);
}
function copyQueriesToContainer(query: LQuery<any>| null): LQuery<any>|null {
let result: LQuery<any>|null = null; let result: LQuery<any>|null = null;
let query = this.deep;
while (query) { while (query) {
const containerValues: any[] = []; // prepare room for views const containerValues: any[] = []; // prepare room for views
query.values.push(containerValues); query.values.push(containerValues);
const clonedQuery: LQuery<any> = { const clonedQuery: LQuery<any> = {
next: result, next: null,
list: query.list, list: query.list,
predicate: query.predicate, predicate: query.predicate,
values: containerValues, values: containerValues,
containerValues: null containerValues: null
}; };
clonedQuery.next = result;
result = clonedQuery; result = clonedQuery;
query = query.next; query = query.next;
} }
return result; return result ? new LQueries_(result) : null;
} }
function copyQueriesToView(query: LQuery<any>| null): LQuery<any>|null { createView(): LQueries|null {
let result: LQuery<any>|null = null; let result: LQuery<any>|null = null;
let query = this.deep;
while (query) { while (query) {
const clonedQuery: LQuery<any> = { const clonedQuery: LQuery<any> = {
next: result, next: null,
list: query.list, list: query.list,
predicate: query.predicate, predicate: query.predicate,
values: [], values: [],
containerValues: query.values containerValues: query.values
}; };
clonedQuery.next = result;
result = clonedQuery; result = clonedQuery;
query = query.next; query = query.next;
} }
return result; return result ? new LQueries_(result) : null;
} }
function insertView(index: number, query: LQuery<any>| null) { insertView(index: number): void {
let query = this.deep;
while (query) { while (query) {
ngDevMode && ngDevMode &&
assertDefined( assertDefined(
@ -197,7 +171,13 @@ function insertView(index: number, query: LQuery<any>| null) {
} }
} }
function removeView(query: LQuery<any>| null) { addNode(node: LNode): void {
add(this.shallow, node);
add(this.deep, node);
}
removeView(): void {
let query = this.deep;
while (query) { while (query) {
ngDevMode && ngDevMode &&
assertDefined( assertDefined(
@ -216,7 +196,7 @@ function removeView(query: LQuery<any>| null) {
query = query.next; query = query.next;
} }
} }
}
/** /**
* Iterates over local names for a given node and returns directive index * Iterates over local names for a given node and returns directive index
@ -438,7 +418,7 @@ export function query<T>(
read?: QueryReadType<T>| Type<T>): QueryList<T> { read?: QueryReadType<T>| Type<T>): QueryList<T> {
ngDevMode && assertPreviousIsParent(); ngDevMode && assertPreviousIsParent();
const queryList = new QueryList<T>(); const queryList = new QueryList<T>();
const queries = getOrCreateCurrentQueries(LQueries_); const queries = getCurrentQueries(LQueries_);
queries.track(queryList, predicate, descend, read); queries.track(queryList, predicate, descend, read);
storeCleanupWithContext(null, queryList, queryList.destroy); storeCleanupWithContext(null, queryList, queryList.destroy);
if (memoryIndex != null) { if (memoryIndex != null) {

View File

@ -22,7 +22,7 @@ import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './err
import {resolveDep} from './provider'; import {resolveDep} from './provider';
import {dirtyParentQueries, getQueryValue} from './query'; import {dirtyParentQueries, getQueryValue} from './query';
import {createInjector, createNgModuleRef, getComponentViewDefinitionFactory} from './refs'; import {createInjector, createNgModuleRef, getComponentViewDefinitionFactory} from './refs';
import {ArgumentType, BindingFlags, CheckType, DebugContext, ElementData, NgModuleDefinition, NodeDef, NodeFlags, NodeLogger, ProviderOverride, RootData, Services, ViewData, ViewDefinition, ViewState, asElementData, asPureExpressionData} from './types'; import {ArgumentType, BindingFlags, CheckType, DebugContext, DepDef, ElementData, NgModuleDefinition, NgModuleProviderDef, NodeDef, NodeFlags, NodeLogger, ProviderOverride, RootData, Services, ViewData, ViewDefinition, ViewState, asElementData, asPureExpressionData} from './types';
import {NOOP, isComponentView, renderNode, resolveDefinition, splitDepsDsl, viewParentEl} from './util'; import {NOOP, isComponentView, renderNode, resolveDefinition, splitDepsDsl, viewParentEl} from './util';
import {checkAndUpdateNode, checkAndUpdateView, checkNoChangesNode, checkNoChangesView, createComponentView, createEmbeddedView, createRootView, destroyView} from './view'; import {checkAndUpdateNode, checkAndUpdateView, checkNoChangesNode, checkNoChangesView, createComponentView, createEmbeddedView, createRootView, destroyView} from './view';
@ -509,7 +509,6 @@ class DebugContext_ implements DebugContext {
private nodeDef: NodeDef; private nodeDef: NodeDef;
private elView: ViewData; private elView: ViewData;
private elDef: NodeDef; private elDef: NodeDef;
constructor(public view: ViewData, public nodeIndex: number|null) { constructor(public view: ViewData, public nodeIndex: number|null) {
if (nodeIndex == null) { if (nodeIndex == null) {
this.nodeIndex = nodeIndex = 0; this.nodeIndex = nodeIndex = 0;
@ -529,18 +528,13 @@ class DebugContext_ implements DebugContext {
this.elDef = elDef; this.elDef = elDef;
this.elView = elView; this.elView = elView;
} }
private get elOrCompView() { private get elOrCompView() {
// Has to be done lazily as we use the DebugContext also during creation of elements... // Has to be done lazily as we use the DebugContext also during creation of elements...
return asElementData(this.elView, this.elDef.nodeIndex).componentView || this.view; return asElementData(this.elView, this.elDef.nodeIndex).componentView || this.view;
} }
get injector(): Injector { return createInjector(this.elView, this.elDef); } get injector(): Injector { return createInjector(this.elView, this.elDef); }
get component(): any { return this.elOrCompView.component; } get component(): any { return this.elOrCompView.component; }
get context(): any { return this.elOrCompView.context; } get context(): any { return this.elOrCompView.context; }
get providerTokens(): any[] { get providerTokens(): any[] {
const tokens: any[] = []; const tokens: any[] = [];
if (this.elDef) { if (this.elDef) {
@ -555,7 +549,6 @@ class DebugContext_ implements DebugContext {
} }
return tokens; return tokens;
} }
get references(): {[key: string]: any} { get references(): {[key: string]: any} {
const references: {[key: string]: any} = {}; const references: {[key: string]: any} = {};
if (this.elDef) { if (this.elDef) {
@ -572,17 +565,14 @@ class DebugContext_ implements DebugContext {
} }
return references; return references;
} }
get componentRenderElement() { get componentRenderElement() {
const elData = findHostElement(this.elOrCompView); const elData = findHostElement(this.elOrCompView);
return elData ? elData.renderElement : undefined; return elData ? elData.renderElement : undefined;
} }
get renderNode(): any { get renderNode(): any {
return this.nodeDef.flags & NodeFlags.TypeText ? renderNode(this.view, this.nodeDef) : return this.nodeDef.flags & NodeFlags.TypeText ? renderNode(this.view, this.nodeDef) :
renderNode(this.elView, this.elDef); renderNode(this.elView, this.elDef);
} }
logError(console: Console, ...values: any[]) { logError(console: Console, ...values: any[]) {
let logViewDef: ViewDefinition; let logViewDef: ViewDefinition;
let logNodeIndex: number; let logNodeIndex: number;
@ -663,7 +653,8 @@ export function getCurrentDebugContext(): DebugContext|null {
return _currentView ? new DebugContext_(_currentView, _currentNodeIndex) : null; return _currentView ? new DebugContext_(_currentView, _currentNodeIndex) : null;
} }
export class DebugRendererFactory2 implements RendererFactory2 {
class DebugRendererFactory2 implements RendererFactory2 {
constructor(private delegate: RendererFactory2) {} constructor(private delegate: RendererFactory2) {}
createRenderer(element: any, renderData: RendererType2|null): Renderer2 { createRenderer(element: any, renderData: RendererType2|null): Renderer2 {
@ -689,21 +680,9 @@ export class DebugRendererFactory2 implements RendererFactory2 {
} }
} }
export class DebugRenderer2 implements Renderer2 {
class DebugRenderer2 implements Renderer2 {
readonly data: {[key: string]: any}; readonly data: {[key: string]: any};
/**
* Factory function used to create a `DebugContext` when a node is created.
*
* The `DebugContext` allows to retrieve information about the nodes that are useful in tests.
*
* The factory is configurable so that the `DebugRenderer2` could instantiate either a View Engine
* or a Render context.
*/
debugContextFactory: () => DebugContext | null = getCurrentDebugContext;
private get debugContext() { return this.debugContextFactory(); }
constructor(private delegate: Renderer2) { this.data = this.delegate.data; } constructor(private delegate: Renderer2) { this.data = this.delegate.data; }
destroyNode(node: any) { destroyNode(node: any) {
@ -717,7 +696,7 @@ export class DebugRenderer2 implements Renderer2 {
createElement(name: string, namespace?: string): any { createElement(name: string, namespace?: string): any {
const el = this.delegate.createElement(name, namespace); const el = this.delegate.createElement(name, namespace);
const debugCtx = this.debugContext; const debugCtx = getCurrentDebugContext();
if (debugCtx) { if (debugCtx) {
const debugEl = new DebugElement(el, null, debugCtx); const debugEl = new DebugElement(el, null, debugCtx);
debugEl.name = name; debugEl.name = name;
@ -728,7 +707,7 @@ export class DebugRenderer2 implements Renderer2 {
createComment(value: string): any { createComment(value: string): any {
const comment = this.delegate.createComment(value); const comment = this.delegate.createComment(value);
const debugCtx = this.debugContext; const debugCtx = getCurrentDebugContext();
if (debugCtx) { if (debugCtx) {
indexDebugNode(new DebugNode(comment, null, debugCtx)); indexDebugNode(new DebugNode(comment, null, debugCtx));
} }
@ -737,7 +716,7 @@ export class DebugRenderer2 implements Renderer2 {
createText(value: string): any { createText(value: string): any {
const text = this.delegate.createText(value); const text = this.delegate.createText(value);
const debugCtx = this.debugContext; const debugCtx = getCurrentDebugContext();
if (debugCtx) { if (debugCtx) {
indexDebugNode(new DebugNode(text, null, debugCtx)); indexDebugNode(new DebugNode(text, null, debugCtx));
} }
@ -775,7 +754,7 @@ export class DebugRenderer2 implements Renderer2 {
selectRootElement(selectorOrNode: string|any): any { selectRootElement(selectorOrNode: string|any): any {
const el = this.delegate.selectRootElement(selectorOrNode); const el = this.delegate.selectRootElement(selectorOrNode);
const debugCtx = this.debugContext; const debugCtx = getCurrentDebugContext();
if (debugCtx) { if (debugCtx) {
indexDebugNode(new DebugElement(el, null, debugCtx)); indexDebugNode(new DebugElement(el, null, debugCtx));
} }

View File

@ -6,33 +6,44 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ApplicationRef, Component, DoCheck, NgModule, OnInit, TestabilityRegistry, ɵivyEnabled as ivyEnabled} from '@angular/core'; import {ApplicationModule, ApplicationRef, DoCheck, InjectFlags, InjectorType, Input, OnInit, PlatformRef, TestabilityRegistry, Type, defineInjector, inject, ɵE as elementStart, ɵNgModuleDef as NgModuleDef, ɵRenderFlags as RenderFlags, ɵT as text, ɵdefineComponent as defineComponent, ɵe as elementEnd, ɵi1 as interpolation1, ɵt as textBinding} from '@angular/core';
import {getTestBed} from '@angular/core/testing'; import {getTestBed} from '@angular/core/testing';
import {BrowserModule} from '@angular/platform-browser'; import {BrowserModule, EVENT_MANAGER_PLUGINS, platformBrowser} from '@angular/platform-browser';
import {withBody} from '@angular/private/testing'; import {withBody} from '@angular/private/testing';
import {BROWSER_MODULE_PROVIDERS} from '../../platform-browser/src/browser';
import {APPLICATION_MODULE_PROVIDERS} from '../src/application_module';
import {NgModuleFactory} from '../src/render3/ng_module_ref'; import {NgModuleFactory} from '../src/render3/ng_module_ref';
ivyEnabled && describe('ApplicationRef bootstrap', () => { describe('ApplicationRef bootstrap', () => {
@Component({ class HelloWorldComponent implements OnInit, DoCheck {
selector: 'hello-world',
template: '<div>Hello {{ name }}</div>',
})
class HelloWorldComponent implements OnInit,
DoCheck {
log: string[] = []; log: string[] = [];
name = 'World'; name = 'World';
static ngComponentDef = defineComponent({
type: HelloWorldComponent,
selectors: [['hello-world']],
factory: () => new HelloWorldComponent(),
template: function(rf: RenderFlags, ctx: HelloWorldComponent): void {
if (rf & RenderFlags.Create) {
elementStart(0, 'div');
text(1);
elementEnd();
}
if (rf & RenderFlags.Update) {
textBinding(1, interpolation1('Hello ', ctx.name, ''));
}
}
});
ngOnInit(): void { this.log.push('OnInit'); } ngOnInit(): void { this.log.push('OnInit'); }
ngDoCheck(): void { this.log.push('DoCheck'); } ngDoCheck(): void { this.log.push('DoCheck'); }
} }
@NgModule({
declarations: [HelloWorldComponent],
bootstrap: [HelloWorldComponent],
imports: [BrowserModule],
})
class MyAppModule { class MyAppModule {
static ngInjectorDef =
defineInjector({factory: () => new MyAppModule(), imports: [BrowserModule]});
static ngModuleDef = defineNgModule({bootstrap: [HelloWorldComponent]});
} }
it('should bootstrap hello world', withBody('<hello-world></hello-world>', async() => { it('should bootstrap hello world', withBody('<hello-world></hello-world>', async() => {
@ -55,3 +66,29 @@ ivyEnabled && describe('ApplicationRef bootstrap', () => {
})); }));
}); });
/////////////////////////////////////////////////////////
// These go away when Compiler is ready
(BrowserModule as any as InjectorType<BrowserModule>).ngInjectorDef = defineInjector({
factory: function BrowserModule_Factory() {
return new BrowserModule(inject(BrowserModule, InjectFlags.Optional | InjectFlags.SkipSelf));
},
imports: [ApplicationModule],
providers: BROWSER_MODULE_PROVIDERS
});
(ApplicationModule as any as InjectorType<ApplicationModule>).ngInjectorDef = defineInjector({
factory: function ApplicationModule_Factory() {
return new ApplicationModule(inject(ApplicationRef));
},
providers: APPLICATION_MODULE_PROVIDERS
});
export function defineNgModule({bootstrap}: {bootstrap?: Type<any>[]}):
NgModuleDef<any, any, any, any> {
return ({ bootstrap: bootstrap || [], } as any);
}
/////////////////////////////////////////////////////////

View File

@ -27,7 +27,7 @@
"name": "EMPTY$1" "name": "EMPTY$1"
}, },
{ {
"name": "EMPTY_ARRAY$2" "name": "EMPTY_RENDERER_TYPE_ID"
}, },
{ {
"name": "FLAGS" "name": "FLAGS"
@ -77,9 +77,15 @@
{ {
"name": "TVIEW" "name": "TVIEW"
}, },
{
"name": "UNDEFINED_RENDERER_TYPE_ID"
},
{ {
"name": "VIEWS" "name": "VIEWS"
}, },
{
"name": "ViewEncapsulation$1"
},
{ {
"name": "_CLEAN_PROMISE" "name": "_CLEAN_PROMISE"
}, },
@ -263,6 +269,9 @@
{ {
"name": "resetApplicationState" "name": "resetApplicationState"
}, },
{
"name": "resolveRendererType2"
},
{ {
"name": "setHostBindings" "name": "setHostBindings"
}, },

View File

@ -45,7 +45,7 @@
"name": "EMPTY$1" "name": "EMPTY$1"
}, },
{ {
"name": "EMPTY_ARRAY$2" "name": "EMPTY_RENDERER_TYPE_ID"
}, },
{ {
"name": "ElementRef" "name": "ElementRef"
@ -182,6 +182,9 @@
{ {
"name": "TodoStore" "name": "TodoStore"
}, },
{
"name": "UNDEFINED_RENDERER_TYPE_ID"
},
{ {
"name": "VIEWS" "name": "VIEWS"
}, },
@ -191,6 +194,9 @@
{ {
"name": "ViewContainerRef$1" "name": "ViewContainerRef$1"
}, },
{
"name": "ViewEncapsulation$1"
},
{ {
"name": "ViewRef" "name": "ViewRef"
}, },
@ -671,9 +677,6 @@
{ {
"name": "isComponent" "name": "isComponent"
}, },
{
"name": "isContentQueryHost"
},
{ {
"name": "isContextDirty" "name": "isContextDirty"
}, },
@ -830,6 +833,9 @@
{ {
"name": "resolveDirective" "name": "resolveDirective"
}, },
{
"name": "resolveRendererType2"
},
{ {
"name": "restoreView" "name": "restoreView"
}, },

View File

@ -6,19 +6,19 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Component} from '../../../src/core'; 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 * as $r3$ from '../../../src/core_render3_private_export';
import {ComponentFixture} from '../render_util'; import {renderComponent, toHtml} from '../render_util';
/// See: `normative.md` /// See: `normative.md`
describe('local references', () => { describe('local references', () => {
type $RenderFlags$ = $r3$.ɵRenderFlags; type $RenderFlags$ = $r3$.ɵRenderFlags;
it('should translate DOM structure', () => { // TODO(misko): currently disabled until local refs are working
xit('should translate DOM structure', () => {
type $MyComponent$ = MyComponent; type $MyComponent$ = MyComponent;
@Component( @Component({selector: 'my-component', template: `<input #user>Hello {{user.value}}!`})
{selector: 'my-component', template: `<input #user value="World">Hello, {{user.value}}!`})
class MyComponent { class MyComponent {
// NORMATIVE // NORMATIVE
static ngComponentDef = $r3$.ɵdefineComponent({ static ngComponentDef = $r3$.ɵdefineComponent({
@ -28,19 +28,19 @@ describe('local references', () => {
template: function(rf: $RenderFlags$, ctx: $MyComponent$) { template: function(rf: $RenderFlags$, ctx: $MyComponent$) {
let l1_user: any; let l1_user: any;
if (rf & 1) { if (rf & 1) {
$r3$.ɵEe(0, 'input', ['value', 'World'], ['user', '']); $r3$.ɵEe(0, 'input', null, ['user', '']);
$r3$.ɵT(2); $r3$.ɵT(2);
} }
if (rf & 2) { if (rf & 2) {
l1_user = $r3$.ɵr<any>(1); l1_user = $r3$.ɵld<any>(1);
$r3$.ɵt(2, $r3$.ɵi1('Hello, ', l1_user.value, '!')); $r3$.ɵt(2, $r3$.ɵi1('Hello ', l1_user.value, '!'));
} }
} }
}); });
// NORMATIVE // NORMATIVE
} }
const fixture = new ComponentFixture(MyComponent); expect(toHtml(renderComponent(MyComponent)))
expect(fixture.html).toEqual(`<input value="World">Hello, World!`); .toEqual('<div class="my-app" title="Hello">Hello <b>World</b>!</div>');
}); });
}); });

View File

@ -204,9 +204,8 @@ describe('encapsulation', () => {
} }
}, },
factory: () => new EncapsulatedComponent, factory: () => new EncapsulatedComponent,
encapsulation: ViewEncapsulation.Emulated, rendererType:
styles: [], createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}),
data: {},
directives: () => [LeafComponent] directives: () => [LeafComponent]
}); });
} }
@ -251,9 +250,8 @@ describe('encapsulation', () => {
} }
}, },
factory: () => new WrapperComponentWith, factory: () => new WrapperComponentWith,
encapsulation: ViewEncapsulation.Emulated, rendererType:
styles: [], createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}),
data: {},
directives: () => [LeafComponentwith] directives: () => [LeafComponentwith]
}); });
} }
@ -270,9 +268,8 @@ describe('encapsulation', () => {
} }
}, },
factory: () => new LeafComponentwith, factory: () => new LeafComponentwith,
encapsulation: ViewEncapsulation.Emulated, rendererType:
styles: [], createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}),
data: {},
}); });
} }

View File

@ -1159,7 +1159,7 @@ describe('di', () => {
/** <div *myIf="showing" dir dirSameInstance #dir="dir"> {{ dir.value }} </div> */ /** <div *myIf="showing" dir dirSameInstance #dir="dir"> {{ dir.value }} </div> */
template: function(rf: RenderFlags, ctx: MyApp) { template: function(rf: RenderFlags, ctx: MyApp) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
container(0, C1, null, ['myIf', 'showing']); container(0, C1, undefined, ['myIf', 'showing']);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
containerRefreshStart(0); containerRefreshStart(0);

View File

@ -9,12 +9,13 @@
import {ɵAnimationEngine, ɵNoopAnimationStyleNormalizer} from '@angular/animations/browser'; import {ɵAnimationEngine, ɵNoopAnimationStyleNormalizer} from '@angular/animations/browser';
import {MockAnimationDriver} from '@angular/animations/browser/testing'; import {MockAnimationDriver} from '@angular/animations/browser/testing';
import {NgZone, RendererFactory2} from '@angular/core'; import {NgZone, RendererFactory2} from '@angular/core';
import {NoopNgZone} from '@angular/core/src/zone/ng_zone';
import {EventManager, ɵDomRendererFactory2, ɵDomSharedStylesHost} from '@angular/platform-browser'; import {EventManager, ɵDomRendererFactory2, ɵDomSharedStylesHost} from '@angular/platform-browser';
import {ɵAnimationRendererFactory} from '@angular/platform-browser/animations'; import {ɵAnimationRendererFactory} from '@angular/platform-browser/animations';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {EventManagerPlugin} from '@angular/platform-browser/src/dom/events/event_manager'; import {EventManagerPlugin} from '@angular/platform-browser/src/dom/events/event_manager';
import {NoopNgZone} from '../../src/zone/ng_zone';
export class SimpleDomEventsPlugin extends EventManagerPlugin { export class SimpleDomEventsPlugin extends EventManagerPlugin {
constructor(doc: any) { super(doc); } constructor(doc: any) { super(doc); }

View File

@ -11,7 +11,7 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
import {EventEmitter} from '../..'; import {EventEmitter} from '../..';
import {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di'; import {QUERY_READ_CONTAINER_REF, QUERY_READ_ELEMENT_REF, QUERY_READ_FROM_NODE, QUERY_READ_TEMPLATE_REF, getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di';
import {AttributeMarker, QueryList, defineComponent, defineDirective, detectChanges, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index'; import {AttributeMarker, QueryList, defineComponent, defineDirective, detectChanges, injectViewContainerRef} from '../../src/render3/index';
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadDirective, loadElement, loadQueryList, registerContentQuery} from '../../src/render3/instructions'; import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementContainerEnd, elementContainerStart, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, loadDirective, loadElement, loadQueryList, registerContentQuery} from '../../src/render3/instructions';
import {RenderFlags} from '../../src/render3/interfaces/definition'; import {RenderFlags} from '../../src/render3/interfaces/definition';
import {query, queryRefresh} from '../../src/render3/query'; import {query, queryRefresh} from '../../src/render3/query';
@ -49,7 +49,7 @@ function isViewContainerRef(candidate: any): boolean {
} }
describe('query', () => { describe('query', () => {
it('should match projected query children', () => { it('should project query children', () => {
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {}); const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {});
let child1 = null; let child1 = null;
@ -510,7 +510,7 @@ describe('query', () => {
'cmpt', 'cmpt',
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
container(1, null, null, null, ['foo', '']); container(1, undefined, undefined, undefined, ['foo', '']);
} }
}, },
[], [], [], [],
@ -542,7 +542,7 @@ describe('query', () => {
'cmpt', 'cmpt',
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
container(1, null, null, null, ['foo', '']); container(1, undefined, undefined, undefined, ['foo', '']);
} }
}, },
[], [], [], [],
@ -577,7 +577,7 @@ describe('query', () => {
'cmpt', 'cmpt',
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
container(1, null, null, null, ['foo', '']); container(1, undefined, undefined, undefined, ['foo', '']);
} }
}, },
[], [], [], [],
@ -609,7 +609,7 @@ describe('query', () => {
'cmpt', 'cmpt',
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
container(1, null, null, null, ['foo', '']); container(1, undefined, undefined, undefined, ['foo', '']);
} }
}, },
[], [], [], [],
@ -1129,7 +1129,7 @@ describe('query', () => {
} }
}, null, []); }, null, []);
container(5, null, null, [AttributeMarker.SelectOnly, 'vc']); container(5, undefined, null, [AttributeMarker.SelectOnly, 'vc']);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
@ -1221,8 +1221,8 @@ describe('query', () => {
} }
}, null, []); }, null, []);
container(2, null, null, [AttributeMarker.SelectOnly, 'vc']); container(2, undefined, null, [AttributeMarker.SelectOnly, 'vc']);
container(3, null, null, [AttributeMarker.SelectOnly, 'vc']); container(3, undefined, null, [AttributeMarker.SelectOnly, 'vc']);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
@ -1288,7 +1288,7 @@ describe('query', () => {
element(0, 'span', ['id', 'from_tpl'], ['foo', '']); element(0, 'span', ['id', 'from_tpl'], ['foo', '']);
} }
}, undefined, undefined, ['tpl', '']); }, undefined, undefined, ['tpl', '']);
container(3, null, null, [AttributeMarker.SelectOnly, 'ngTemplateOutlet']); container(3, undefined, null, [AttributeMarker.SelectOnly, 'ngTemplateOutlet']);
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
const tplRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(1))); const tplRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(1)));
@ -1617,9 +1617,7 @@ describe('query', () => {
it('should support combination of deep and shallow queries', () => { it('should support combination of deep and shallow queries', () => {
/** /**
* % if (exp) { "> * % if (exp) { ">
* <div #foo>
* <div #foo></div> * <div #foo></div>
* </div>
* % } * % }
* <span #foo></span> * <span #foo></span>
* class Cmpt { * class Cmpt {
@ -1641,9 +1639,7 @@ describe('query', () => {
let rf0 = embeddedViewStart(0); let rf0 = embeddedViewStart(0);
{ {
if (rf0 & RenderFlags.Create) { if (rf0 & RenderFlags.Create) {
elementStart(0, 'div', null, ['foo', '']); element(0, 'div', null, ['foo', '']);
{ element(2, 'div', null, ['foo', '']); }
elementEnd();
} }
} }
embeddedViewEnd(); embeddedViewEnd();
@ -1675,12 +1671,8 @@ describe('query', () => {
cmptInstance.exp = true; cmptInstance.exp = true;
detectChanges(cmptInstance); detectChanges(cmptInstance);
expect(deep.length).toBe(3); expect(deep.length).toBe(2);
expect(shallow.length).toBe(1);
// embedded % if blocks should behave the same way as *ngIf, namely they
// should match shallow queries on the first level of elements underneath
// the embedded view boundary.
expect(shallow.length).toBe(2);
cmptInstance.exp = false; cmptInstance.exp = false;
detectChanges(cmptInstance); detectChanges(cmptInstance);
@ -1780,63 +1772,13 @@ describe('query', () => {
}); });
}); });
it('should restore queries if view changes', () => {
class SomeDir {
constructor(public vcr: ViewContainerRef, public temp: TemplateRef<any>) {
this.vcr.createEmbeddedView(this.temp);
}
static ngDirectiveDef = defineDirective({
type: SomeDir,
selectors: [['', 'someDir', '']],
factory: () => new SomeDir(injectViewContainerRef(), injectTemplateRef())
});
}
function template(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
element(0, 'div');
}
}
/**
* <div *someDir></div>
* <div #foo></div>
*/
const AppComponent = createComponent(
'app',
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
container(1, template, null, [AttributeMarker.SelectOnly, 'someDir']);
element(2, 'div', null, ['foo', '']);
}
},
[SomeDir], [],
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
query(0, ['foo'], true, QUERY_READ_FROM_NODE);
}
if (rf & RenderFlags.Update) {
let tmp: any;
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.query = tmp as QueryList<any>);
}
});
const fixture = new ComponentFixture(AppComponent);
expect(fixture.component.query.length).toBe(1);
});
describe('content', () => { describe('content', () => {
let withContentInstance: WithContentDirective|null;
let shallowCompInstance: ShallowComp|null;
beforeEach(() => { it('should support content queries for directives', () => {
withContentInstance = null; let withContentInstance: WithContentDirective|null = null;
shallowCompInstance = null;
});
class WithContentDirective { class WithContentDirective {
// @ContentChildren('foo') // @ContentChildren('foo') foos;
foos !: QueryList<ElementRef>; foos !: QueryList<ElementRef>;
contentInitQuerySnapshot = 0; contentInitQuerySnapshot = 0;
contentCheckedQuerySnapshot = 0; contentCheckedQuerySnapshot = 0;
@ -1862,31 +1804,12 @@ describe('query', () => {
}); });
} }
class ShallowComp {
// @ContentChildren('foo', {descendants: false})
foos !: QueryList<ElementRef>;
static ngComponentDef = defineComponent({
type: ShallowComp,
selectors: [['shallow-comp']],
factory: () => new ShallowComp(),
template: function(rf: RenderFlags, ctx: any) {},
contentQueries:
() => { registerContentQuery(query(null, ['foo'], false, QUERY_READ_FROM_NODE)); },
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
shallowCompInstance = loadDirective<ShallowComp>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(shallowCompInstance.foos = tmp);
}
});
}
it('should support content queries for directives', () => {
/** /**
* <div with-content> * <div with-content>
* <span #foo></span> * <span #foo></span>
* </div> * </div>
* class Cmpt {
* }
*/ */
const AppComponent = createComponent('app-component', function(rf: RenderFlags, ctx: any) { const AppComponent = createComponent('app-component', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
@ -1911,65 +1834,37 @@ describe('query', () => {
`Expected content query results to be available when ngAfterContentChecked was called.`); `Expected content query results to be available when ngAfterContentChecked was called.`);
}); });
it('should support content query matches on directive hosts', () => {
/**
* <div with-content #foo>
* </div>
*/
const AppComponent = createComponent('app-component', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
element(0, 'div', ['with-content', ''], ['foo', '']);
}
}, [WithContentDirective]);
const fixture = new ComponentFixture(AppComponent);
expect(withContentInstance !.foos.length)
.toBe(1, `Expected content query to match <div with-content #foo>.`);
});
it('should match shallow content queries in views inserted / removed by ngIf', () => {
function IfTemplate(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
element(0, 'div', null, ['foo', '']);
}
}
/**
* <shallow-comp>
* <div *ngIf="showing" #foo></div>
* </shallow-comp>
*/
const AppComponent = createComponent('app-component', function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
elementStart(0, 'shallow-comp');
{ container(1, IfTemplate, null, [AttributeMarker.SelectOnly, 'ngIf', '']); }
elementEnd();
}
if (rf & RenderFlags.Update) {
elementProperty(1, 'ngIf', bind(ctx.showing));
}
}, [ShallowComp, NgIf]);
const fixture = new ComponentFixture(AppComponent);
const qList = shallowCompInstance !.foos;
expect(qList.length).toBe(0);
fixture.component.showing = true;
fixture.update();
expect(qList.length).toBe(1);
fixture.component.showing = false;
fixture.update();
expect(qList.length).toBe(0);
});
// https://stackblitz.com/edit/angular-wlenwd?file=src%2Fapp%2Fapp.component.ts // https://stackblitz.com/edit/angular-wlenwd?file=src%2Fapp%2Fapp.component.ts
it('should support view and content queries matching the same element', () => { it('should support view and content queries matching the same element', () => {
let withContentComponentInstance: WithContentComponent;
class WithContentComponent {
// @ContentChildren('foo') foos;
// TODO(issue/24571): remove '!'.
foos !: QueryList<ElementRef>;
static ngComponentDef = defineComponent({
type: WithContentComponent,
selectors: [['with-content']],
factory: () => new WithContentComponent(),
contentQueries:
() => { registerContentQuery(query(null, ['foo'], true, QUERY_READ_FROM_NODE)); },
template: (rf: RenderFlags, ctx: WithContentComponent) => {
// intentionally left empty, don't need anything for this test
},
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
withContentComponentInstance = loadDirective<WithContentComponent>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(withContentComponentInstance.foos = tmp);
},
});
}
/** /**
* <div with-content> * <with-content>
* <div #foo></div> * <div #foo></div>
* </div> * </with-content>
* <div id="after" #bar></div> * <div id="after" #bar></div>
* class Cmpt { * class Cmpt {
* @ViewChildren('foo, bar') foos; * @ViewChildren('foo, bar') foos;
@ -1979,13 +1874,13 @@ describe('query', () => {
'app-component', 'app-component',
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
elementStart(1, 'div', ['with-content', '']); elementStart(1, 'with-content');
{ element(2, 'div', null, ['foo', '']); } { element(2, 'div', null, ['foo', '']); }
elementEnd(); elementEnd();
element(4, 'div', ['id', 'after'], ['bar', '']); element(4, 'div', ['id', 'after'], ['bar', '']);
} }
}, },
[WithContentDirective], [], [WithContentComponent], [],
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
query(0, ['foo', 'bar'], true, QUERY_READ_FROM_NODE); query(0, ['foo', 'bar'], true, QUERY_READ_FROM_NODE);
@ -2000,48 +1895,14 @@ describe('query', () => {
const viewQList = fixture.component.foos; const viewQList = fixture.component.foos;
expect(viewQList.length).toBe(2); expect(viewQList.length).toBe(2);
expect(withContentInstance !.foos.length).toBe(1); expect(withContentComponentInstance !.foos.length).toBe(1);
expect(viewQList.first.nativeElement).toBe(withContentInstance !.foos.first.nativeElement); expect(viewQList.first.nativeElement)
.toBe(withContentComponentInstance !.foos.first.nativeElement);
expect(viewQList.last.nativeElement.id).toBe('after'); expect(viewQList.last.nativeElement.id).toBe('after');
}); });
it('should not report deep content query matches found above content children', () => { it('should report results to appropriate queries where content queries are nested', () => {
/**
* <div with-content>
* <div #foo id="yes"></div> <-- should match content query
* </div>
* <div #foo></div> <-- should not match content query
* class AppComponent {
* @ViewChildren('bar') bars: QueryList<ElementRef>;
* }
*/
const AppComponent = createComponent(
'app-component',
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
elementStart(1, 'div', ['with-content', '']);
{ element(2, 'div', ['id', 'yes'], ['foo', '']); }
elementEnd();
element(4, 'div', null, ['foo', '']);
}
},
[WithContentDirective], [],
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
query(0, ['bar'], true, QUERY_READ_FROM_NODE);
}
if (rf & RenderFlags.Update) {
let tmp: any;
queryRefresh(tmp = load<QueryList<any>>(0)) && (ctx.bars = tmp as QueryList<any>);
}
});
const fixture = new ComponentFixture(AppComponent);
expect(withContentInstance !.foos.length).toBe(1);
expect(withContentInstance !.foos.first.nativeElement.id).toEqual('yes');
});
it('should report results to appropriate queries where deep content queries are nested', () => {
class QueryDirective { class QueryDirective {
fooBars: any; fooBars: any;
static ngDirectiveDef = defineDirective({ static ngDirectiveDef = defineDirective({
@ -2101,63 +1962,6 @@ describe('query', () => {
expect(inInstance !.fooBars.length).toBe(1); expect(inInstance !.fooBars.length).toBe(1);
}); });
it('should support nested shallow content queries ', () => {
let outInstance: QueryDirective;
let inInstance: QueryDirective;
class QueryDirective {
fooBars: any;
static ngDirectiveDef = defineDirective({
type: QueryDirective,
selectors: [['', 'query', '']],
exportAs: 'query',
factory: () => new QueryDirective(),
contentQueries: () => {
// @ContentChildren('foo, bar, baz', {descendants: true}) fooBars:
// QueryList<ElementRef>;
registerContentQuery(query(null, ['foo'], false, QUERY_READ_FROM_NODE));
},
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
let tmp: any;
const instance = loadDirective<QueryDirective>(dirIndex);
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
(instance.fooBars = tmp);
},
});
}
const AppComponent = createComponent(
'app-component',
/**
* <div query #out="query">
* <div query #in="query" #foo>
* <span #foo></span>
* </div>
* </div>
*/
function(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
elementStart(0, 'div', ['query', ''], ['out', 'query']);
{
elementStart(2, 'div', ['query', ''], ['in', 'query', 'foo', '']);
{ element(5, 'span', ['id', 'bar'], ['foo', '']); }
elementEnd();
}
elementEnd();
}
if (rf & RenderFlags.Update) {
outInstance = load<QueryDirective>(1);
inInstance = load<QueryDirective>(3);
}
},
[QueryDirective]);
const fixture = new ComponentFixture(AppComponent);
expect(outInstance !.fooBars.length).toBe(1);
expect(inInstance !.fooBars.length).toBe(2);
});
it('should respect shallow flag on content queries when mixing deep and shallow queries', it('should respect shallow flag on content queries when mixing deep and shallow queries',
() => { () => {
class ShallowQueryDirective { class ShallowQueryDirective {
@ -2208,9 +2012,6 @@ describe('query', () => {
/** /**
* <div shallow-query #shallow="shallow-query" deep-query #deep="deep-query"> * <div shallow-query #shallow="shallow-query" deep-query #deep="deep-query">
* <span #foo></span> * <span #foo></span>
* <div>
* <span #foo></span>
* </div>
* </div> * </div>
*/ */
function(rf: RenderFlags, ctx: any) { function(rf: RenderFlags, ctx: any) {
@ -2218,12 +2019,7 @@ describe('query', () => {
elementStart( elementStart(
0, 'div', [AttributeMarker.SelectOnly, 'shallow-query', 'deep-query'], 0, 'div', [AttributeMarker.SelectOnly, 'shallow-query', 'deep-query'],
['shallow', 'shallow-query', 'deep', 'deep-query']); ['shallow', 'shallow-query', 'deep', 'deep-query']);
{ { element(3, 'span', ['id', 'foo'], ['foo', '']); }
element(3, 'span', null, ['foo', '']);
elementStart(5, 'div');
{ element(6, 'span', null, ['foo', '']); }
elementEnd();
}
elementEnd(); elementEnd();
} }
if (rf & RenderFlags.Update) { if (rf & RenderFlags.Update) {
@ -2235,7 +2031,7 @@ describe('query', () => {
const fixture = new ComponentFixture(AppComponent); const fixture = new ComponentFixture(AppComponent);
expect(shallowInstance !.foos.length).toBe(1); expect(shallowInstance !.foos.length).toBe(1);
expect(deepInstance !.foos.length).toBe(2); expect(deepInstance !.foos.length).toBe(1);
}); });
}); });
}); });

View File

@ -13,7 +13,7 @@ import {CreateComponentOptions} from '../../src/render3/component';
import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition'; import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition';
import {ComponentTemplate, ComponentType, DirectiveDefInternal, DirectiveType, PublicFeature, RenderFlags, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index'; import {ComponentTemplate, ComponentType, DirectiveDefInternal, DirectiveType, PublicFeature, RenderFlags, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index';
import {NG_HOST_SYMBOL, renderTemplate} from '../../src/render3/instructions'; import {NG_HOST_SYMBOL, renderTemplate} from '../../src/render3/instructions';
import {DirectiveDefList, DirectiveTypesOrFactory, PipeDefInternal, PipeDefList, PipeTypesOrFactory} from '../../src/render3/interfaces/definition'; import {DirectiveDefList, DirectiveDefListOrFactory, DirectiveTypesOrFactory, PipeDefInternal, PipeDefList, PipeDefListOrFactory, PipeTypesOrFactory} from '../../src/render3/interfaces/definition';
import {LElementNode} from '../../src/render3/interfaces/node'; import {LElementNode} from '../../src/render3/interfaces/node';
import {RElement, RText, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; import {RElement, RText, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer';
import {Sanitizer} from '../../src/sanitization/security'; import {Sanitizer} from '../../src/sanitization/security';

View File

@ -158,6 +158,7 @@ describe('animation renderer factory', () => {
} }
}, },
factory: () => new SomeComponentWithAnimation, factory: () => new SomeComponentWithAnimation,
rendererType: createRendererType2({
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
styles: [], styles: [],
data: { data: {
@ -173,7 +174,8 @@ describe('animation renderer factory', () => {
}], }],
options: {} options: {}
}] }]
}, }
}),
}); });
} }

View File

@ -163,7 +163,7 @@ describe('ViewContainerRef', () => {
it('should work on containers', () => { it('should work on containers', () => {
function createTemplate() { function createTemplate() {
container(0, embeddedTemplate, null, ['vcref', '']); container(0, embeddedTemplate, undefined, ['vcref', '']);
element(1, 'footer'); element(1, 'footer');
} }
@ -246,8 +246,8 @@ describe('ViewContainerRef', () => {
template: (rf: RenderFlags, cmp: TestComponent) => { template: (rf: RenderFlags, cmp: TestComponent) => {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
text(0, 'before|'); text(0, 'before|');
container(1, EmbeddedTemplateA, null, ['testdir', '']); container(1, EmbeddedTemplateA, undefined, ['testdir', '']);
container(2, EmbeddedTemplateB, null, ['testdir', '']); container(2, EmbeddedTemplateB, undefined, ['testdir', '']);
text(3, '|after'); text(3, '|after');
} }
}, },
@ -315,7 +315,7 @@ describe('ViewContainerRef', () => {
template: (rf: RenderFlags, cmp: TestComponent) => { template: (rf: RenderFlags, cmp: TestComponent) => {
if (rf & RenderFlags.Create) { if (rf & RenderFlags.Create) {
text(0, 'before|'); text(0, 'before|');
container(1, EmbeddedTemplateA, null, ['testdir', '']); container(1, EmbeddedTemplateA, undefined, ['testdir', '']);
container(2); container(2);
text(3, '|after'); text(3, '|after');
} }
@ -1074,7 +1074,7 @@ describe('ViewContainerRef', () => {
it('should work on containers', () => { it('should work on containers', () => {
function createTemplate() { function createTemplate() {
container(0, embeddedTemplate, null, ['vcref', '']); container(0, embeddedTemplate, undefined, ['vcref', '']);
element(1, 'footer'); element(1, 'footer');
} }

View File

@ -1,149 +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, Inject, InjectionToken, NgModule, Optional} from '@angular/core';
import {TestBed, getTestBed} from '@angular/core/testing/src/test_bed';
import {By} from '@angular/platform-browser';
import {expect} from '@angular/platform-browser/testing/src/matchers';
const NAME = new InjectionToken<string>('name');
// -- module: HWModule
@Component({
selector: 'hello-world',
template: '<greeting-cmp></greeting-cmp>',
})
export class HelloWorld {
}
// -- module: Greeting
@Component({
selector: 'greeting-cmp',
template: 'Hello {{ name }}',
})
export class GreetingCmp {
name: string;
constructor(@Inject(NAME) @Optional() name: string) { this.name = name || 'nobody!'; }
}
@NgModule({
declarations: [GreetingCmp],
exports: [GreetingCmp],
})
export class GreetingModule {
}
@Component({selector: 'simple-cmp', template: '<b>simple</b>'})
export class SimpleCmp {
}
@NgModule({
declarations: [HelloWorld, SimpleCmp],
imports: [GreetingModule],
providers: [
{provide: NAME, useValue: 'World!'},
]
})
export class HelloWorldModule {
}
describe('TestBed', () => {
beforeEach(() => {
getTestBed().resetTestingModule();
TestBed.configureTestingModule({imports: [HelloWorldModule]});
});
it('should compile and render a component', () => {
const hello = TestBed.createComponent(HelloWorld);
hello.detectChanges();
expect(hello.nativeElement).toHaveText('Hello World!');
});
it('should give access to the component instance', () => {
const hello = TestBed.createComponent(HelloWorld);
hello.detectChanges();
expect(hello.componentInstance).toBeAnInstanceOf(HelloWorld);
});
it('should give the ability to query by css', () => {
const hello = TestBed.createComponent(HelloWorld);
hello.detectChanges();
const greetingByCss = hello.debugElement.query(By.css('greeting-cmp'));
expect(greetingByCss.nativeElement).toHaveText('Hello World!');
expect(greetingByCss.componentInstance).toBeAnInstanceOf(GreetingCmp);
});
it('should give the ability to trigger the change detection', () => {
const hello = TestBed.createComponent(HelloWorld);
hello.detectChanges();
const greetingByCss = hello.debugElement.query(By.css('greeting-cmp'));
expect(greetingByCss.nativeElement).toHaveText('Hello World!');
greetingByCss.componentInstance.name = 'TestBed!';
hello.detectChanges();
expect(greetingByCss.nativeElement).toHaveText('Hello TestBed!');
});
it('should give access to the node injector', () => {
const hello = TestBed.createComponent(HelloWorld);
hello.detectChanges();
const injector = hello.debugElement.query(By.css('greeting-cmp')).injector;
// from the node injector
const helloInjected = injector.get(HelloWorld);
expect(helloInjected).toBe(hello.componentInstance);
// from the module injector
const nameInjected = injector.get(NAME);
expect(nameInjected).toEqual('World!');
});
it('should give the ability to query by directive', () => {
const hello = TestBed.createComponent(HelloWorld);
hello.detectChanges();
const greetingByDirective = hello.debugElement.query(By.directive(GreetingCmp));
expect(greetingByDirective.componentInstance).toBeAnInstanceOf(GreetingCmp);
});
it('allow to override a template', () => {
// use original template when there is no override
let hello = TestBed.createComponent(HelloWorld);
hello.detectChanges();
expect(hello.nativeElement).toHaveText('Hello World!');
// override the template
getTestBed().resetTestingModule();
TestBed.configureTestingModule({imports: [HelloWorldModule]});
TestBed.overrideComponent(GreetingCmp, {set: {template: `Bonjour {{ name }}`}});
hello = TestBed.createComponent(HelloWorld);
hello.detectChanges();
expect(hello.nativeElement).toHaveText('Bonjour World!');
// restore the original template by calling `.resetTestingModule()`
getTestBed().resetTestingModule();
TestBed.configureTestingModule({imports: [HelloWorldModule]});
hello = TestBed.createComponent(HelloWorld);
hello.detectChanges();
expect(hello.nativeElement).toHaveText('Hello World!');
});
it('allow to override a provider', () => {
TestBed.overrideProvider(NAME, {useValue: 'injected World !'});
const hello = TestBed.createComponent(HelloWorld);
hello.detectChanges();
expect(hello.nativeElement).toHaveText('Hello injected World !');
});
});

View File

@ -11,7 +11,6 @@ const sourcemaps = require('rollup-plugin-sourcemaps');
const globals = { const globals = {
'@angular/core': 'ng.core', '@angular/core': 'ng.core',
'@angular/compiler': 'ng.compiler',
'rxjs': 'rxjs', 'rxjs': 'rxjs',
}; };

View File

@ -1,131 +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 {ɵstringify as stringify} from '@angular/core';
import {MetadataOverride} from './metadata_override';
type StringMap = {
[key: string]: any
};
let _nextReferenceId = 0;
export class MetadataOverrider {
private _references = new Map<any, string>();
/**
* Creates a new instance for the given metadata class
* based on an old instance and overrides.
*/
overrideMetadata<C extends T, T>(
metadataClass: {new (options: T): C;}, oldMetadata: C, override: MetadataOverride<T>): C {
const props: StringMap = {};
if (oldMetadata) {
_valueProps(oldMetadata).forEach((prop) => props[prop] = (<any>oldMetadata)[prop]);
}
if (override.set) {
if (override.remove || override.add) {
throw new Error(`Cannot set and add/remove ${stringify(metadataClass)} at the same time!`);
}
setMetadata(props, override.set);
}
if (override.remove) {
removeMetadata(props, override.remove, this._references);
}
if (override.add) {
addMetadata(props, override.add);
}
return new metadataClass(<any>props);
}
}
function removeMetadata(metadata: StringMap, remove: any, references: Map<any, string>) {
const removeObjects = new Set<string>();
for (const prop in remove) {
const removeValue = remove[prop];
if (removeValue instanceof Array) {
removeValue.forEach(
(value: any) => { removeObjects.add(_propHashKey(prop, value, references)); });
} else {
removeObjects.add(_propHashKey(prop, removeValue, references));
}
}
for (const prop in metadata) {
const propValue = metadata[prop];
if (propValue instanceof Array) {
metadata[prop] = propValue.filter(
(value: any) => !removeObjects.has(_propHashKey(prop, value, references)));
} else {
if (removeObjects.has(_propHashKey(prop, propValue, references))) {
metadata[prop] = undefined;
}
}
}
}
function addMetadata(metadata: StringMap, add: any) {
for (const prop in add) {
const addValue = add[prop];
const propValue = metadata[prop];
if (propValue != null && propValue instanceof Array) {
metadata[prop] = propValue.concat(addValue);
} else {
metadata[prop] = addValue;
}
}
}
function setMetadata(metadata: StringMap, set: any) {
for (const prop in set) {
metadata[prop] = set[prop];
}
}
function _propHashKey(propName: any, propValue: any, references: Map<any, string>): string {
const replacer = (key: any, value: any) => {
if (typeof value === 'function') {
value = _serializeReference(value, references);
}
return value;
};
return `${propName}:${JSON.stringify(propValue, replacer)}`;
}
function _serializeReference(ref: any, references: Map<any, string>): string {
let id = references.get(ref);
if (!id) {
id = `${stringify(ref)}${_nextReferenceId++}`;
references.set(ref, id);
}
return id;
}
function _valueProps(obj: any): string[] {
const props: string[] = [];
// regular public props
Object.keys(obj).forEach((prop) => {
if (!prop.startsWith('_')) {
props.push(prop);
}
});
// getters
let proto = obj;
while (proto = Object.getPrototypeOf(proto)) {
Object.keys(proto).forEach((protoProp) => {
const desc = Object.getOwnPropertyDescriptor(proto, protoProp);
if (!protoProp.startsWith('_') && desc && 'get' in desc) {
props.push(protoProp);
}
});
}
return props;
}

View File

@ -1,602 +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, Injector, NgModule, Pipe, PlatformRef, Provider, RendererFactory2, SchemaMetadata, Type, ɵNgModuleDefInternal as NgModuleDefInternal, ɵNgModuleTransitiveScopes as NgModuleTransitiveScopes, ɵRender3ComponentFactory as ComponentFactory, ɵRender3DebugRendererFactory2 as Render3DebugRendererFactory2, ɵRender3NgModuleRef as NgModuleRef, ɵWRAP_RENDERER_FACTORY2 as WRAP_RENDERER_FACTORY2, ɵcompileComponent as compileComponent, ɵcompileDirective as compileDirective, ɵcompileNgModuleDefs as compileNgModuleDefs, ɵcompilePipe as compilePipe, ɵpatchComponentDefWithScope as patchComponentDefWithScope, ɵstringify as stringify} from '@angular/core';
import {ComponentFixture} from './component_fixture';
import {MetadataOverride} from './metadata_override';
import {ComponentResolver, DirectiveResolver, NgModuleResolver, PipeResolver, Resolver} from './resolvers';
import {TestBed} from './test_bed';
import {ComponentFixtureAutoDetect, TestBedStatic, TestComponentRenderer, TestModuleMetadata} from './test_bed_common';
let _nextRootElementId = 0;
/**
* @description
* Configures and initializes environment for unit testing and provides methods for
* creating components and services in unit tests.
*
* TestBed is the primary api for writing unit tests for Angular applications and libraries.
*
* Note: Use `TestBed` in tests. It will be set to either `TestBedViewEngine` or `TestBedRender3`
* according to the compiler used.
*/
export class TestBedRender3 implements Injector, TestBed {
/**
* Initialize the environment for testing with a compiler factory, a PlatformRef, and an
* angular module. These are common to every test in the suite.
*
* This may only be called once, to set up the common providers for the current test
* suite on the current platform. If you absolutely need to change the providers,
* first use `resetTestEnvironment`.
*
* Test modules and platforms for individual platforms are available from
* '@angular/<platform_name>/testing'.
*
* @experimental
*/
static initTestEnvironment(
ngModule: Type<any>|Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): TestBed {
const testBed = _getTestBedRender3();
testBed.initTestEnvironment(ngModule, platform, aotSummaries);
return testBed;
}
/**
* Reset the providers for the test injector.
*
* @experimental
*/
static resetTestEnvironment(): void { _getTestBedRender3().resetTestEnvironment(); }
static configureCompiler(config: {providers?: any[]; useJit?: boolean;}): TestBedStatic {
_getTestBedRender3().configureCompiler(config);
return TestBedRender3 as any as TestBedStatic;
}
/**
* Allows overriding default providers, directives, pipes, modules of the test injector,
* which are defined in test_injector.js
*/
static configureTestingModule(moduleDef: TestModuleMetadata): TestBedStatic {
_getTestBedRender3().configureTestingModule(moduleDef);
return TestBedRender3 as any as TestBedStatic;
}
/**
* Compile components with a `templateUrl` for the test's NgModule.
* It is necessary to call this function
* as fetching urls is asynchronous.
*/
static compileComponents(): Promise<any> { return _getTestBedRender3().compileComponents(); }
static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): TestBedStatic {
_getTestBedRender3().overrideModule(ngModule, override);
return TestBedRender3 as any as TestBedStatic;
}
static overrideComponent(component: Type<any>, override: MetadataOverride<Component>):
TestBedStatic {
_getTestBedRender3().overrideComponent(component, override);
return TestBedRender3 as any as TestBedStatic;
}
static overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>):
TestBedStatic {
_getTestBedRender3().overrideDirective(directive, override);
return TestBedRender3 as any as TestBedStatic;
}
static overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): TestBedStatic {
_getTestBedRender3().overridePipe(pipe, override);
return TestBedRender3 as any as TestBedStatic;
}
static overrideTemplate(component: Type<any>, template: string): TestBedStatic {
_getTestBedRender3().overrideComponent(component, {set: {template, templateUrl: null !}});
return TestBedRender3 as any as TestBedStatic;
}
/**
* Overrides the template of the given component, compiling the template
* in the context of the TestingModule.
*
* Note: This works for JIT and AOTed components as well.
*/
static overrideTemplateUsingTestingModule(component: Type<any>, template: string): TestBedStatic {
_getTestBedRender3().overrideTemplateUsingTestingModule(component, template);
return TestBedRender3 as any as TestBedStatic;
}
overrideTemplateUsingTestingModule(component: Type<any>, template: string): void {
throw new Error('Render3TestBed.overrideTemplateUsingTestingModule is not implemented yet');
}
static overrideProvider(token: any, provider: {
useFactory: Function,
deps: any[],
}): TestBedStatic;
static overrideProvider(token: any, provider: {useValue: any;}): TestBedStatic;
static overrideProvider(token: any, provider: {
useFactory?: Function,
useValue?: any,
deps?: any[],
}): TestBedStatic {
_getTestBedRender3().overrideProvider(token, provider);
return TestBedRender3 as any as TestBedStatic;
}
/**
* Overwrites all providers for the given token with the given provider definition.
*
* @deprecated as it makes all NgModules lazy. Introduced only for migrating off of it.
*/
static deprecatedOverrideProvider(token: any, provider: {
useFactory: Function,
deps: any[],
}): void;
static deprecatedOverrideProvider(token: any, provider: {useValue: any;}): void;
static deprecatedOverrideProvider(token: any, provider: {
useFactory?: Function,
useValue?: any,
deps?: any[],
}): TestBedStatic {
throw new Error('Render3TestBed.deprecatedOverrideProvider is not implemented');
}
static get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
return _getTestBedRender3().get(token, notFoundValue);
}
static createComponent<T>(component: Type<T>): ComponentFixture<T> {
return _getTestBedRender3().createComponent(component);
}
static resetTestingModule(): TestBedStatic {
_getTestBedRender3().resetTestingModule();
return TestBedRender3 as any as TestBedStatic;
}
// Properties
platform: PlatformRef = null !;
ngModule: Type<any>|Type<any>[] = null !;
// metadata overrides
private _moduleOverrides: [Type<any>, MetadataOverride<NgModule>][] = [];
private _componentOverrides: [Type<any>, MetadataOverride<Component>][] = [];
private _directiveOverrides: [Type<any>, MetadataOverride<Directive>][] = [];
private _pipeOverrides: [Type<any>, MetadataOverride<Pipe>][] = [];
private _providerOverrides: Provider[] = [];
private _rootProviderOverrides: Provider[] = [];
// test module configuration
private _providers: Provider[] = [];
private _declarations: Array<Type<any>|any[]|any> = [];
private _imports: Array<Type<any>|any[]|any> = [];
private _schemas: Array<SchemaMetadata|any[]> = [];
private _activeFixtures: ComponentFixture<any>[] = [];
private _moduleRef: NgModuleRef<any> = null !;
private _instantiated: boolean = false;
/**
* Initialize the environment for testing with a compiler factory, a PlatformRef, and an
* angular module. These are common to every test in the suite.
*
* This may only be called once, to set up the common providers for the current test
* suite on the current platform. If you absolutely need to change the providers,
* first use `resetTestEnvironment`.
*
* Test modules and platforms for individual platforms are available from
* '@angular/<platform_name>/testing'.
*
* @experimental
*/
initTestEnvironment(
ngModule: Type<any>|Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): void {
if (this.platform || this.ngModule) {
throw new Error('Cannot set base providers because it has already been called');
}
this.platform = platform;
this.ngModule = ngModule;
}
/**
* Reset the providers for the test injector.
*
* @experimental
*/
resetTestEnvironment(): void {
this.resetTestingModule();
this.platform = null !;
this.ngModule = null !;
}
resetTestingModule(): void {
// reset metadata overrides
this._moduleOverrides = [];
this._componentOverrides = [];
this._directiveOverrides = [];
this._pipeOverrides = [];
this._providerOverrides = [];
this._rootProviderOverrides = [];
// reset test module config
this._providers = [];
this._declarations = [];
this._imports = [];
this._schemas = [];
this._moduleRef = null !;
this._instantiated = false;
this._activeFixtures.forEach((fixture) => {
try {
fixture.destroy();
} catch (e) {
console.error('Error during cleanup of component', {
component: fixture.componentInstance,
stacktrace: e,
});
}
});
this._activeFixtures = [];
}
configureCompiler(config: {providers?: any[]; useJit?: boolean;}): void {
throw new Error('the Render3 compiler is not configurable !');
}
configureTestingModule(moduleDef: TestModuleMetadata): void {
this._assertNotInstantiated('R3TestBed.configureTestingModule', 'configure the test module');
if (moduleDef.providers) {
this._providers.push(...moduleDef.providers);
}
if (moduleDef.declarations) {
this._declarations.push(...moduleDef.declarations);
}
if (moduleDef.imports) {
this._imports.push(...moduleDef.imports);
}
if (moduleDef.schemas) {
this._schemas.push(...moduleDef.schemas);
}
}
// TODO(vicb): implement
compileComponents(): Promise<any> {
throw new Error('Render3TestBed.compileComponents is not implemented yet');
}
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
this._initIfNeeded();
if (token === TestBedRender3) {
return this;
}
return this._moduleRef.injector.get(token, notFoundValue);
}
execute(tokens: any[], fn: Function, context?: any): any {
this._initIfNeeded();
const params = tokens.map(t => this.get(t));
return fn.apply(context, params);
}
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void {
this._assertNotInstantiated('overrideModule', 'override module metadata');
this._moduleOverrides.push([ngModule, override]);
}
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void {
this._assertNotInstantiated('overrideComponent', 'override component metadata');
this._componentOverrides.push([component, override]);
}
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void {
this._assertNotInstantiated('overrideDirective', 'override directive metadata');
this._directiveOverrides.push([directive, override]);
}
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void {
this._assertNotInstantiated('overridePipe', 'override pipe metadata');
this._pipeOverrides.push([pipe, override]);
}
/**
* Overwrites all providers for the given token with the given provider definition.
*/
overrideProvider(token: any, provider: {useFactory?: Function, useValue?: any, deps?: any[]}):
void {
const isRoot =
(typeof token !== 'string' && token.ngInjectableDef &&
token.ngInjectableDef.providedIn === 'root');
const overrides = isRoot ? this._rootProviderOverrides : this._providerOverrides;
if (provider.useFactory) {
overrides.push({provide: token, useFactory: provider.useFactory, deps: provider.deps || []});
} else {
overrides.push({provide: token, useValue: provider.useValue});
}
}
/**
* Overwrites all providers for the given token with the given provider definition.
*
* @deprecated as it makes all NgModules lazy. Introduced only for migrating off of it.
*/
deprecatedOverrideProvider(token: any, provider: {
useFactory: Function,
deps: any[],
}): void;
deprecatedOverrideProvider(token: any, provider: {useValue: any;}): void;
deprecatedOverrideProvider(
token: any, provider: {useFactory?: Function, useValue?: any, deps?: any[]}): void {
throw new Error('No implemented in IVY');
}
createComponent<T>(type: Type<T>): ComponentFixture<T> {
this._initIfNeeded();
const testComponentRenderer: TestComponentRenderer = this.get(TestComponentRenderer);
const rootElId = `root${_nextRootElementId++}`;
testComponentRenderer.insertRootElement(rootElId);
const componentDef = (type as any).ngComponentDef;
if (!componentDef) {
throw new Error(
`It looks like '${stringify(type)}' has not been IVY compiled - it has no 'ngComponentDef' field`);
}
const componentFactory = new ComponentFactory(componentDef);
const componentRef =
componentFactory.create(Injector.NULL, [], `#${rootElId}`, this._moduleRef);
const autoDetect: boolean = this.get(ComponentFixtureAutoDetect, false);
const fixture = new ComponentFixture<any>(componentRef, null, autoDetect);
this._activeFixtures.push(fixture);
return fixture;
}
// internal methods
private _initIfNeeded(): void {
if (this._instantiated) {
return;
}
const resolvers = this._getResolvers();
const testModuleType = this._createTestModule();
compileNgModule(testModuleType, resolvers);
const parentInjector = this.platform.injector;
this._moduleRef = new NgModuleRef(testModuleType, parentInjector);
this._instantiated = true;
}
// creates resolvers taking overrides into account
private _getResolvers() {
const module = new NgModuleResolver();
module.setOverrides(this._moduleOverrides);
const component = new ComponentResolver();
component.setOverrides(this._componentOverrides);
const directive = new DirectiveResolver();
directive.setOverrides(this._directiveOverrides);
const pipe = new PipeResolver();
pipe.setOverrides(this._pipeOverrides);
return {module, component, directive, pipe};
}
private _assertNotInstantiated(methodName: string, methodDescription: string) {
if (this._instantiated) {
throw new Error(
`Cannot ${methodDescription} when the test module has already been instantiated. ` +
`Make sure you are not using \`inject\` before \`${methodName}\`.`);
}
}
private _createTestModule(): Type<any> {
const rootProviderOverrides = this._rootProviderOverrides;
const rendererFactoryWrapper = {
provide: WRAP_RENDERER_FACTORY2,
useFactory: () => (rf: RendererFactory2) => new Render3DebugRendererFactory2(rf),
};
@NgModule({
providers: [...rootProviderOverrides, rendererFactoryWrapper],
jit: true,
})
class RootScopeModule {
}
const providers = [...this._providers, ...this._providerOverrides];
const declarations = this._declarations;
const imports = [RootScopeModule, this.ngModule, this._imports];
const schemas = this._schemas;
@NgModule({providers, declarations, imports, schemas, jit: true})
class DynamicTestModule {
}
return DynamicTestModule;
}
}
let testBed: TestBedRender3;
export function _getTestBedRender3(): TestBedRender3 {
return testBed = testBed || new TestBedRender3();
}
// Module compiler
const EMPTY_ARRAY: Type<any>[] = [];
// Resolvers for Angular decorators
type Resolvers = {
module: Resolver<NgModule>,
component: Resolver<Directive>,
directive: Resolver<Component>,
pipe: Resolver<Pipe>,
};
function compileNgModule(moduleType: Type<any>, resolvers: Resolvers): void {
const ngModule = resolvers.module.resolve(moduleType);
if (ngModule === null) {
throw new Error(`${stringify(moduleType)} has not @NgModule annotation`);
}
compileNgModuleDefs(moduleType, ngModule);
const declarations: Type<any>[] = flatten(ngModule.declarations || EMPTY_ARRAY);
const compiledComponents: Type<any>[] = [];
// Compile the components, directives and pipes declared by this module
declarations.forEach(declaration => {
const component = resolvers.component.resolve(declaration);
if (component) {
compileComponent(declaration, component);
compiledComponents.push(declaration);
return;
}
const directive = resolvers.directive.resolve(declaration);
if (directive) {
compileDirective(declaration, directive);
return;
}
const pipe = resolvers.pipe.resolve(declaration);
if (pipe) {
compilePipe(declaration, pipe);
return;
}
});
// Compile transitive modules, components, directives and pipes
const transitiveScope = transitiveScopesFor(moduleType, resolvers);
compiledComponents.forEach(
cmp => patchComponentDefWithScope((cmp as any).ngComponentDef, transitiveScope));
}
/**
* Compute the pair of transitive scopes (compilation scope and exported scope) for a given module.
*
* This operation is memoized and the result is cached on the module's definition. It can be called
* on modules with components that have not fully compiled yet, but the result should not be used
* until they have.
*/
function transitiveScopesFor<T>(
moduleType: Type<T>, resolvers: Resolvers): NgModuleTransitiveScopes {
if (!isNgModule(moduleType)) {
throw new Error(`${moduleType.name} does not have an ngModuleDef`);
}
const def = moduleType.ngModuleDef;
if (def.transitiveCompileScopes !== null) {
return def.transitiveCompileScopes;
}
const scopes: NgModuleTransitiveScopes = {
compilation: {
directives: new Set<any>(),
pipes: new Set<any>(),
},
exported: {
directives: new Set<any>(),
pipes: new Set<any>(),
},
};
def.declarations.forEach(declared => {
const declaredWithDefs = declared as Type<any>& { ngPipeDef?: any; };
if (declaredWithDefs.ngPipeDef !== undefined) {
scopes.compilation.pipes.add(declared);
} else {
scopes.compilation.directives.add(declared);
}
});
def.imports.forEach(<I>(imported: Type<I>) => {
const ngModule = resolvers.module.resolve(imported);
if (ngModule === null) {
throw new Error(`Importing ${imported.name} which does not have an @ngModule`);
} else {
compileNgModule(imported, resolvers);
}
// When this module imports another, the imported module's exported directives and pipes are
// added to the compilation scope of this module.
const importedScope = transitiveScopesFor(imported, resolvers);
importedScope.exported.directives.forEach(entry => scopes.compilation.directives.add(entry));
importedScope.exported.pipes.forEach(entry => scopes.compilation.pipes.add(entry));
});
def.exports.forEach(<E>(exported: Type<E>) => {
const exportedTyped = exported as Type<E>& {
// Components, Directives, NgModules, and Pipes can all be exported.
ngComponentDef?: any;
ngDirectiveDef?: any;
ngModuleDef?: NgModuleDefInternal<E>;
ngPipeDef?: any;
};
// Either the type is a module, a pipe, or a component/directive (which may not have an
// ngComponentDef as it might be compiled asynchronously).
if (isNgModule(exportedTyped)) {
// When this module exports another, the exported module's exported directives and pipes are
// added to both the compilation and exported scopes of this module.
const exportedScope = transitiveScopesFor(exportedTyped, resolvers);
exportedScope.exported.directives.forEach(entry => {
scopes.compilation.directives.add(entry);
scopes.exported.directives.add(entry);
});
exportedScope.exported.pipes.forEach(entry => {
scopes.compilation.pipes.add(entry);
scopes.exported.pipes.add(entry);
});
} else if (exportedTyped.ngPipeDef !== undefined) {
scopes.exported.pipes.add(exportedTyped);
} else {
scopes.exported.directives.add(exportedTyped);
}
});
def.transitiveCompileScopes = scopes;
return scopes;
}
function flatten<T>(values: any[]): T[] {
const out: T[] = [];
values.forEach(value => {
if (Array.isArray(value)) {
out.push(...flatten<T>(value));
} else {
out.push(value);
}
});
return out;
}
function isNgModule<T>(value: Type<T>): value is Type<T>&{ngModuleDef: NgModuleDefInternal<T>} {
return (value as{ngModuleDef?: NgModuleDefInternal<T>}).ngModuleDef !== undefined;
}

View File

@ -1,73 +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, NgModule, Pipe, Type, ɵReflectionCapabilities as ReflectionCapabilities} from '@angular/core';
import {MetadataOverride} from './metadata_override';
import {MetadataOverrider} from './metadata_overrider';
const reflection = new ReflectionCapabilities();
/**
* Base interface to resolve `@Component`, `@Directive`, `@Pipe` and `@NgModule`.
*/
export interface Resolver<T> { resolve(type: Type<any>): T|null; }
/**
* Allows to override ivy metadata for tests (via the `TestBed`).
*/
abstract class OverrideResolver<T> implements Resolver<T> {
private overrides = new Map<Type<any>, MetadataOverride<T>>();
private resolved = new Map<Type<any>, T|null>();
abstract get type(): any;
setOverrides(overrides: Array<[Type<any>, MetadataOverride<T>]>) {
this.overrides.clear();
overrides.forEach(([type, override]) => this.overrides.set(type, override));
}
getAnnotation(type: Type<any>): T|null {
return reflection.annotations(type).find(a => a instanceof this.type) || null;
}
resolve(type: Type<any>): T|null {
let resolved = this.resolved.get(type) || null;
if (!resolved) {
resolved = this.getAnnotation(type);
if (resolved) {
const override = this.overrides.get(type);
if (override) {
const overrider = new MetadataOverrider();
resolved = overrider.overrideMetadata(this.type, resolved, override);
}
}
this.resolved.set(type, resolved);
}
return resolved;
}
}
export class DirectiveResolver extends OverrideResolver<Directive> {
get type() { return Directive; }
}
export class ComponentResolver extends OverrideResolver<Component> {
get type() { return Component; }
}
export class PipeResolver extends OverrideResolver<Pipe> {
get type() { return Pipe; }
}
export class NgModuleResolver extends OverrideResolver<NgModule> {
get type() { return NgModule; }
}

View File

@ -6,109 +6,58 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ApplicationInitStatus, CompilerOptions, Component, Directive, Injector, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, StaticProvider, Type, ɵAPP_ROOT as APP_ROOT, ɵDepFlags as DepFlags, ɵNodeFlags as NodeFlags, ɵclearOverrides as clearOverrides, ɵgetComponentViewDefinitionFactory as getComponentViewDefinitionFactory, ɵivyEnabled as ivyEnabled, ɵoverrideComponentView as overrideComponentView, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core'; import {ApplicationInitStatus, CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, StaticProvider, Type, ɵAPP_ROOT as APP_ROOT, ɵDepFlags as DepFlags, ɵNodeFlags as NodeFlags, ɵclearOverrides as clearOverrides, ɵgetComponentViewDefinitionFactory as getComponentViewDefinitionFactory, ɵoverrideComponentView as overrideComponentView, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core';
import {AsyncTestCompleter} from './async_test_completer'; import {AsyncTestCompleter} from './async_test_completer';
import {ComponentFixture} from './component_fixture'; import {ComponentFixture} from './component_fixture';
import {MetadataOverride} from './metadata_override'; import {MetadataOverride} from './metadata_override';
import {TestBedRender3, _getTestBedRender3} from './r3_test_bed';
import {ComponentFixtureAutoDetect, ComponentFixtureNoNgZone, TestBedStatic, TestComponentRenderer, TestModuleMetadata} from './test_bed_common';
import {TestingCompiler, TestingCompilerFactory} from './test_compiler'; import {TestingCompiler, TestingCompilerFactory} from './test_compiler';
const UNDEFINED = new Object(); const UNDEFINED = new Object();
/**
* An abstract class for inserting the root test component element in a platform independent way.
*
* @experimental
*/
export class TestComponentRenderer {
insertRootElement(rootElementId: string) {}
}
let _nextRootElementId = 0; let _nextRootElementId = 0;
export interface TestBed {
platform: PlatformRef;
ngModule: Type<any>|Type<any>[];
/** /**
* Initialize the environment for testing with a compiler factory, a PlatformRef, and an
* angular module. These are common to every test in the suite.
*
* This may only be called once, to set up the common providers for the current test
* suite on the current platform. If you absolutely need to change the providers,
* first use `resetTestEnvironment`.
*
* Test modules and platforms for individual platforms are available from
* '@angular/<platform_name>/testing'.
*
* @experimental * @experimental
*/ */
initTestEnvironment( export const ComponentFixtureAutoDetect =
ngModule: Type<any>|Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): void; new InjectionToken<boolean[]>('ComponentFixtureAutoDetect');
/** /**
* Reset the providers for the test injector.
*
* @experimental * @experimental
*/ */
resetTestEnvironment(): void; export const ComponentFixtureNoNgZone = new InjectionToken<boolean[]>('ComponentFixtureNoNgZone');
resetTestingModule(): void;
configureCompiler(config: {providers?: any[], useJit?: boolean}): void;
configureTestingModule(moduleDef: TestModuleMetadata): void;
compileComponents(): Promise<any>;
get(token: any, notFoundValue?: any): any;
execute(tokens: any[], fn: Function, context?: any): any;
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void;
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void;
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void;
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void;
/** /**
* Overwrites all providers for the given token with the given provider definition. * @experimental
*/ */
overrideProvider(token: any, provider: { export type TestModuleMetadata = {
useFactory: Function, providers?: any[],
deps: any[], declarations?: any[],
}): void; imports?: any[],
overrideProvider(token: any, provider: {useValue: any;}): void; schemas?: Array<SchemaMetadata|any[]>,
overrideProvider(token: any, provider: {useFactory?: Function, useValue?: any, deps?: any[]}): aotSummaries?: () => any[],
void; };
/**
* Overwrites all providers for the given token with the given provider definition.
*
* @deprecated as it makes all NgModules lazy. Introduced only for migrating off of it.
*/
deprecatedOverrideProvider(token: any, provider: {
useFactory: Function,
deps: any[],
}): void;
deprecatedOverrideProvider(token: any, provider: {useValue: any;}): void;
deprecatedOverrideProvider(
token: any, provider: {useFactory?: Function, useValue?: any, deps?: any[]}): void;
overrideTemplateUsingTestingModule(component: Type<any>, template: string): void;
createComponent<T>(component: Type<T>): ComponentFixture<T>;
}
/** /**
* @description * @description
* Configures and initializes environment for unit testing and provides methods for * Configures and initializes environment for unit testing and provides methods for
* creating components and services in unit tests. * creating components and services in unit tests.
* *
* `TestBed` is the primary api for writing unit tests for Angular applications and libraries. * TestBed is the primary api for writing unit tests for Angular applications and libraries.
*
* *
* Note: Use `TestBed` in tests. It will be set to either `TestBedViewEngine` or `TestBedRender3`
* according to the compiler used.
*/ */
export class TestBedViewEngine implements Injector, TestBed { export class TestBed implements Injector {
/** /**
* Initialize the environment for testing with a compiler factory, a PlatformRef, and an * Initialize the environment for testing with a compiler factory, a PlatformRef, and an
* angular module. These are common to every test in the suite. * angular module. These are common to every test in the suite.
@ -123,9 +72,8 @@ export class TestBedViewEngine implements Injector, TestBed {
* @experimental * @experimental
*/ */
static initTestEnvironment( static initTestEnvironment(
ngModule: Type<any>|Type<any>[], platform: PlatformRef, ngModule: Type<any>|Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): TestBed {
aotSummaries?: () => any[]): TestBedViewEngine { const testBed = getTestBed();
const testBed = _getTestBedViewEngine();
testBed.initTestEnvironment(ngModule, platform, aotSummaries); testBed.initTestEnvironment(ngModule, platform, aotSummaries);
return testBed; return testBed;
} }
@ -135,29 +83,29 @@ export class TestBedViewEngine implements Injector, TestBed {
* *
* @experimental * @experimental
*/ */
static resetTestEnvironment(): void { _getTestBedViewEngine().resetTestEnvironment(); } static resetTestEnvironment() { getTestBed().resetTestEnvironment(); }
static resetTestingModule(): TestBedStatic { static resetTestingModule(): typeof TestBed {
_getTestBedViewEngine().resetTestingModule(); getTestBed().resetTestingModule();
return TestBedViewEngine as any as TestBedStatic; return TestBed;
} }
/** /**
* Allows overriding default compiler providers and settings * Allows overriding default compiler providers and settings
* which are defined in test_injector.js * which are defined in test_injector.js
*/ */
static configureCompiler(config: {providers?: any[]; useJit?: boolean;}): TestBedStatic { static configureCompiler(config: {providers?: any[]; useJit?: boolean;}): typeof TestBed {
_getTestBedViewEngine().configureCompiler(config); getTestBed().configureCompiler(config);
return TestBedViewEngine as any as TestBedStatic; return TestBed;
} }
/** /**
* Allows overriding default providers, directives, pipes, modules of the test injector, * Allows overriding default providers, directives, pipes, modules of the test injector,
* which are defined in test_injector.js * which are defined in test_injector.js
*/ */
static configureTestingModule(moduleDef: TestModuleMetadata): TestBedStatic { static configureTestingModule(moduleDef: TestModuleMetadata): typeof TestBed {
_getTestBedViewEngine().configureTestingModule(moduleDef); getTestBed().configureTestingModule(moduleDef);
return TestBedViewEngine as any as TestBedStatic; return TestBed;
} }
/** /**
@ -167,31 +115,31 @@ export class TestBedViewEngine implements Injector, TestBed {
*/ */
static compileComponents(): Promise<any> { return getTestBed().compileComponents(); } static compileComponents(): Promise<any> { return getTestBed().compileComponents(); }
static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): TestBedStatic { static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): typeof TestBed {
_getTestBedViewEngine().overrideModule(ngModule, override); getTestBed().overrideModule(ngModule, override);
return TestBedViewEngine as any as TestBedStatic; return TestBed;
} }
static overrideComponent(component: Type<any>, override: MetadataOverride<Component>): static overrideComponent(component: Type<any>, override: MetadataOverride<Component>):
TestBedStatic { typeof TestBed {
_getTestBedViewEngine().overrideComponent(component, override); getTestBed().overrideComponent(component, override);
return TestBedViewEngine as any as TestBedStatic; return TestBed;
} }
static overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): static overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>):
TestBedStatic { typeof TestBed {
_getTestBedViewEngine().overrideDirective(directive, override); getTestBed().overrideDirective(directive, override);
return TestBedViewEngine as any as TestBedStatic; return TestBed;
} }
static overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): TestBedStatic { static overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): typeof TestBed {
_getTestBedViewEngine().overridePipe(pipe, override); getTestBed().overridePipe(pipe, override);
return TestBedViewEngine as any as TestBedStatic; return TestBed;
} }
static overrideTemplate(component: Type<any>, template: string): TestBedStatic { static overrideTemplate(component: Type<any>, template: string): typeof TestBed {
_getTestBedViewEngine().overrideComponent(component, {set: {template, templateUrl: null !}}); getTestBed().overrideComponent(component, {set: {template, templateUrl: null !}});
return TestBedViewEngine as any as TestBedStatic; return TestBed;
} }
/** /**
@ -200,11 +148,13 @@ export class TestBedViewEngine implements Injector, TestBed {
* *
* Note: This works for JIT and AOTed components as well. * Note: This works for JIT and AOTed components as well.
*/ */
static overrideTemplateUsingTestingModule(component: Type<any>, template: string): TestBedStatic { static overrideTemplateUsingTestingModule(component: Type<any>, template: string):
_getTestBedViewEngine().overrideTemplateUsingTestingModule(component, template); typeof TestBed {
return TestBedViewEngine as any as TestBedStatic; getTestBed().overrideTemplateUsingTestingModule(component, template);
return TestBed;
} }
/** /**
* Overwrites all providers for the given token with the given provider definition. * Overwrites all providers for the given token with the given provider definition.
* *
@ -213,15 +163,15 @@ export class TestBedViewEngine implements Injector, TestBed {
static overrideProvider(token: any, provider: { static overrideProvider(token: any, provider: {
useFactory: Function, useFactory: Function,
deps: any[], deps: any[],
}): TestBedStatic; }): typeof TestBed;
static overrideProvider(token: any, provider: {useValue: any;}): TestBedStatic; static overrideProvider(token: any, provider: {useValue: any;}): typeof TestBed;
static overrideProvider(token: any, provider: { static overrideProvider(token: any, provider: {
useFactory?: Function, useFactory?: Function,
useValue?: any, useValue?: any,
deps?: any[], deps?: any[],
}): TestBedStatic { }): typeof TestBed {
_getTestBedViewEngine().overrideProvider(token, provider as any); getTestBed().overrideProvider(token, provider as any);
return TestBedViewEngine as any as TestBedStatic; return TestBed;
} }
/** /**
@ -238,17 +188,17 @@ export class TestBedViewEngine implements Injector, TestBed {
useFactory?: Function, useFactory?: Function,
useValue?: any, useValue?: any,
deps?: any[], deps?: any[],
}): TestBedStatic { }): typeof TestBed {
_getTestBedViewEngine().deprecatedOverrideProvider(token, provider as any); getTestBed().deprecatedOverrideProvider(token, provider as any);
return TestBedViewEngine as any as TestBedStatic; return TestBed;
} }
static get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND) { static get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND) {
return _getTestBedViewEngine().get(token, notFoundValue); return getTestBed().get(token, notFoundValue);
} }
static createComponent<T>(component: Type<T>): ComponentFixture<T> { static createComponent<T>(component: Type<T>): ComponentFixture<T> {
return _getTestBedViewEngine().createComponent(component); return getTestBed().createComponent(component);
} }
private _instantiated: boolean = false; private _instantiated: boolean = false;
@ -295,7 +245,7 @@ export class TestBedViewEngine implements Injector, TestBed {
* @experimental * @experimental
*/ */
initTestEnvironment( initTestEnvironment(
ngModule: Type<any>|Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): void { ngModule: Type<any>|Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]) {
if (this.platform || this.ngModule) { if (this.platform || this.ngModule) {
throw new Error('Cannot set base providers because it has already been called'); throw new Error('Cannot set base providers because it has already been called');
} }
@ -311,14 +261,14 @@ export class TestBedViewEngine implements Injector, TestBed {
* *
* @experimental * @experimental
*/ */
resetTestEnvironment(): void { resetTestEnvironment() {
this.resetTestingModule(); this.resetTestingModule();
this.platform = null !; this.platform = null !;
this.ngModule = null !; this.ngModule = null !;
this._testEnvAotSummaries = () => []; this._testEnvAotSummaries = () => [];
} }
resetTestingModule(): void { resetTestingModule() {
clearOverrides(); clearOverrides();
this._aotSummaries = []; this._aotSummaries = [];
this._templateOverrides = []; this._templateOverrides = [];
@ -352,12 +302,12 @@ export class TestBedViewEngine implements Injector, TestBed {
this._activeFixtures = []; this._activeFixtures = [];
} }
configureCompiler(config: {providers?: any[], useJit?: boolean}): void { configureCompiler(config: {providers?: any[], useJit?: boolean}) {
this._assertNotInstantiated('TestBed.configureCompiler', 'configure the compiler'); this._assertNotInstantiated('TestBed.configureCompiler', 'configure the compiler');
this._compilerOptions.push(config); this._compilerOptions.push(config);
} }
configureTestingModule(moduleDef: TestModuleMetadata): void { configureTestingModule(moduleDef: TestModuleMetadata) {
this._assertNotInstantiated('TestBed.configureTestingModule', 'configure the test module'); this._assertNotInstantiated('TestBed.configureTestingModule', 'configure the test module');
if (moduleDef.providers) { if (moduleDef.providers) {
this._providers.push(...moduleDef.providers); this._providers.push(...moduleDef.providers);
@ -388,7 +338,7 @@ export class TestBedViewEngine implements Injector, TestBed {
}); });
} }
private _initIfNeeded(): void { private _initIfNeeded() {
if (this._instantiated) { if (this._instantiated) {
return; return;
} }
@ -477,7 +427,7 @@ export class TestBedViewEngine implements Injector, TestBed {
} }
} }
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any { get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND) {
this._initIfNeeded(); this._initIfNeeded();
if (token === TestBed) { if (token === TestBed) {
return this; return this;
@ -626,32 +576,13 @@ export class TestBedViewEngine implements Injector, TestBed {
} }
} }
/** let _testBed: TestBed = null !;
* @description
* Configures and initializes environment for unit testing and provides methods for
* creating components and services in unit tests.
*
* `TestBed` is the primary api for writing unit tests for Angular applications and libraries.
*
* Note: Use `TestBed` in tests. It will be set to either `TestBedViewEngine` or `TestBedRender3`
* according to the compiler used.
*/
export const TestBed: TestBedStatic =
ivyEnabled ? TestBedRender3 as any as TestBedStatic : TestBedViewEngine as any as TestBedStatic;
/** /**
* Returns a singleton of the applicable `TestBed`.
*
* It will be either an instance of `TestBedViewEngine` or `TestBedRender3`.
*
* @experimental * @experimental
*/ */
export const getTestBed: () => TestBed = ivyEnabled ? _getTestBedRender3 : _getTestBedViewEngine; export function getTestBed() {
return _testBed = _testBed || new TestBed();
let testBed: TestBedViewEngine;
function _getTestBedViewEngine(): TestBedViewEngine {
return testBed = testBed || new TestBedViewEngine();
} }
/** /**

View File

@ -1,136 +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, InjectionToken, NgModule, Pipe, PlatformRef, SchemaMetadata, Type} from '@angular/core';
import {ComponentFixture} from './component_fixture';
import {MetadataOverride} from './metadata_override';
import {TestBed} from './test_bed';
/**
* An abstract class for inserting the root test component element in a platform independent way.
*
* @experimental
*/
export class TestComponentRenderer {
insertRootElement(rootElementId: string) {}
}
/**
* @experimental
*/
export const ComponentFixtureAutoDetect =
new InjectionToken<boolean[]>('ComponentFixtureAutoDetect');
/**
* @experimental
*/
export const ComponentFixtureNoNgZone = new InjectionToken<boolean[]>('ComponentFixtureNoNgZone');
/**
* @experimental
*/
export type TestModuleMetadata = {
providers?: any[],
declarations?: any[],
imports?: any[],
schemas?: Array<SchemaMetadata|any[]>,
aotSummaries?: () => any[],
};
/**
* Static methods implemented by the `TestBedViewEngine` and `TestBedRender3`
*/
export interface TestBedStatic {
new (...args: any[]): TestBed;
initTestEnvironment(
ngModule: Type<any>|Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): TestBed;
/**
* Reset the providers for the test injector.
*
* @experimental
*/
resetTestEnvironment(): void;
resetTestingModule(): TestBedStatic;
/**
* Allows overriding default compiler providers and settings
* which are defined in test_injector.js
*/
configureCompiler(config: {providers?: any[]; useJit?: boolean;}): TestBedStatic;
/**
* Allows overriding default providers, directives, pipes, modules of the test injector,
* which are defined in test_injector.js
*/
configureTestingModule(moduleDef: TestModuleMetadata): TestBedStatic;
/**
* Compile components with a `templateUrl` for the test's NgModule.
* It is necessary to call this function
* as fetching urls is asynchronous.
*/
compileComponents(): Promise<any>;
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): TestBedStatic;
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): TestBedStatic;
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): TestBedStatic;
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): TestBedStatic;
overrideTemplate(component: Type<any>, template: string): TestBedStatic;
/**
* Overrides the template of the given component, compiling the template
* in the context of the TestingModule.
*
* Note: This works for JIT and AOTed components as well.
*/
overrideTemplateUsingTestingModule(component: Type<any>, template: string): TestBedStatic;
/**
* Overwrites all providers for the given token with the given provider definition.
*
* Note: This works for JIT and AOTed components as well.
*/
overrideProvider(token: any, provider: {
useFactory: Function,
deps: any[],
}): TestBedStatic;
overrideProvider(token: any, provider: {useValue: any;}): TestBedStatic;
overrideProvider(token: any, provider: {
useFactory?: Function,
useValue?: any,
deps?: any[],
}): TestBedStatic;
/**
* Overwrites all providers for the given token with the given provider definition.
*
* @deprecated as it makes all NgModules lazy. Introduced only for migrating off of it.
*/
deprecatedOverrideProvider(token: any, provider: {
useFactory: Function,
deps: any[],
}): void;
deprecatedOverrideProvider(token: any, provider: {useValue: any;}): void;
deprecatedOverrideProvider(token: any, provider: {
useFactory?: Function,
useValue?: any,
deps?: any[],
}): TestBedStatic;
get(token: any, notFoundValue?: any): any;
createComponent<T>(component: Type<T>): ComponentFixture<T>;
}

View File

@ -15,9 +15,7 @@
export * from './async'; export * from './async';
export * from './component_fixture'; export * from './component_fixture';
export * from './fake_async'; export * from './fake_async';
export {TestBed, getTestBed, inject, InjectSetupWrapper, withModule} from './test_bed'; export * from './test_bed';
export * from './test_bed_common';
export * from './before_each'; export * from './before_each';
export * from './metadata_override'; export * from './metadata_override';
export {MetadataOverrider as ɵMetadataOverrider} from './metadata_overrider';
export * from './private_export_testing'; export * from './private_export_testing';

View File

@ -1,27 +1,23 @@
{ {
"extends": "../tsconfig-build.json", "extends": "../tsconfig-build.json",
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"rootDir": "../", "rootDir": "../",
"paths": { "paths": {
"rxjs/*": [ "rxjs/*": ["../../../node_modules/rxjs/*"],
"../../../node_modules/rxjs/*" "@angular/core": ["../../../dist/packages/core"]
],
"@angular/core": [
"../../../dist/packages/core"
],
"@angular/compiler": [
"../../../dist/packages/compiler"
],
}, },
"outDir": "../../../dist/packages/core" "outDir": "../../../dist/packages/core"
}, },
"files": [ "files": [
"public_api.ts", "public_api.ts",
"../../../node_modules/zone.js/dist/zone.js.d.ts", "../../../node_modules/zone.js/dist/zone.js.d.ts",
"../../system.d.ts", "../../system.d.ts",
"../../types.d.ts" "../../types.d.ts"
], ],
"angularCompilerOptions": { "angularCompilerOptions": {
"strictMetadataEmit": false, "strictMetadataEmit": false,
"skipTemplateCodegen": true, "skipTemplateCodegen": true,

View File

@ -1,26 +1,22 @@
{ {
"extends": "../tsconfig-build.json", "extends": "../tsconfig-build.json",
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"rootDir": ".", "rootDir": ".",
"paths": { "paths": {
"rxjs/*": [ "rxjs/*": ["../../node_modules/rxjs/*"],
"../../node_modules/rxjs/*" "@angular/core": ["."]
],
"@angular/core": [
"."
],
"@angular/compiler": [
"../../dist/packages/compiler"
]
}, },
"outDir": "../../dist/packages/core" "outDir": "../../dist/packages/core"
}, },
"files": [ "files": [
"public_api.ts", "public_api.ts",
"../../node_modules/zone.js/dist/zone.js.d.ts", "../../node_modules/zone.js/dist/zone.js.d.ts",
"../system.d.ts" "../system.d.ts"
], ],
"angularCompilerOptions": { "angularCompilerOptions": {
"annotateForClosureCompiler": true, "annotateForClosureCompiler": true,
"strictMetadataEmit": false, "strictMetadataEmit": false,

View File

@ -18,7 +18,6 @@ jasmine_node_test(
bootstrap = ["angular/tools/testing/init_node_spec.js"], bootstrap = ["angular/tools/testing/init_node_spec.js"],
data = [ data = [
"//packages/common:npm_package", "//packages/common:npm_package",
"//packages/compiler:npm_package",
"//packages/core:npm_package", "//packages/core:npm_package",
"//packages/forms:npm_package", "//packages/forms:npm_package",
], ],

View File

@ -13,7 +13,7 @@ export const MODULE_SUFFIX = '';
const builtinExternalReferences = createBuiltinExternalReferencesMap(); const builtinExternalReferences = createBuiltinExternalReferencesMap();
export class JitReflector implements CompileReflector { export class JitReflector implements CompileReflector {
private reflectionCapabilities = new ReflectionCapabilities(); private reflectionCapabilities: ReflectionCapabilities = new ReflectionCapabilities();
componentModuleUrl(type: any, cmpMetadata: Component): string { componentModuleUrl(type: any, cmpMetadata: Component): string {
const moduleId = cmpMetadata.moduleId; const moduleId = cmpMetadata.moduleId;

View File

@ -8,9 +8,9 @@
import {CompileReflector, DirectiveResolver, ERROR_COMPONENT_TYPE, NgModuleResolver, PipeResolver} from '@angular/compiler'; import {CompileReflector, DirectiveResolver, ERROR_COMPONENT_TYPE, NgModuleResolver, PipeResolver} from '@angular/compiler';
import {MockDirectiveResolver, MockNgModuleResolver, MockPipeResolver} from '@angular/compiler/testing'; import {MockDirectiveResolver, MockNgModuleResolver, MockPipeResolver} from '@angular/compiler/testing';
import {CompilerFactory, CompilerOptions, Component, ComponentFactory, Directive, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, Pipe, StaticProvider, Type, ɵstringify as stringify} from '@angular/core'; import {Compiler, CompilerFactory, CompilerOptions, Component, ComponentFactory, Directive, Injectable, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, Pipe, PlatformRef, StaticProvider, Type, createPlatformFactory, ɵstringify} from '@angular/core';
import {MetadataOverride, ɵTestingCompiler as TestingCompiler, ɵTestingCompilerFactory as TestingCompilerFactory} from '@angular/core/testing'; import {MetadataOverride, ɵTestingCompiler as TestingCompiler, ɵTestingCompilerFactory as TestingCompilerFactory} from '@angular/core/testing';
import {ɵCompilerImpl as CompilerImpl} from '@angular/platform-browser-dynamic'; import {ɵCompilerImpl as CompilerImpl, ɵplatformCoreDynamic as platformCoreDynamic} from '@angular/platform-browser-dynamic';
import {MetadataOverrider} from './metadata_overrider'; import {MetadataOverrider} from './metadata_overrider';
@ -63,7 +63,7 @@ export class TestingCompilerImpl implements TestingCompiler {
checkOverrideAllowed(type: Type<any>) { checkOverrideAllowed(type: Type<any>) {
if (this._compiler.hasAotSummary(type)) { if (this._compiler.hasAotSummary(type)) {
throw new Error(`${stringify(type)} was AOT compiled, so its metadata cannot be changed.`); throw new Error(`${ɵstringify(type)} was AOT compiled, so its metadata cannot be changed.`);
} }
} }

View File

@ -15,7 +15,7 @@ import {DOCUMENT, ɵgetDOM as getDOM} from '@angular/platform-browser';
*/ */
@Injectable() @Injectable()
export class DOMTestComponentRenderer extends TestComponentRenderer { export class DOMTestComponentRenderer extends TestComponentRenderer {
constructor(@Inject(DOCUMENT) private _doc: any) { super(); } constructor(@Inject(DOCUMENT) private _doc: any /** TODO #9100 */) { super(); }
insertRootElement(rootElId: string) { insertRootElement(rootElId: string) {
const rootEl = <HTMLElement>getDOM().firstChild( const rootEl = <HTMLElement>getDOM().firstChild(

View File

@ -6,5 +6,126 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
// `MetadataOverrider` has been moved to core/testing to allow using it from the render3 TestBed import {ɵstringify as stringify} from '@angular/core';
export {ɵMetadataOverrider as MetadataOverrider} from '@angular/core/testing'; import {MetadataOverride} from '@angular/core/testing';
type StringMap = {
[key: string]: any
};
let _nextReferenceId = 0;
export class MetadataOverrider {
private _references = new Map<any, string>();
/**
* Creates a new instance for the given metadata class
* based on an old instance and overrides.
*/
overrideMetadata<C extends T, T>(
metadataClass: {new (options: T): C;}, oldMetadata: C, override: MetadataOverride<T>): C {
const props: StringMap = {};
if (oldMetadata) {
_valueProps(oldMetadata).forEach((prop) => props[prop] = (<any>oldMetadata)[prop]);
}
if (override.set) {
if (override.remove || override.add) {
throw new Error(`Cannot set and add/remove ${stringify(metadataClass)} at the same time!`);
}
setMetadata(props, override.set);
}
if (override.remove) {
removeMetadata(props, override.remove, this._references);
}
if (override.add) {
addMetadata(props, override.add);
}
return new metadataClass(<any>props);
}
}
function removeMetadata(metadata: StringMap, remove: any, references: Map<any, string>) {
const removeObjects = new Set<string>();
for (const prop in remove) {
const removeValue = remove[prop];
if (removeValue instanceof Array) {
removeValue.forEach(
(value: any) => { removeObjects.add(_propHashKey(prop, value, references)); });
} else {
removeObjects.add(_propHashKey(prop, removeValue, references));
}
}
for (const prop in metadata) {
const propValue = metadata[prop];
if (propValue instanceof Array) {
metadata[prop] = propValue.filter(
(value: any) => !removeObjects.has(_propHashKey(prop, value, references)));
} else {
if (removeObjects.has(_propHashKey(prop, propValue, references))) {
metadata[prop] = undefined;
}
}
}
}
function addMetadata(metadata: StringMap, add: any) {
for (const prop in add) {
const addValue = add[prop];
const propValue = metadata[prop];
if (propValue != null && propValue instanceof Array) {
metadata[prop] = propValue.concat(addValue);
} else {
metadata[prop] = addValue;
}
}
}
function setMetadata(metadata: StringMap, set: any) {
for (const prop in set) {
metadata[prop] = set[prop];
}
}
function _propHashKey(propName: any, propValue: any, references: Map<any, string>): string {
const replacer = (key: any, value: any) => {
if (typeof value === 'function') {
value = _serializeReference(value, references);
}
return value;
};
return `${propName}:${JSON.stringify(propValue, replacer)}`;
}
function _serializeReference(ref: any, references: Map<any, string>): string {
let id = references.get(ref);
if (!id) {
id = `${stringify(ref)}${_nextReferenceId++}`;
references.set(ref, id);
}
return id;
}
function _valueProps(obj: any): string[] {
const props: string[] = [];
// regular public props
Object.keys(obj).forEach((prop) => {
if (!prop.startsWith('_')) {
props.push(prop);
}
});
// getters
let proto = obj;
while (proto = Object.getPrototypeOf(proto)) {
Object.keys(proto).forEach((protoProp) => {
const desc = Object.getOwnPropertyDescriptor(proto, protoProp);
if (!protoProp.startsWith('_') && desc && 'get' in desc) {
props.push(protoProp);
}
});
}
return props;
}

View File

@ -193,10 +193,9 @@ export abstract class AssetGroup {
cacheDirectives.forEach(v => v[0] = v[0].toLowerCase()); cacheDirectives.forEach(v => v[0] = v[0].toLowerCase());
// Find the max-age directive, if one exists. // Find the max-age directive, if one exists.
const maxAgeDirective = cacheDirectives.find(v => v[0] === 'max-age'); const cacheAge = cacheDirectives.filter(v => v[0] === 'max-age').map(v => v[1])[0];
const cacheAge = maxAgeDirective ? maxAgeDirective[1] : undefined;
if (!cacheAge) { if (cacheAge.length === 0) {
// No usable TTL defined. Must assume that the response is stale. // No usable TTL defined. Must assume that the response is stale.
return true; return true;
} }

View File

@ -28,7 +28,6 @@ const dist =
.addFile('/lazy/unchanged1.txt', 'this is unchanged (1)') .addFile('/lazy/unchanged1.txt', 'this is unchanged (1)')
.addFile('/lazy/unchanged2.txt', 'this is unchanged (2)') .addFile('/lazy/unchanged2.txt', 'this is unchanged (2)')
.addUnhashedFile('/unhashed/a.txt', 'this is unhashed', {'Cache-Control': 'max-age=10'}) .addUnhashedFile('/unhashed/a.txt', 'this is unhashed', {'Cache-Control': 'max-age=10'})
.addUnhashedFile('/unhashed/b.txt', 'this is unhashed b', {'Cache-Control': 'no-cache'})
.build(); .build();
const distUpdate = const distUpdate =
@ -622,13 +621,6 @@ const manifestUpdateHash = sha1(JSON.stringify(manifestUpdate));
server.assertNoOtherRequests(); server.assertNoOtherRequests();
}); });
async_it(`doesn't error when 'Cache-Control' is 'no-cache'`, async() => {
expect(await makeRequest(scope, '/unhashed/b.txt')).toEqual('this is unhashed b');
server.assertSawRequestFor('/unhashed/b.txt');
expect(await makeRequest(scope, '/unhashed/b.txt')).toEqual('this is unhashed b');
server.assertNoOtherRequests();
});
async_it('avoid opaque responses', async() => { async_it('avoid opaque responses', async() => {
expect(await makeRequest(scope, '/unhashed/a.txt', 'default', { expect(await makeRequest(scope, '/unhashed/a.txt', 'default', {
credentials: 'include' credentials: 'include'

View File

@ -56,7 +56,7 @@ if [[ ${TRAVIS} &&
travisFoldStart "yarn-install.aio" travisFoldStart "yarn-install.aio"
( (
# HACK (don't submit with this): Build Angular # HACK (don't submit with this): Build Angular
./build.sh --packages=compiler,core,elements --examples=false ./build.sh --packages=core,elements --examples=false
cd ${PROJECT_ROOT}/aio cd ${PROJECT_ROOT}/aio
yarn install --frozen-lockfile --non-interactive yarn install --frozen-lockfile --non-interactive

View File

@ -28,6 +28,7 @@ export interface AnimationAnimateRefMetadata extends AnimationMetadata {
options: AnimationOptions | null; options: AnimationOptions | null;
} }
/** @experimental */
export declare abstract class AnimationBuilder { export declare abstract class AnimationBuilder {
abstract build(animation: AnimationMetadata | AnimationMetadata[]): AnimationFactory; abstract build(animation: AnimationMetadata | AnimationMetadata[]): AnimationFactory;
} }
@ -43,6 +44,7 @@ export interface AnimationEvent {
triggerName: string; triggerName: string;
} }
/** @experimental */
export declare abstract class AnimationFactory { export declare abstract class AnimationFactory {
abstract create(element: any, options?: AnimationOptions): AnimationPlayer; abstract create(element: any, options?: AnimationOptions): AnimationPlayer;
} }
@ -83,6 +85,7 @@ export declare interface AnimationOptions {
}; };
} }
/** @experimental */
export interface AnimationPlayer { export interface AnimationPlayer {
beforeDestroy?: () => any; beforeDestroy?: () => any;
parentPlayer: AnimationPlayer | null; parentPlayer: AnimationPlayer | null;
@ -171,6 +174,7 @@ export declare function group(steps: AnimationMetadata[], options?: AnimationOpt
export declare function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSequenceMetadata; export declare function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSequenceMetadata;
/** @experimental */
export declare class NoopAnimationPlayer implements AnimationPlayer { export declare class NoopAnimationPlayer implements AnimationPlayer {
parentPlayer: AnimationPlayer | null; parentPlayer: AnimationPlayer | null;
readonly totalTime: number; readonly totalTime: number;

View File

@ -37,7 +37,7 @@ export declare function flush(maxTurns?: number): number;
export declare function flushMicrotasks(): void; export declare function flushMicrotasks(): void;
/** @experimental */ /** @experimental */
export declare const getTestBed: () => TestBed; export declare function getTestBed(): TestBed;
export declare function inject(tokens: any[], fn: Function): () => any; export declare function inject(tokens: any[], fn: Function): () => any;
@ -57,51 +57,71 @@ export declare type MetadataOverride<T> = {
/** @experimental */ /** @experimental */
export declare function resetFakeAsyncZone(): void; export declare function resetFakeAsyncZone(): void;
export declare const TestBed: TestBedStatic; export declare class TestBed implements Injector {
ngModule: Type<any> | Type<any>[];
export interface TestBedStatic { platform: PlatformRef;
new (...args: any[]): TestBed;
compileComponents(): Promise<any>; compileComponents(): Promise<any>;
configureCompiler(config: { configureCompiler(config: {
providers?: any[]; providers?: any[];
useJit?: boolean; useJit?: boolean;
}): TestBedStatic;
configureTestingModule(moduleDef: TestModuleMetadata): TestBedStatic;
createComponent<T>(component: Type<T>): ComponentFixture<T>;
deprecatedOverrideProvider(token: any, provider: {
useFactory?: Function;
useValue?: any;
deps?: any[];
}): TestBedStatic;
deprecatedOverrideProvider(token: any, provider: {
useValue: any;
}): void; }): void;
configureTestingModule(moduleDef: TestModuleMetadata): void;
createComponent<T>(component: Type<T>): ComponentFixture<T>;
/** @deprecated */ deprecatedOverrideProvider(token: any, provider: { /** @deprecated */ deprecatedOverrideProvider(token: any, provider: {
useFactory: Function; useFactory: Function;
deps: any[]; deps: any[];
}): void; }): void;
get(token: any, notFoundValue?: any): any; deprecatedOverrideProvider(token: any, provider: {
initTestEnvironment(ngModule: Type<any> | Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): TestBed;
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): TestBedStatic;
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): TestBedStatic;
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): TestBedStatic;
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): TestBedStatic;
overrideProvider(token: any, provider: {
useValue: any; useValue: any;
}): TestBedStatic; }): void;
overrideProvider(token: any, provider: { execute(tokens: any[], fn: Function, context?: any): any;
useFactory?: Function; get(token: any, notFoundValue?: any): any;
useValue?: any; /** @experimental */ initTestEnvironment(ngModule: Type<any> | Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): void;
deps?: any[]; overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void;
}): TestBedStatic; overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void;
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void;
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void;
overrideProvider(token: any, provider: { overrideProvider(token: any, provider: {
useFactory: Function; useFactory: Function;
deps: any[]; deps: any[];
}): TestBedStatic; }): void;
overrideTemplate(component: Type<any>, template: string): TestBedStatic; overrideProvider(token: any, provider: {
overrideTemplateUsingTestingModule(component: Type<any>, template: string): TestBedStatic; useValue: any;
}): void;
overrideTemplateUsingTestingModule(component: Type<any>, template: string): void;
/** @experimental */ resetTestEnvironment(): void; /** @experimental */ resetTestEnvironment(): void;
resetTestingModule(): TestBedStatic; resetTestingModule(): void;
static compileComponents(): Promise<any>;
static configureCompiler(config: {
providers?: any[];
useJit?: boolean;
}): typeof TestBed;
static configureTestingModule(moduleDef: TestModuleMetadata): typeof TestBed;
static createComponent<T>(component: Type<T>): ComponentFixture<T>;
static deprecatedOverrideProvider(token: any, provider: {
useValue: any;
}): void;
/** @deprecated */ static deprecatedOverrideProvider(token: any, provider: {
useFactory: Function;
deps: any[];
}): void;
static get(token: any, notFoundValue?: any): any;
/** @experimental */ static initTestEnvironment(ngModule: Type<any> | Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): TestBed;
static overrideComponent(component: Type<any>, override: MetadataOverride<Component>): typeof TestBed;
static overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): typeof TestBed;
static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): typeof TestBed;
static overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): typeof TestBed;
static overrideProvider(token: any, provider: {
useValue: any;
}): typeof TestBed;
static overrideProvider(token: any, provider: {
useFactory: Function;
deps: any[];
}): typeof TestBed;
static overrideTemplate(component: Type<any>, template: string): typeof TestBed;
static overrideTemplateUsingTestingModule(component: Type<any>, template: string): typeof TestBed;
/** @experimental */ static resetTestEnvironment(): void;
static resetTestingModule(): typeof TestBed;
} }
/** @experimental */ /** @experimental */