Compare commits
3 Commits
7.0.0-beta
...
ivy-aot-ba
Author | SHA1 | Date | |
---|---|---|---|
a2efa0691a | |||
70b1b8385f | |||
244ee5846d |
7
.github/angular-robot.yml
vendored
7
.github/angular-robot.yml
vendored
@ -62,13 +62,6 @@ merge:
|
||||
|
||||
# list of checks that will determine if the merge label can be added
|
||||
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
|
||||
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")
|
||||
|
11
CHANGELOG.md
11
CHANGELOG.md
@ -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>
|
||||
# [7.0.0-beta.1](https://github.com/angular/angular/compare/7.0.0-beta.0...7.0.0-beta.1) (2018-08-08)
|
||||
|
||||
|
12
WORKSPACE
12
WORKSPACE
@ -20,16 +20,16 @@ http_archive(
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_webtesting",
|
||||
url = "https://github.com/bazelbuild/rules_webtesting/archive/0.2.1.zip",
|
||||
strip_prefix = "rules_webtesting-0.2.1",
|
||||
sha256 = "7d490aadff9b5262e5251fa69427ab2ffd1548422467cb9f9e1d110e2c36f0fa",
|
||||
url = "https://github.com/bazelbuild/rules_webtesting/archive/7ffe970bbf380891754487f66c3d680c087d67f2.zip",
|
||||
strip_prefix = "rules_webtesting-7ffe970bbf380891754487f66c3d680c087d67f2",
|
||||
sha256 = "4fb0dca8c9a90547891b7ef486592775a523330fc4555c88cd8f09270055c2ce",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "build_bazel_rules_typescript",
|
||||
url = "https://github.com/bazelbuild/rules_typescript/archive/0.16.0.zip",
|
||||
strip_prefix = "rules_typescript-0.16.0",
|
||||
sha256 = "e65c5639a42e2f6d3f9d2bda62487d6b42734830dda45be1620c3e2b1115070c",
|
||||
url = "https://github.com/bazelbuild/rules_typescript/archive/1d9a4b0087f307e31af91e2b221a6447288994c6.zip",
|
||||
strip_prefix = "rules_typescript-1d9a4b0087f307e31af91e2b221a6447288994c6",
|
||||
sha256 = "e17ac3f33d5d3cd2a0c385c4fd28b814d0ad46c6c67ccaef97160be99d7a24eb",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
|
@ -8,7 +8,7 @@ Everything in this folder is part of the documentation project. This includes
|
||||
|
||||
## 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.
|
||||
Here are the most important tasks you might need to use:
|
||||
|
||||
|
@ -16,7 +16,6 @@ describe('Form Validation Tests', function () {
|
||||
|
||||
tests('Template-Driven Form');
|
||||
bobTests();
|
||||
asyncValidationTests();
|
||||
crossValidationTests();
|
||||
});
|
||||
|
||||
@ -27,7 +26,6 @@ describe('Form Validation Tests', function () {
|
||||
|
||||
tests('Reactive Form');
|
||||
bobTests();
|
||||
asyncValidationTests();
|
||||
crossValidationTests();
|
||||
});
|
||||
});
|
||||
@ -47,7 +45,6 @@ let page: {
|
||||
errorMessages: ElementArrayFinder,
|
||||
heroFormButtons: ElementArrayFinder,
|
||||
heroSubmitted: ElementFinder,
|
||||
alterEgoErrors: ElementFinder,
|
||||
crossValidationErrorMessage: ElementFinder,
|
||||
};
|
||||
|
||||
@ -66,7 +63,6 @@ function getPage(sectionTag: string) {
|
||||
errorMessages: section.all(by.css('div.alert')),
|
||||
heroFormButtons: buttons,
|
||||
heroSubmitted: section.element(by.css('.submitted-message')),
|
||||
alterEgoErrors: section.element(by.css('.alter-ego-errors')),
|
||||
crossValidationErrorMessage: section.element(by.css('.cross-validation-error-message')),
|
||||
};
|
||||
}
|
||||
@ -160,16 +156,6 @@ function expectFormIsInvalid() {
|
||||
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() {
|
||||
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() {
|
||||
const emsg = 'Name cannot match alter ego.';
|
||||
|
||||
@ -227,9 +187,6 @@ function crossValidationTests() {
|
||||
page.alterEgoInput.clear();
|
||||
page.alterEgoInput.sendKeys('Batman');
|
||||
|
||||
triggerAlterEgoValidation();
|
||||
waitForAlterEgoValidation();
|
||||
|
||||
expectFormIsInvalid();
|
||||
expect(page.crossValidationErrorMessage.getText()).toBe(emsg);
|
||||
});
|
||||
@ -241,9 +198,6 @@ function crossValidationTests() {
|
||||
page.alterEgoInput.clear();
|
||||
page.alterEgoInput.sendKeys('Superman');
|
||||
|
||||
triggerAlterEgoValidation();
|
||||
waitForAlterEgoValidation();
|
||||
|
||||
expectFormIsValid();
|
||||
expect(page.crossValidationErrorMessage.isPresent()).toBe(false);
|
||||
});
|
||||
|
@ -8,7 +8,6 @@ import { HeroFormTemplateComponent } from './template/hero-form-template.compone
|
||||
import { HeroFormReactiveComponent } from './reactive/hero-form-reactive.component';
|
||||
import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive';
|
||||
import { IdentityRevealedValidatorDirective } from './shared/identity-revealed.directive';
|
||||
import { UniqueAlterEgoValidatorDirective } from './shared/alter-ego.directive';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -21,8 +20,7 @@ import { UniqueAlterEgoValidatorDirective } from './shared/alter-ego.directive';
|
||||
HeroFormTemplateComponent,
|
||||
HeroFormReactiveComponent,
|
||||
ForbiddenValidatorDirective,
|
||||
IdentityRevealedValidatorDirective,
|
||||
UniqueAlterEgoValidatorDirective
|
||||
IdentityRevealedValidatorDirective
|
||||
],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
|
@ -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
|
||||
}
|
@ -35,13 +35,6 @@
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<input id="alterEgo" class="form-control"
|
||||
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>
|
||||
|
||||
<!-- #docregion cross-validation-error-message -->
|
||||
|
@ -4,7 +4,6 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
|
||||
import { identityRevealedValidator } from '../shared/identity-revealed.directive';
|
||||
import { UniqueAlterEgoValidator } from '../shared/alter-ego.directive';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-form-reactive',
|
||||
@ -26,10 +25,7 @@ export class HeroFormReactiveComponent implements OnInit {
|
||||
Validators.minLength(4),
|
||||
forbiddenNameValidator(/bob/i)
|
||||
]),
|
||||
'alterEgo': new FormControl(this.hero.alterEgo, {
|
||||
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
|
||||
updateOn: 'blur'
|
||||
}),
|
||||
'alterEgo': new FormControl(this.hero.alterEgo),
|
||||
'power': new FormControl(this.hero.power, Validators.required)
|
||||
}, { 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 power() { return this.heroForm.get('power'); }
|
||||
|
||||
get alterEgo() { return this.heroForm.get('alterEgo'); }
|
||||
|
||||
constructor(private alterEgoValidator: UniqueAlterEgoValidator) { }
|
||||
}
|
||||
|
@ -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
|
@ -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));
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@
|
||||
<label for="name">Name</label>
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
<!-- #docregion name-input -->
|
||||
<input id="name" name="name" class="form-control"
|
||||
<input id="name" name="name" class="form-control"
|
||||
required minlength="4" appForbiddenName="bob"
|
||||
[(ngModel)]="hero.name" #name="ngModel" >
|
||||
<!-- #enddocregion name-input -->
|
||||
@ -35,20 +35,8 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<!-- #docregion async-validation -->
|
||||
<input id="alterEgo" class="form-control" name="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>
|
||||
<input id="alterEgo" class="form-control"
|
||||
name="alterEgo" [(ngModel)]="hero.alterEgo" >
|
||||
</div>
|
||||
|
||||
<!-- #docregion cross-validation-error-message -->
|
||||
|
@ -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.
|
||||
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.
|
||||
* *Property binding* lets you interpolate values that are computed from your application data into the HTML.
|
||||
|
@ -6,12 +6,12 @@
|
||||
Improve overall data quality by validating user input for accuracy and completeness.
|
||||
|
||||
This page shows how to validate user input in the UI and display useful validation messages
|
||||
using both reactive and template-driven forms. It assumes some basic knowledge of the two
|
||||
using both reactive and template-driven forms. It assumes some basic knowledge of the two
|
||||
forms modules.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
If you're new to forms, start by reviewing the [Forms](guide/forms) and
|
||||
If you're new to forms, start by reviewing the [Forms](guide/forms) and
|
||||
[Reactive Forms](guide/reactive-forms) guides.
|
||||
|
||||
</div>
|
||||
@ -19,11 +19,11 @@ If you're new to forms, start by reviewing the [Forms](guide/forms) and
|
||||
|
||||
## Template-driven validation
|
||||
|
||||
To add validation to a template-driven form, you add the same validation attributes as you
|
||||
would with [native HTML form validation](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation).
|
||||
To add validation to a template-driven form, you add the same validation attributes as you
|
||||
would with [native HTML form validation](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation).
|
||||
Angular uses directives to match these attributes with validator functions in the framework.
|
||||
|
||||
Every time the value of a form control changes, Angular runs validation and generates
|
||||
Every time the value of a form control changes, Angular runs validation and generates
|
||||
either a list of validation errors, which results in an INVALID status, or null, which results in a VALID status.
|
||||
|
||||
You can then inspect the control's state by exporting `ngModel` to a local template variable.
|
||||
@ -36,12 +36,12 @@ The following example exports `NgModel` into a variable called `name`:
|
||||
|
||||
Note the following:
|
||||
|
||||
* The `<input>` element carries the HTML validation attributes: `required` and `minlength`. It
|
||||
also carries a custom validator directive, `forbiddenName`. For more
|
||||
* The `<input>` element carries the HTML validation attributes: `required` and `minlength`. It
|
||||
also carries a custom validator directive, `forbiddenName`. For more
|
||||
information, see [Custom validators](guide/form-validation#custom-validators) section.
|
||||
|
||||
* `#name="ngModel"` exports `NgModel` into a local variable called `name`. `NgModel` mirrors many of the properties of its underlying
|
||||
`FormControl` instance, so you can use this in the template to check for control states such as `valid` and `dirty`. For a full list of control properties, see the [AbstractControl](api/forms/AbstractControl)
|
||||
* `#name="ngModel"` exports `NgModel` into a local variable called `name`. `NgModel` mirrors many of the properties of its underlying
|
||||
`FormControl` instance, so you can use this in the template to check for control states such as `valid` and `dirty`. For a full list of control properties, see the [AbstractControl](api/forms/AbstractControl)
|
||||
API reference.
|
||||
|
||||
* The `*ngIf` on the `<div>` element reveals a set of nested message `divs`
|
||||
@ -49,7 +49,7 @@ but only if the `name` is invalid and the control is either `dirty` or `touched`
|
||||
|
||||
* Each nested `<div>` can present a custom message for one of the possible validation errors.
|
||||
There are messages for `required`, `minlength`, and `forbiddenName`.
|
||||
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@ -58,8 +58,8 @@ There are messages for `required`, `minlength`, and `forbiddenName`.
|
||||
#### Why check _dirty_ and _touched_?
|
||||
|
||||
You may not want your application to display errors before the user has a chance to edit the form.
|
||||
The checks for `dirty` and `touched` prevent errors from showing until the user
|
||||
does one of two things: changes the value,
|
||||
The checks for `dirty` and `touched` prevent errors from showing until the user
|
||||
does one of two things: changes the value,
|
||||
turning the control dirty; or blurs the form control element, setting the control to touched.
|
||||
|
||||
</div>
|
||||
@ -70,24 +70,24 @@ In a reactive form, the source of truth is the component class. Instead of addin
|
||||
|
||||
### Validator functions
|
||||
|
||||
There are two types of validator functions: sync validators and async validators.
|
||||
There are two types of validator functions: sync validators and async validators.
|
||||
|
||||
* **Sync validators**: functions that take a control instance and immediately return either a set of validation errors or `null`. You can pass these in as the second argument when you instantiate a `FormControl`.
|
||||
|
||||
* **Async validators**: functions that take a control instance and return a Promise
|
||||
or Observable that later emits a set of validation errors or `null`. You can
|
||||
pass these in as the third argument when you instantiate a `FormControl`.
|
||||
* **Async validators**: functions that take a control instance and return a Promise
|
||||
or Observable that later emits a set of validation errors or `null`. You can
|
||||
pass these in as the third argument when you instantiate a `FormControl`.
|
||||
|
||||
Note: for performance reasons, Angular only runs async validators if all sync validators pass. Each must complete before errors are set.
|
||||
|
||||
### Built-in validators
|
||||
|
||||
You can choose to [write your own validator functions](guide/form-validation#custom-validators), or you can use some of
|
||||
Angular's built-in validators.
|
||||
You can choose to [write your own validator functions](guide/form-validation#custom-validators), or you can use some of
|
||||
Angular's built-in validators.
|
||||
|
||||
The same built-in validators that are available as attributes in template-driven forms, such as `required` and `minlength`, are all available to use as functions from the `Validators` class. For a full list of built-in validators, see the [Validators](api/forms/Validators) API reference.
|
||||
|
||||
To update the hero form to be a reactive form, you can use some of the same
|
||||
To update the hero form to be a reactive form, you can use some of the same
|
||||
built-in validators—this time, in function form. See below:
|
||||
|
||||
{@a reactive-component-class}
|
||||
@ -98,31 +98,31 @@ built-in validators—this time, in function form. See below:
|
||||
Note that:
|
||||
|
||||
* The name control sets up two built-in validators—`Validators.required` and `Validators.minLength(4)`—and one custom validator, `forbiddenNameValidator`. For more details see the [Custom validators](guide/form-validation#custom-validators) section in this guide.
|
||||
* As these validators are all sync validators, you pass them in as the second argument.
|
||||
* As these validators are all sync validators, you pass them in as the second argument.
|
||||
* Support multiple validators by passing the functions in as an array.
|
||||
* This example adds a few getter methods. In a reactive form, you can always access any form control through the `get` method on its parent group, but sometimes it's useful to define getters as shorthands
|
||||
* This example adds a few getter methods. In a reactive form, you can always access any form control through the `get` method on its parent group, but sometimes it's useful to define getters as shorthands
|
||||
for the template.
|
||||
|
||||
|
||||
If you look at the template for the name input again, it is fairly similar to the template-driven example.
|
||||
If you look at the template for the name input again, it is fairly similar to the template-driven example.
|
||||
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="name-with-error-msg" title="reactive/hero-form-reactive.component.html (name with error msg)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Key takeaways:
|
||||
|
||||
* The form no longer exports any directives, and instead uses the `name` getter defined in
|
||||
|
||||
* The form no longer exports any directives, and instead uses the `name` getter defined in
|
||||
the component class.
|
||||
* The `required` attribute is still present. While it's not necessary for validation purposes,
|
||||
* The `required` attribute is still present. While it's not necessary for validation purposes,
|
||||
you may want to keep it in your template for CSS styling or accessibility reasons.
|
||||
|
||||
|
||||
## Custom validators
|
||||
|
||||
Since the built-in validators won't always match the exact use case of your application, sometimes you'll want to create a custom validator.
|
||||
Since the built-in validators won't always match the exact use case of your application, sometimes you'll want to create a custom validator.
|
||||
|
||||
Consider the `forbiddenNameValidator` function from previous
|
||||
[examples](guide/form-validation#reactive-component-class) in
|
||||
[examples](guide/form-validation#reactive-component-class) in
|
||||
this guide. Here's what the definition of that function looks like:
|
||||
|
||||
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" title="shared/forbidden-name.directive.ts (forbiddenNameValidator)" linenums="false">
|
||||
@ -145,7 +145,7 @@ at which point the form uses the last value emitted for validation.
|
||||
|
||||
### Adding to reactive forms
|
||||
|
||||
In reactive forms, custom validators are fairly simple to add. All you have to do is pass the function directly
|
||||
In reactive forms, custom validators are fairly simple to add. All you have to do is pass the function directly
|
||||
to the `FormControl`.
|
||||
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="custom-validator" title="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
|
||||
@ -153,7 +153,7 @@ to the `FormControl`.
|
||||
|
||||
### Adding to template-driven forms
|
||||
|
||||
In template-driven forms, you don't have direct access to the `FormControl` instance, so you can't pass the
|
||||
In template-driven forms, you don't have direct access to the `FormControl` instance, so you can't pass the
|
||||
validator in like you can for reactive forms. Instead, you need to add a directive to the template.
|
||||
|
||||
The corresponding `ForbiddenValidatorDirective` serves as a wrapper around the `forbiddenNameValidator`.
|
||||
@ -164,8 +164,8 @@ with the `NG_VALIDATORS` provider, a provider with an extensible collection of v
|
||||
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" title="shared/forbidden-name.directive.ts (providers)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The directive class then implements the `Validator` interface, so that it can easily integrate
|
||||
with Angular forms. Here is the rest of the directive to help you get an idea of how it all
|
||||
The directive class then implements the `Validator` interface, so that it can easily integrate
|
||||
with Angular forms. Here is the rest of the directive to help you get an idea of how it all
|
||||
comes together:
|
||||
|
||||
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive" title="shared/forbidden-name.directive.ts (directive)">
|
||||
@ -201,14 +201,14 @@ Like in AngularJS, Angular automatically mirrors many control properties onto th
|
||||
* `.ng-untouched`
|
||||
* `.ng-touched`
|
||||
|
||||
The hero form uses the `.ng-valid` and `.ng-invalid` classes to
|
||||
The hero form uses the `.ng-valid` and `.ng-invalid` classes to
|
||||
set the color of each form control's border.
|
||||
|
||||
<code-example path="form-validation/src/assets/forms.css" title="forms.css (status classes)">
|
||||
|
||||
</code-example>
|
||||
|
||||
## Cross field validation
|
||||
## Cross field validation
|
||||
This section shows how to perform cross field validation. It assumes some basic knowledge of creating custom validators.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@ -217,7 +217,7 @@ If you haven't created custom validators before, start by reviewing the [custom
|
||||
|
||||
</div>
|
||||
|
||||
In the following section, we will make sure that our heroes do not reveal their true identities by filling out the Hero Form. We will do that by validating that the hero names and alter egos do not match.
|
||||
In the following section, we will make sure that our heroes do not reveal their true identities by filling out the Hero Form. We will do that by validating that the hero names and alter egos do not match.
|
||||
|
||||
### Adding to reactive forms
|
||||
|
||||
@ -250,7 +250,7 @@ The validator code is as follows:
|
||||
|
||||
The identity validator implements the `ValidatorFn` interface. It takes an Angular control object as an argument and returns either null if the form is valid, or `ValidationErrors` otherwise.
|
||||
|
||||
First we retrieve the child controls by calling the `FormGroup`'s [get](api/forms/AbstractControl#get) method. Then we simply compare the values of the `name` and `alterEgo` controls.
|
||||
First we retrieve the child controls by calling the `FormGroup`'s [get](api/forms/AbstractControl#get) method. Then we simply compare the values of the `name` and `alterEgo` controls.
|
||||
|
||||
If the values do not match, the hero's identity remains secret, and we can safely return null. Otherwise, the hero's identity is revealed and we must mark the form as invalid by returning an error object.
|
||||
|
||||
@ -259,7 +259,7 @@ Next, to provide better user experience, we show an appropriate error message wh
|
||||
</code-example>
|
||||
|
||||
Note that we check if:
|
||||
- the `FormGroup` has the cross validation error returned by the `identityRevealed` validator,
|
||||
- the `FormGroup` has the cross validation error returned by the `identityRevealed` validator,
|
||||
- the user is yet to [interact](guide/form-validation#why-check-dirty-and-touched) with the form.
|
||||
|
||||
### Adding to template driven forms
|
||||
@ -277,78 +277,11 @@ To provide better user experience, we show an appropriate error message when the
|
||||
</code-example>
|
||||
|
||||
Note that we check if:
|
||||
- the form has the cross validation error returned by the `identityRevealed` validator,
|
||||
- the form has the cross validation error returned by the `identityRevealed` validator,
|
||||
- the user is yet to [interact](guide/form-validation#why-check-dirty-and-touched) with the form.
|
||||
|
||||
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.
|
||||
|
||||
## 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.**
|
||||
|
@ -1263,7 +1263,7 @@ But it is occasionally necessary.
|
||||
For example, you can't call `async` or `fakeAsync` when testing
|
||||
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.
|
||||
|
||||
<code-example
|
||||
|
@ -661,12 +661,6 @@
|
||||
"Workshops & Onsite Training": {
|
||||
"order": 2,
|
||||
"resources": {
|
||||
"-acceleb": {
|
||||
"desc": "Customized, Instructor-Led Angular Training",
|
||||
"rev": true,
|
||||
"title": "Accelebrate",
|
||||
"url": "https://www.accelebrate.com/angular-training"
|
||||
},
|
||||
"-KLIBoFWStce29UCwkvY": {
|
||||
"desc": "Private Angular Training and Mentoring",
|
||||
"rev": true,
|
||||
|
@ -8,27 +8,11 @@ module.exports = function computeApiBreadCrumbs(API_DOC_TYPES_TO_RENDER) {
|
||||
if (API_DOC_TYPES_TO_RENDER.indexOf(doc.docType) !== -1) {
|
||||
doc.breadCrumbs = [];
|
||||
doc.breadCrumbs.push({ text: 'API', path: '/api' });
|
||||
if (isSecondaryEntryPoint(doc)) {
|
||||
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 });
|
||||
}
|
||||
if (doc.moduleDoc) doc.breadCrumbs.push({ text: '@angular/' + doc.moduleDoc.id, path: doc.moduleDoc.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 };
|
||||
}
|
||||
|
@ -17,17 +17,11 @@ describe('angular-api-package: computeApiBreadCrumbs processor', () => {
|
||||
const API_DOC_TYPES_TO_RENDER = ['class', 'interface', 'package'];
|
||||
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 = [
|
||||
{ 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: 'guide', name: 'Guide One', path: 'guide/guide-1' },
|
||||
httpPackage,
|
||||
httpTestingPackage,
|
||||
testRequestClass
|
||||
{ docType: 'package', name: 'testing', id: 'http/testing', path: 'http/testing' },
|
||||
];
|
||||
processor.$process(docs);
|
||||
|
||||
@ -44,18 +38,7 @@ describe('angular-api-package: computeApiBreadCrumbs processor', () => {
|
||||
expect(docs[2].breadCrumbs).toBeUndefined();
|
||||
expect(docs[3].breadCrumbs).toEqual([
|
||||
{ text: 'API', path: '/api' },
|
||||
{ text: '@angular/http', path: 'http' },
|
||||
]);
|
||||
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' },
|
||||
{ text: 'testing', path: 'http/testing' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
{% import "lib/githubLinks.html" as github -%}
|
||||
{% set comma = joiner(',') %}
|
||||
{% set breadcrumbDelimiter = joiner('>') %}
|
||||
{% set slash = joiner('/') %}
|
||||
<article>
|
||||
{$ github.githubLinks(doc, versionInfo) $}
|
||||
<div class="breadcrumb">
|
||||
@ -14,11 +14,14 @@
|
||||
]
|
||||
}
|
||||
</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>
|
||||
<header class="api-header">
|
||||
<h1>{$ doc.name $}</h1>
|
||||
<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 %}
|
||||
</header>
|
||||
<aio-toc class="embedded"></aio-toc>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{% extends 'base.template.html' -%}
|
||||
|
||||
{% macro listItems(items, title, overridePath) %}
|
||||
{% macro listItems(items, title) %}
|
||||
{% if items.length %}
|
||||
<section class="export-list">
|
||||
<h3>{$ title $}</h3>
|
||||
@ -8,7 +8,7 @@
|
||||
{% for item in items %}
|
||||
<tr>
|
||||
<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>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@ -24,13 +24,12 @@
|
||||
|
||||
{% include "includes/see-also.html" %}
|
||||
|
||||
{% if doc.isPrimaryPackage %}
|
||||
<h2>Entry points</h2>
|
||||
{$ listItems([doc.packageInfo.primary], 'Primary', '#primary-entry-point-exports') $}
|
||||
{$ listItems([doc.packageInfo.primary], 'Primary') $}
|
||||
{$ 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.decorators, 'Decorators') $}
|
||||
{$ listItems(doc.functions, 'Functions') $}
|
||||
|
13
build.sh
13
build.sh
@ -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
|
||||
cd ${currentDir}
|
||||
|
||||
PACKAGES=(compiler
|
||||
core
|
||||
PACKAGES=(core
|
||||
compiler
|
||||
common
|
||||
animations
|
||||
platform-browser
|
||||
@ -27,8 +27,7 @@ PACKAGES=(compiler
|
||||
service-worker
|
||||
elements)
|
||||
|
||||
TSC_PACKAGES=(compiler
|
||||
compiler-cli
|
||||
TSC_PACKAGES=(compiler-cli
|
||||
language-service
|
||||
benchpress)
|
||||
|
||||
@ -240,13 +239,7 @@ compilePackage() {
|
||||
# For TSC_PACKAGES items
|
||||
if containsElement "${3}" "${TSC_PACKAGES[@]}"; then
|
||||
echo "====== [${3}]: COMPILING: ${TSC} -p ${1}/tsconfig-build.json"
|
||||
local package_name=$(basename "${2}")
|
||||
$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
|
||||
echo "====== [${3}]: COMPILING: ${NGC} -p ${1}/tsconfig-build.json"
|
||||
local package_name=$(basename "${2}")
|
||||
|
@ -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
|
||||
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
|
||||
<a id="debugging"></a>
|
||||
|
||||
- Open chrome at: [chrome://inspect](chrome://inspect)
|
||||
- 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`
|
||||
- Add the following to the `configurations` array:
|
||||
|
||||
```json
|
||||
```
|
||||
{
|
||||
"name": "Attach (inspect)",
|
||||
"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
|
||||
and launch the bazel corresponding test (`bazel test <target> --config=debug`).
|
||||
|
||||
|
@ -14,17 +14,11 @@ Ctrl + Shift + j.
|
||||
By default the debug tools are disabled. You can enable debug tools as follows:
|
||||
|
||||
```typescript
|
||||
import {ApplicationRef} from '@angular/core';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
import {enableDebugTools} from '@angular/platform-browser';
|
||||
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.then(moduleRef => {
|
||||
const applicationRef = moduleRef.injector.get(ApplicationRef);
|
||||
const appComponent = applicationRef.components[0];
|
||||
enableDebugTools(appComponent);
|
||||
})
|
||||
bootstrap(Application).then((appRef) => {
|
||||
enableDebugTools(appRef);
|
||||
});
|
||||
```
|
||||
|
||||
### Using debug tools
|
||||
|
@ -24,8 +24,9 @@ filegroup(
|
||||
|
||||
ANGULAR_TESTING = [
|
||||
"node_modules/@angular/*/bundles/*-testing.umd.js",
|
||||
# We use AOT, so the dynamic platform-browser should be visible only in tests
|
||||
# NOTE that we still need to include the compiler because the core depends on it
|
||||
# We use AOT, so the compiler and the dynamic platform-browser should be
|
||||
# visible only in tests
|
||||
"node_modules/@angular/compiler/bundles/*.umd.js",
|
||||
"node_modules/@angular/platform-browser-dynamic/bundles/*.umd.js",
|
||||
]
|
||||
|
||||
|
@ -20,16 +20,16 @@ http_archive(
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_webtesting",
|
||||
url = "https://github.com/bazelbuild/rules_webtesting/archive/0.2.1.zip",
|
||||
strip_prefix = "rules_webtesting-0.2.1",
|
||||
sha256 = "7d490aadff9b5262e5251fa69427ab2ffd1548422467cb9f9e1d110e2c36f0fa",
|
||||
url = "https://github.com/bazelbuild/rules_webtesting/archive/8fd9ce0fd9254bde251da0bc373d6cd08e811434.zip",
|
||||
strip_prefix = "rules_webtesting-8fd9ce0fd9254bde251da0bc373d6cd08e811434",
|
||||
sha256 = "4baee95fcfadfbaf868707af8accfd1cb98c5d13f808908e0152468bfb47f0f7",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "build_bazel_rules_typescript",
|
||||
url = "https://github.com/bazelbuild/rules_typescript/archive/0.16.0.zip",
|
||||
strip_prefix = "rules_typescript-0.16.0",
|
||||
sha256 = "e65c5639a42e2f6d3f9d2bda62487d6b42734830dda45be1620c3e2b1115070c",
|
||||
url = "https://github.com/bazelbuild/rules_typescript/archive/1d9a4b0087f307e31af91e2b221a6447288994c6.zip",
|
||||
strip_prefix = "rules_typescript-1d9a4b0087f307e31af91e2b221a6447288994c6",
|
||||
sha256 = "e17ac3f33d5d3cd2a0c385c4fd28b814d0ad46c6c67ccaef97160be99d7a24eb",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
|
@ -19,9 +19,6 @@ node_modules/zone.js/dist/zone_externs.js
|
||||
--js node_modules/rxjs/operators/package.json
|
||||
--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/fesm2015/core.js
|
||||
--js node_modules/@angular/core/src/testability/testability.externs.js
|
||||
|
@ -17,9 +17,6 @@ node_modules/zone.js/dist/zone_externs.js
|
||||
--module_resolution=node
|
||||
--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/fesm2015/core.js
|
||||
--js node_modules/@angular/core/src/testability/testability.externs.js
|
||||
|
@ -9,7 +9,6 @@
|
||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||
"@angular/core": "file:../../dist/packages-dist/core",
|
||||
"@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",
|
||||
"google-closure-compiler": "git+https://github.com/alexeagle/closure-compiler.git#packagejson.dist",
|
||||
"rxjs": "file:../../node_modules/rxjs",
|
||||
@ -31,4 +30,4 @@
|
||||
"preprotractor": "tsc -p e2e",
|
||||
"protractor": "protractor e2e/protractor.config.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,48 +3,40 @@
|
||||
|
||||
|
||||
"@angular/animations@file:../../dist/packages-dist/animations":
|
||||
version "7.0.0-beta.1-d2d510089c"
|
||||
version "6.0.0-beta.7-8203e0365a"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/common@file:../../dist/packages-dist/common":
|
||||
version "7.0.0-beta.1-d2d510089c"
|
||||
version "6.0.0-beta.7-8203e0365a"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli":
|
||||
version "7.0.0-beta.1-d2d510089c"
|
||||
version "6.0.0-beta.7-8203e0365a"
|
||||
dependencies:
|
||||
chokidar "^1.4.2"
|
||||
convert-source-map "^1.5.1"
|
||||
magic-string "^0.25.0"
|
||||
minimist "^1.2.0"
|
||||
reflect-metadata "^0.1.2"
|
||||
source-map "^0.6.1"
|
||||
tsickle "^0.32.1"
|
||||
tsickle "^0.27.2"
|
||||
|
||||
"@angular/compiler@file:../../dist/packages-dist/compiler":
|
||||
version "7.0.0-beta.1-d2d510089c"
|
||||
version "6.0.0-beta.7-8203e0365a"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/core@file:../../dist/packages-dist/core":
|
||||
version "7.0.0-beta.1-d2d510089c"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic":
|
||||
version "7.0.0-beta.1-d2d510089c"
|
||||
version "6.0.0-beta.7-8203e0365a"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/platform-browser@file:../../dist/packages-dist/platform-browser":
|
||||
version "7.0.0-beta.1-d2d510089c"
|
||||
version "6.0.0-beta.7-8203e0365a"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
"@angular/platform-server@file:../../dist/packages-dist/platform-server":
|
||||
version "7.0.0-beta.1-d2d510089c"
|
||||
version "6.0.0-beta.7-8203e0365a"
|
||||
dependencies:
|
||||
domino "^2.0.1"
|
||||
tslib "^1.9.0"
|
||||
@ -518,10 +510,6 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
|
||||
version "1.1.0"
|
||||
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:
|
||||
version "0.3.1"
|
||||
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"
|
||||
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:
|
||||
version "2.0.1"
|
||||
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"
|
||||
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:
|
||||
version "2.8.0"
|
||||
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"
|
||||
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:
|
||||
version "2.3.11"
|
||||
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"
|
||||
|
||||
"rxjs@file:../../node_modules/rxjs":
|
||||
version "6.0.0"
|
||||
version "6.0.0-alpha.4"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
@ -2044,14 +2016,10 @@ source-map@^0.5.1, source-map@^0.5.6:
|
||||
version "0.5.7"
|
||||
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"
|
||||
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:
|
||||
version "0.0.2"
|
||||
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"
|
||||
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36"
|
||||
|
||||
tsickle@^0.32.1:
|
||||
version "0.32.1"
|
||||
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.32.1.tgz#f16e94ba80b32fc9ebe320dc94fbc2ca7f3521a5"
|
||||
tsickle@^0.27.2:
|
||||
version "0.27.2"
|
||||
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.27.2.tgz#f33d46d046f73dd5c155a37922e422816e878736"
|
||||
dependencies:
|
||||
jasmine-diff "^0.1.3"
|
||||
minimist "^1.2.0"
|
||||
mkdirp "^0.5.1"
|
||||
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"
|
||||
|
||||
"typescript@file:../../node_modules/typescript":
|
||||
version "2.9.2"
|
||||
version "2.7.2"
|
||||
|
||||
ua-parser-js@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"
|
||||
|
||||
"zone.js@file:../../node_modules/zone.js":
|
||||
version "0.8.26"
|
||||
version "0.8.20"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "7.0.0-beta.2",
|
||||
"version": "7.0.0-beta.1",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
|
@ -9,42 +9,31 @@ import {AnimationMetadata, AnimationOptions} from './animation_metadata';
|
||||
import {AnimationPlayer} from './players/animation_player';
|
||||
|
||||
/**
|
||||
* An injectable service that produces an animation sequence programmatically within an
|
||||
* Angular component or directive.
|
||||
* Provided by the `BrowserAnimationsModule` or `NoopAnimationsModule`.
|
||||
* AnimationBuilder is an injectable service that is available when the {@link
|
||||
* BrowserAnimationsModule BrowserAnimationsModule} or {@link 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.
|
||||
* The service is instantiated along with your component.
|
||||
*
|
||||
* 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:
|
||||
* Programmatic animations are first built and then a player is created when the build animation is
|
||||
* attached to an element.
|
||||
*
|
||||
* ```ts
|
||||
* // import the service from BrowserAnimationsModule
|
||||
* // remember to include the BrowserAnimationsModule module for this to work...
|
||||
* import {AnimationBuilder} from '@angular/animations';
|
||||
* // require the service as a dependency
|
||||
*
|
||||
* class MyCmp {
|
||||
* constructor(private _builder: AnimationBuilder) {}
|
||||
*
|
||||
* makeAnimation(element: any) {
|
||||
* // first define a reusable animation
|
||||
* // first build the animation
|
||||
* const myAnimation = this._builder.build([
|
||||
* style({ width: 0 }),
|
||||
* 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);
|
||||
*
|
||||
* 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 {
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
@ -38,33 +38,11 @@
|
||||
* @experimental Animation support is experimental.
|
||||
*/
|
||||
export interface AnimationEvent {
|
||||
/**
|
||||
* The name of the state from which the animation is triggered.
|
||||
*/
|
||||
fromState: string;
|
||||
/**
|
||||
* The name of the state in which the animation completes.
|
||||
*/
|
||||
toState: string;
|
||||
/**
|
||||
* The time it takes the animation to complete, in milliseconds.
|
||||
*/
|
||||
totalTime: number;
|
||||
/**
|
||||
* The animation phase in which the callback was invoked, one of
|
||||
* "start" or "done".
|
||||
*/
|
||||
phaseName: string;
|
||||
/**
|
||||
* The element to which the animation is attached.
|
||||
*/
|
||||
element: any;
|
||||
/**
|
||||
* Internal.
|
||||
*/
|
||||
triggerName: string;
|
||||
/**
|
||||
* Internal.
|
||||
*/
|
||||
disabled: boolean;
|
||||
}
|
||||
|
@ -9,14 +9,6 @@
|
||||
import {scheduleMicroTask} from '../util';
|
||||
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 {
|
||||
private _onDoneFns: Function[] = [];
|
||||
private _onStartFns: Function[] = [];
|
||||
|
@ -8,110 +8,37 @@
|
||||
import {scheduleMicroTask} from '../util';
|
||||
|
||||
/**
|
||||
* Provides programmatic control of a reusable animation sequence,
|
||||
* built using the `build()` method of `AnimationBuilder`. The `build()` method
|
||||
* returns a factory, whose `create()` method instantiates and initializes this interface.
|
||||
*
|
||||
* @see `AnimationBuilder`
|
||||
* @see `AnimationFactory`
|
||||
* @see `animate()`
|
||||
* AnimationPlayer controls an animation sequence that was produced from a programmatic animation.
|
||||
* (see {@link AnimationBuilder AnimationBuilder} for more information on how to create programmatic
|
||||
* animations.)
|
||||
*
|
||||
* @experimental Animation support is experimental.
|
||||
*/
|
||||
export interface AnimationPlayer {
|
||||
/**
|
||||
* Provides a callback to invoke when the animation finishes.
|
||||
* @param fn The callback function.
|
||||
* @see `finish()`
|
||||
*/
|
||||
onDone(fn: () => void): void;
|
||||
/**
|
||||
* Provides a callback to invoke when the animation starts.
|
||||
* @param fn The callback function.
|
||||
* @see `run()`
|
||||
*/
|
||||
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;
|
||||
/**
|
||||
* Initializes the animation.
|
||||
*/
|
||||
init(): void;
|
||||
/**
|
||||
* Reports whether the animation has started.
|
||||
* @returns True if the animation has started, false otherwise.
|
||||
*/
|
||||
hasStarted(): boolean;
|
||||
/**
|
||||
* Runs the animation, invoking the `onStart()` callback.
|
||||
*/
|
||||
play(): void;
|
||||
/**
|
||||
* Pauses the animation.
|
||||
*/
|
||||
pause(): void;
|
||||
/**
|
||||
* Restarts the paused animation.
|
||||
*/
|
||||
restart(): void;
|
||||
/**
|
||||
* Ends the animation, invoking the `onDone()` callback.
|
||||
*/
|
||||
finish(): void;
|
||||
/**
|
||||
* Destroys the animation, after invoking the `beforeDestroy()` callback.
|
||||
* Calls the `onDestroy()` callback when destruction is completed.
|
||||
*/
|
||||
destroy(): void;
|
||||
/**
|
||||
* Resets the animation to its initial state.
|
||||
*/
|
||||
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;
|
||||
/**
|
||||
* Reports the current position of the animation.
|
||||
* @returns A 0-based offset into the duration, in milliseconds.
|
||||
*/
|
||||
getPosition(): number;
|
||||
/**
|
||||
* The parent of this player, if any.
|
||||
*/
|
||||
parentPlayer: AnimationPlayer|null;
|
||||
/**
|
||||
* The total run time of the animation, in milliseconds.
|
||||
*/
|
||||
readonly totalTime: number;
|
||||
/**
|
||||
* Provides a callback to invoke before the animation is destroyed.
|
||||
*/
|
||||
beforeDestroy?: () => any;
|
||||
/** @internal
|
||||
* Internal
|
||||
*/
|
||||
/** @internal */
|
||||
triggerCallback?: (phaseName: string) => void;
|
||||
/** @internal
|
||||
* Internal
|
||||
*/
|
||||
/** @internal */
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* An empty programmatic controller for reusable animations.
|
||||
* 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`
|
||||
*
|
||||
* @experimental Animation support is experimental.
|
||||
*/
|
||||
export class NoopAnimationPlayer implements AnimationPlayer {
|
||||
private _onDoneFns: Function[] = [];
|
||||
|
@ -176,7 +176,7 @@ def _expected_outs(ctx):
|
||||
else:
|
||||
devmode_js = [".js"]
|
||||
if not _is_bazel():
|
||||
devmode_js += [".ngfactory.js"]
|
||||
devmode_js += ".ngfactory.js"
|
||||
summaries = []
|
||||
metadata = []
|
||||
elif include_ng_files and short_path.endswith(".css"):
|
||||
|
@ -6,7 +6,6 @@
|
||||
"Install toolchain dependencies"
|
||||
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "yarn_install")
|
||||
load("@build_bazel_rules_typescript//:defs.bzl", "check_rules_typescript_version")
|
||||
|
||||
def ng_setup_workspace():
|
||||
"""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",
|
||||
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")
|
||||
|
@ -13,6 +13,10 @@ load(
|
||||
_compile_ts = "compile_ts",
|
||||
_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
|
||||
COMMON_ATTRIBUTES = _COMMON_ATTRIBUTES
|
||||
@ -20,3 +24,4 @@ COMMON_OUTPUTS = _COMMON_OUTPUTS
|
||||
compile_ts = _compile_ts
|
||||
DEPS_ASPECTS = _DEPS_ASPECTS
|
||||
ts_providers_dict_to_struct = _ts_providers_dict_to_struct
|
||||
json_marshal = _json_marshal
|
||||
|
@ -229,8 +229,14 @@ describe('@angular/core ng_package', () => {
|
||||
expect(shx.cat('testing.metadata.json'))
|
||||
.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', () => {
|
||||
it('should have a fesm15 file in the /fesm2015 directory',
|
||||
() => { expect(shx.cat('fesm2015/testing.js')).toContain(`export {`); });
|
||||
|
@ -77,12 +77,13 @@ export class Analyzer {
|
||||
|
||||
protected analyzeClass(file: ts.SourceFile, pool: ConstantPool, clazz: ParsedClass): AnalyzedClass
|
||||
|undefined {
|
||||
const matchingHandlers = this.handlers
|
||||
.map(handler => ({
|
||||
handler,
|
||||
match: handler.detect(clazz.declaration, clazz.decorators),
|
||||
}))
|
||||
.filter(isMatchingHandler);
|
||||
const matchingHandlers =
|
||||
this.handlers
|
||||
.map(handler => ({
|
||||
handler,
|
||||
match: handler.detect(clazz.declaration, clazz.decorators),
|
||||
}))
|
||||
.filter(isMatchingHandler);
|
||||
|
||||
if (matchingHandlers.length > 1) {
|
||||
throw new Error('TODO.Diagnostic: Class has multiple Angular decorators.');
|
||||
|
@ -168,7 +168,7 @@ export class ComponentDecoratorHandler implements DecoratorHandler<R3ComponentMe
|
||||
return {
|
||||
name: 'ngComponentDef',
|
||||
initializer: res.expression,
|
||||
statements: res.statements,
|
||||
statements: [],
|
||||
type: res.type,
|
||||
};
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ export class SelectorScopeRegistry {
|
||||
// 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
|
||||
// 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;
|
||||
|
||||
// Either the node represents a directive or a pipe. Look for both.
|
||||
@ -183,15 +183,6 @@ export class SelectorScopeRegistry {
|
||||
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.
|
||||
*
|
||||
@ -199,8 +190,7 @@ export class SelectorScopeRegistry {
|
||||
* (`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.
|
||||
*/
|
||||
private lookupScopes(node: ts.Declaration, ngModuleImportedFrom: string|null): SelectorScopes
|
||||
|null {
|
||||
private lookupScopes(node: ts.Declaration, ngModuleImportedFrom: string|null): SelectorScopes {
|
||||
let data: ModuleData|null = null;
|
||||
|
||||
// Either this module was analyzed directly, or has a precompiled ngModuleDef.
|
||||
@ -216,7 +206,7 @@ export class SelectorScopeRegistry {
|
||||
}
|
||||
|
||||
if (data === null) {
|
||||
return null;
|
||||
throw new Error(`Module not registered: ${reflectNameOfDeclaration(node)}`);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -224,19 +214,18 @@ export class SelectorScopeRegistry {
|
||||
...data.declarations,
|
||||
// Expand imports to the exported scope of those imports.
|
||||
...flatten(data.imports.map(
|
||||
ref => this.lookupScopesOrDie(ref.node as ts.Declaration, absoluteModuleName(ref))
|
||||
.exported)),
|
||||
ref =>
|
||||
this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref)).exported)),
|
||||
// And include the compilation scope of exported modules.
|
||||
...flatten(
|
||||
data.exports
|
||||
.map(ref => this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref)))
|
||||
.filter((scope: SelectorScopes | null): scope is SelectorScopes => scope !== null)
|
||||
.map(scope => scope.exported))
|
||||
data.exports.filter(ref => this._moduleToData.has(ref.node as ts.Declaration))
|
||||
.map(
|
||||
ref => this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref))
|
||||
.exported))
|
||||
],
|
||||
exported: flatten(data.exports.map(ref => {
|
||||
const scope = this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref));
|
||||
if (scope !== null) {
|
||||
return scope.exported;
|
||||
if (this._moduleToData.has(ref.node as ts.Declaration)) {
|
||||
return this.lookupScopes(ref.node as ts.Declaration, absoluteModuleName(ref)).exported;
|
||||
} else {
|
||||
return [ref];
|
||||
}
|
||||
|
@ -27,22 +27,22 @@ describe('SelectorScopeRegistry', () => {
|
||||
{
|
||||
name: 'node_modules/some_library/index.d.ts',
|
||||
contents: `
|
||||
import {NgModuleDef} from '@angular/core';
|
||||
import {NgComponentDef, NgModuleDef} from '@angular/core';
|
||||
import * as i0 from './component';
|
||||
|
||||
export declare class SomeModule {
|
||||
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',
|
||||
contents: `
|
||||
import {NgComponentDef} from '@angular/core';
|
||||
|
||||
export declare class SomeCmp {
|
||||
static ngComponentDef: NgComponentDef<SomeCmp, 'some-cmp'>;
|
||||
}
|
||||
export declare class SomeCmp {}
|
||||
`
|
||||
},
|
||||
{
|
||||
@ -76,64 +76,6 @@ describe('SelectorScopeRegistry', () => {
|
||||
const scope = registry.lookupCompilationScope(ProgramCmp) !;
|
||||
expect(scope).toBeDefined();
|
||||
expect(scope.directives).toBeDefined();
|
||||
expect(scope.directives.size).toBe(2);
|
||||
});
|
||||
|
||||
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);
|
||||
expect(scope.directives.size).toBe(1);
|
||||
});
|
||||
});
|
@ -16,7 +16,7 @@ import {NgtscProgram} from '../../src/ngtsc/program';
|
||||
|
||||
const IDENTIFIER = /[A-Za-z_$ɵ][A-Za-z0-9_$]*/;
|
||||
const OPERATOR =
|
||||
/!|\?|%|\*|\/|\^|&&?|\|\|?|\(|\)|\{|\}|\[|\]|:|;|<=?|>=?|={1,3}|!==?|=>|\+\+?|--?|@|,|\.|\.\.\./;
|
||||
/!|%|\*|\/|\^|&&?|\|\|?|\(|\)|\{|\}|\[|\]|:|;|<=?|>=?|={1,3}|!==?|=>|\+\+?|--?|@|,|\.|\.\.\./;
|
||||
const STRING = /'[^']*'|"[^"]*"|`[\s\S]*?`/;
|
||||
const NUMBER = /\d+/;
|
||||
|
||||
|
@ -367,10 +367,10 @@ describe('compiler compliance', () => {
|
||||
$r3$.ɵrS(10);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵp(0, "ternary", $r3$.ɵb((ctx.cond ? $r3$.ɵf1(6, _c0, ctx.a): _c1)));
|
||||
$r3$.ɵp(0, "pipe", $r3$.ɵb($r3$.ɵpb3(1, 4, ctx.value, 1, 2)));
|
||||
$r3$.ɵp(0, "and", $r3$.ɵb((ctx.cond && $r3$.ɵf1(8, _c0, ctx.b))));
|
||||
$r3$.ɵp(0, "or", $r3$.ɵb((ctx.cond || $r3$.ɵf1(10, _c0, ctx.c))));
|
||||
$r3$.ɵp(0, "ternary", $r3$.ɵb((ctx.cond ? $r3$.ɵf1(2, _c0, ctx.a): _c1)));
|
||||
$r3$.ɵp(0, "pipe", $r3$.ɵb($r3$.ɵpb3(6, 1, ctx.value, 1, 2)));
|
||||
$r3$.ɵp(0, "and", $r3$.ɵb((ctx.cond && $r3$.ɵf1(4, _c0, ctx.b))));
|
||||
$r3$.ɵp(0, "or", $r3$.ɵb((ctx.cond || $r3$.ɵf1(6, _c0, ctx.c))));
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -52,12 +52,5 @@ function getHookName(hook: LifecycleHooks): string {
|
||||
return 'ngAfterViewInit';
|
||||
case LifecycleHooks.AfterViewChecked:
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
|
@ -115,13 +115,16 @@ export class CompileMetadataResolver {
|
||||
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`;
|
||||
if (dirType instanceof StaticSymbol) {
|
||||
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 {
|
||||
|
@ -181,8 +181,7 @@ export function compileFactoryFunction(meta: R3FactoryMetadata):
|
||||
} else {
|
||||
const baseFactory = o.variable(`ɵ${meta.name}_BaseFactory`);
|
||||
const getInheritedFactory = o.importExpr(R3.getInheritedFactory);
|
||||
const baseFactoryStmt = baseFactory.set(getInheritedFactory.callFn([meta.type]))
|
||||
.toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]);
|
||||
const baseFactoryStmt = baseFactory.set(getInheritedFactory.callFn([meta.type])).toDeclStmt();
|
||||
statements.push(baseFactoryStmt);
|
||||
|
||||
// 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)) {
|
||||
throw new Error(`Illegal state: compiling factory that delegates to itself`);
|
||||
}
|
||||
const delegateFactoryStmt = delegateFactory.set(getFactoryOf.callFn([meta.delegate]))
|
||||
.toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]);
|
||||
const delegateFactoryStmt =
|
||||
delegateFactory.set(getFactoryOf.callFn([meta.delegate])).toDeclStmt();
|
||||
|
||||
statements.push(delegateFactoryStmt);
|
||||
const r = makeConditionalFactory(delegateFactory.callFn([]));
|
||||
|
@ -940,8 +940,7 @@ function needsAdditionalRootNode(astNodes: TemplateAst[]): boolean {
|
||||
|
||||
|
||||
function elementBindingDef(inputAst: BoundElementPropertyAst, dirAst: DirectiveAst): o.Expression {
|
||||
const inputType = inputAst.type;
|
||||
switch (inputType) {
|
||||
switch (inputAst.type) {
|
||||
case PropertyBindingType.Attribute:
|
||||
return o.literalArr([
|
||||
o.literal(BindingFlags.TypeElementAttribute), o.literal(inputAst.name),
|
||||
@ -966,13 +965,6 @@ function elementBindingDef(inputAst: BoundElementPropertyAst, dirAst: DirectiveA
|
||||
return o.literalArr([
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,6 @@ export {
|
||||
detectChanges as ɵdetectChanges,
|
||||
renderComponent as ɵrenderComponent,
|
||||
ComponentType as ɵComponentType,
|
||||
ComponentFactory as ɵRender3ComponentFactory,
|
||||
ComponentRef as ɵRender3ComponentRef,
|
||||
DirectiveType as ɵDirectiveType,
|
||||
RenderFlags as ɵRenderFlags,
|
||||
directiveInject as ɵdirectiveInject,
|
||||
@ -32,7 +30,6 @@ export {
|
||||
InheritDefinitionFeature as ɵInheritDefinitionFeature,
|
||||
NgOnChangesFeature as ɵNgOnChangesFeature,
|
||||
NgModuleType as ɵNgModuleType,
|
||||
NgModuleRef as ɵRender3NgModuleRef,
|
||||
CssSelectorList as ɵCssSelectorList,
|
||||
markDirty as ɵmarkDirty,
|
||||
NgModuleFactory as ɵNgModuleFactory,
|
||||
@ -100,7 +97,6 @@ export {
|
||||
Pp as ɵPp,
|
||||
BaseDef as ɵBaseDef,
|
||||
ComponentDef as ɵComponentDef,
|
||||
ComponentDefInternal as ɵComponentDefInternal,
|
||||
DirectiveDef as ɵDirectiveDef,
|
||||
PipeDef as ɵPipeDef,
|
||||
whenRendered as ɵwhenRendered,
|
||||
@ -118,38 +114,14 @@ export {
|
||||
iM as ɵiM,
|
||||
I18nInstruction as ɵI18nInstruction,
|
||||
I18nExpInstruction as ɵI18nExpInstruction,
|
||||
WRAP_RENDERER_FACTORY2 as ɵWRAP_RENDERER_FACTORY2,
|
||||
Render3DebugRendererFactory2 as ɵRender3DebugRendererFactory2,
|
||||
} from './render3/index';
|
||||
|
||||
|
||||
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 {NgModuleDef as ɵNgModuleDef} from './metadata/ng_module';
|
||||
export {
|
||||
sanitizeHtml as ɵsanitizeHtml,
|
||||
sanitizeStyle as ɵsanitizeStyle,
|
||||
sanitizeUrl as ɵsanitizeUrl,
|
||||
sanitizeResourceUrl as ɵsanitizeResourceUrl,
|
||||
} from './sanitization/sanitization';
|
||||
|
||||
export {
|
||||
bypassSanitizationTrustHtml as ɵbypassSanitizationTrustHtml,
|
||||
bypassSanitizationTrustStyle as ɵbypassSanitizationTrustStyle,
|
||||
@ -157,4 +129,4 @@ export {
|
||||
bypassSanitizationTrustUrl as ɵbypassSanitizationTrustUrl,
|
||||
bypassSanitizationTrustResourceUrl as ɵbypassSanitizationTrustResourceUrl,
|
||||
} from './sanitization/bypass';
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
|
@ -9,8 +9,27 @@
|
||||
/**
|
||||
* 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),
|
||||
* please see [BAZEL.md](./docs/BAZEL.md).
|
||||
* Reexport from:
|
||||
* - `./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';
|
||||
|
||||
|
@ -27,7 +27,7 @@ export interface DirectiveDecorator {
|
||||
* runtime.
|
||||
*
|
||||
* 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
|
||||
|
@ -106,7 +106,7 @@ export function renderComponent<T>(
|
||||
const rootContext = createRootContext(opts.scheduler || requestAnimationFrame.bind(window));
|
||||
|
||||
const rootView: LViewData = createLViewData(
|
||||
rendererFactory.createRenderer(hostNode, componentDef),
|
||||
rendererFactory.createRenderer(hostNode, componentDef.rendererType),
|
||||
createTView(-1, null, null, null, null), rootContext,
|
||||
componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways);
|
||||
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
|
||||
component = baseDirectiveCreate(0, componentDef.factory() as T, componentDef);
|
||||
rootContext.components.push(component);
|
||||
|
||||
(elementNode.data as LViewData)[CONTEXT] = component;
|
||||
initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !);
|
||||
|
||||
|
@ -6,12 +6,12 @@
|
||||
* 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 {Injector, inject} from '../di/injector';
|
||||
import {ComponentFactory as viewEngine_ComponentFactory, ComponentRef as viewEngine_ComponentRef} from '../linker/component_factory';
|
||||
import {ComponentFactoryResolver as viewEngine_ComponentFactoryResolver} from '../linker/component_factory_resolver';
|
||||
import {ElementRef as viewEngine_ElementRef} from '../linker/element_ref';
|
||||
import {ElementRef} from '../linker/element_ref';
|
||||
import {NgModuleRef as viewEngine_NgModuleRef} from '../linker/ng_module_factory';
|
||||
import {RendererFactory2} from '../render/api';
|
||||
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 {ComponentDefInternal, ComponentType, RenderFlags} from './interfaces/definition';
|
||||
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 {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
|
||||
* for the default `RootContext` found in the {@link ROOT_CONTEXT} token.
|
||||
*/
|
||||
export const SCHEDULER = new InjectionToken<((fn: () => void) => void)>('SCHEDULER_TOKEN', {
|
||||
providedIn: 'root',
|
||||
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');
|
||||
export const SCHEDULER = new InjectionToken<((fn: () => void) => void)>(
|
||||
'SCHEDULER_TOKEN', {providedIn: 'root', factory: () => requestAnimationFrame.bind(window)});
|
||||
|
||||
/**
|
||||
* Render3 implementation of {@link viewEngine_ComponentFactory}.
|
||||
@ -77,11 +65,9 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
||||
selector: string;
|
||||
componentType: Type<any>;
|
||||
ngContentSelectors: string[];
|
||||
|
||||
get inputs(): {propName: string; templateName: string;}[] {
|
||||
return toRefArray(this.componentDef.inputs);
|
||||
}
|
||||
|
||||
get outputs(): {propName: string; templateName: string;}[] {
|
||||
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> {
|
||||
const isInternalRootView = rootSelectorOrNode === undefined;
|
||||
|
||||
let rendererFactory: RendererFactory3;
|
||||
|
||||
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 rendererFactory =
|
||||
ngModule ? ngModule.injector.get(RendererFactory2) : domRendererFactory3;
|
||||
const hostNode = isInternalRootView ?
|
||||
elementCreate(this.selector, rendererFactory.createRenderer(null, this.componentDef)) :
|
||||
elementCreate(
|
||||
this.selector, rendererFactory.createRenderer(null, this.componentDef.rendererType)) :
|
||||
locateHostElement(rendererFactory, rootSelectorOrNode);
|
||||
|
||||
// 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.
|
||||
const rootView: LViewData = createLViewData(
|
||||
rendererFactory.createRenderer(hostNode, this.componentDef),
|
||||
rendererFactory.createRenderer(hostNode, this.componentDef.rendererType),
|
||||
createTView(-1, null, null, null, null), rootContext,
|
||||
this.componentDef.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways);
|
||||
rootView[INJECTOR] = ngModule && ngModule.injector || null;
|
||||
@ -137,10 +117,11 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
||||
elementNode = hostElement(componentTag, hostNode, this.componentDef);
|
||||
|
||||
// Create directive instance with factory() and store at index 0 in directives array
|
||||
component = baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef);
|
||||
rootContext.components.push(component);
|
||||
rootContext.components.push(
|
||||
component = baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef) as T);
|
||||
initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !);
|
||||
(elementNode.data as LViewData)[CONTEXT] = component;
|
||||
|
||||
// TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
|
||||
// executed here?
|
||||
// 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> {
|
||||
destroyCbs: (() => void)[]|null = [];
|
||||
location: viewEngine_ElementRef<any>;
|
||||
location: ElementRef<any>;
|
||||
injector: Injector;
|
||||
instance: T;
|
||||
hostView: ViewRef<T>;
|
||||
changeDetectorRef: ViewEngine_ChangeDetectorRef;
|
||||
changeDetectorRef: ChangeDetectorRef;
|
||||
componentType: Type<T>;
|
||||
|
||||
constructor(
|
||||
@ -212,7 +193,7 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
|
||||
this.hostView = this.changeDetectorRef = new RootViewRef<T>(rootView);
|
||||
this.hostView._lViewNode = createLNode(-1, TNodeType.View, null, null, null, rootView);
|
||||
this.injector = injector;
|
||||
this.location = new viewEngine_ElementRef(hostNode);
|
||||
this.location = new ElementRef(hostNode);
|
||||
this.componentType = componentType;
|
||||
}
|
||||
|
||||
@ -225,4 +206,4 @@ export class ComponentRef<T> extends viewEngine_ComponentRef<T> {
|
||||
ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
|
||||
this.destroyCbs !.push(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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); }
|
||||
}
|
@ -6,23 +6,17 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import './ng_dev_mode';
|
||||
|
||||
import {ChangeDetectionStrategy} from '../change_detection/constants';
|
||||
import {Provider, ViewEncapsulation} from '../core';
|
||||
import {Provider} from '../core';
|
||||
import {NgModuleDef, NgModuleDefInternal} from '../metadata/ng_module';
|
||||
import {RendererType2} from '../render/api';
|
||||
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 {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.
|
||||
@ -186,28 +180,8 @@ export function defineComponent<T>(componentDefinition: {
|
||||
*/
|
||||
features?: ComponentDefFeature[];
|
||||
|
||||
/**
|
||||
* Defines template and style encapsulation options available for Component's {@link Component}.
|
||||
*/
|
||||
encapsulation?: ViewEncapsulation;
|
||||
rendererType?: RendererType2;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
@ -241,7 +215,6 @@ export function defineComponent<T>(componentDefinition: {
|
||||
const pipeTypes = componentDefinition.pipes !;
|
||||
const directiveTypes = componentDefinition.directives !;
|
||||
const declaredInputs: {[key: string]: string} = {} as any;
|
||||
const encapsulation = componentDefinition.encapsulation;
|
||||
const def: ComponentDefInternal<any> = {
|
||||
type: type,
|
||||
diPublic: null,
|
||||
@ -254,6 +227,7 @@ export function defineComponent<T>(componentDefinition: {
|
||||
inputs: invertObject(componentDefinition.inputs, declaredInputs),
|
||||
declaredInputs: declaredInputs,
|
||||
outputs: invertObject(componentDefinition.outputs),
|
||||
rendererType: resolveRendererType2(componentDefinition.rendererType) || null,
|
||||
exportAs: componentDefinition.exportAs || null,
|
||||
onInit: type.prototype.ngOnInit || null,
|
||||
doCheck: type.prototype.ngDoCheck || null,
|
||||
@ -273,12 +247,6 @@ export function defineComponent<T>(componentDefinition: {
|
||||
selectors: componentDefinition.selectors,
|
||||
viewQuery: componentDefinition.viewQuery || 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;
|
||||
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 {
|
||||
const res: NgModuleDefInternal<T> = {
|
||||
type: def.type,
|
||||
bootstrap: def.bootstrap || EMPTY_ARRAY,
|
||||
declarations: def.declarations || EMPTY_ARRAY,
|
||||
imports: def.imports || EMPTY_ARRAY,
|
||||
exports: def.exports || EMPTY_ARRAY,
|
||||
bootstrap: def.bootstrap || [],
|
||||
declarations: def.declarations || [],
|
||||
imports: def.imports || [],
|
||||
exports: def.exports || [],
|
||||
transitiveCompileScopes: null,
|
||||
};
|
||||
return res as never;
|
||||
}
|
||||
|
||||
const EMPTY = {};
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -584,10 +584,15 @@ export function getOrCreateContainerRef(di: LInjector): viewEngine.ViewContainer
|
||||
const hostParent = getParentLNode(vcRefHost) !;
|
||||
const lContainer = createLContainer(hostParent, vcRefHost.view, true);
|
||||
const comment = vcRefHost.view[RENDERER].createComment(ngDevMode ? 'container' : '');
|
||||
const lContainerNode: LContainerNode =
|
||||
createLNodeObject(TNodeType.Container, vcRefHost.view, hostParent, comment, lContainer);
|
||||
const lContainerNode: LContainerNode = createLNodeObject(
|
||||
TNodeType.Container, vcRefHost.view, hostParent, comment, lContainer, null);
|
||||
appendChild(hostParent, comment, vcRefHost.view);
|
||||
|
||||
|
||||
if (vcRefHost.queries) {
|
||||
lContainerNode.queries = vcRefHost.queries.container();
|
||||
}
|
||||
|
||||
const hostTNode = vcRefHost.tNode as TElementNode | TContainerNode;
|
||||
if (!hostTNode.dynamicContainerNode) {
|
||||
hostTNode.dynamicContainerNode =
|
||||
@ -605,7 +610,7 @@ export function getOrCreateContainerRef(di: LInjector): viewEngine.ViewContainer
|
||||
return di.viewContainerRef;
|
||||
}
|
||||
|
||||
export class NodeInjector implements Injector {
|
||||
class NodeInjector implements Injector {
|
||||
constructor(private _lInjector: LInjector) {}
|
||||
|
||||
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 {
|
||||
debugger;
|
||||
const proto = Object.getPrototypeOf(type.prototype).constructor as Type<any>;
|
||||
const factory = getFactoryOf<T>(proto);
|
||||
if (factory !== null) {
|
||||
return factory;
|
||||
} 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();
|
||||
if (factory === null) {
|
||||
throw new Error(`Type ${proto.name} does not support inheritance`);
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
|
||||
class TemplateRef<T> implements viewEngine.TemplateRef<T> {
|
||||
|
@ -89,9 +89,7 @@ export function NgOnChangesFeature<T>(definition: DirectiveDefInternal<T>): void
|
||||
}
|
||||
|
||||
if (setter) setter.call(this, value);
|
||||
},
|
||||
// Make the property configurable in dev mode to allow overriding in tests
|
||||
configurable: !!ngDevMode
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -13,12 +13,13 @@ import {NgOnChangesFeature} from './features/ng_onchanges_feature';
|
||||
import {PublicFeature} from './features/public_feature';
|
||||
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 {Render3DebugRendererFactory2} from './debug';
|
||||
export {ComponentFactory, ComponentFactoryResolver, ComponentRef} from './component_ref';
|
||||
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 {CssSelectorList} from './interfaces/projection';
|
||||
|
||||
|
||||
|
||||
// Naming scheme:
|
||||
// - Capital letters are for creating things: T(Text), E(Element), D(Directive), V(View),
|
||||
// C(Container), L(Listener)
|
||||
|
@ -22,7 +22,7 @@ import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LEleme
|
||||
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
||||
import {LQueries} from './interfaces/query';
|
||||
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 {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getChildLNode, getLViewChild, getNextLNode, getParentLNode, insertView, removeView} from './node_manipulation';
|
||||
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 {ViewRef} from './view_ref';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -124,6 +126,16 @@ export function getCurrentView(): 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.
|
||||
*
|
||||
@ -163,18 +175,12 @@ let currentQueries: LQueries|null;
|
||||
* - when creating content queries (inb this previousOrParentNode points to a node on which we
|
||||
* create content queries).
|
||||
*/
|
||||
export function getOrCreateCurrentQueries(
|
||||
QueryType: {new (parent: null, shallow: null, deep: null): LQueries}): LQueries {
|
||||
const tNode = previousOrParentNode.tNode;
|
||||
|
||||
// if this is the first content query on a node, any existing LQueries needs to be cloned
|
||||
// in subsequent template passes, the cloning occurs before directive instantiation.
|
||||
if (previousOrParentNode.data !== viewData && !isContentQueryHost(tNode)) {
|
||||
currentQueries && (currentQueries = currentQueries.clone());
|
||||
tNode.flags |= TNodeFlags.hasContentQuery;
|
||||
}
|
||||
|
||||
return currentQueries || (currentQueries = new QueryType(null, null, null));
|
||||
export function getCurrentQueries(QueryType: {new (): LQueries}): LQueries {
|
||||
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
|
||||
return currentQueries ||
|
||||
(currentQueries =
|
||||
(previousOrParentNode.queries && previousOrParentNode.queries.clone() ||
|
||||
new QueryType()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -195,16 +201,6 @@ export function getCreationMode(): boolean {
|
||||
*/
|
||||
let viewData: LViewData;
|
||||
|
||||
/**
|
||||
* Internal function that returns the current LViewData instance.
|
||||
*
|
||||
* The getCurrentView() instruction should be used for anything public.
|
||||
*/
|
||||
export function _getViewData(): LViewData {
|
||||
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
|
||||
return viewData;
|
||||
}
|
||||
|
||||
/**
|
||||
* The last viewData retrieved by nextContext().
|
||||
* Allows building nextContext() and reference() calls.
|
||||
@ -272,7 +268,6 @@ export function enterView(newView: LViewData, host: LElementNode | LViewNode | n
|
||||
}
|
||||
|
||||
viewData = contextViewData = newView;
|
||||
oldView && (oldView[QUERIES] = currentQueries);
|
||||
currentQueries = newView && newView[QUERIES];
|
||||
|
||||
return oldView;
|
||||
@ -397,13 +392,14 @@ export function createLViewData<T>(
|
||||
*/
|
||||
export function createLNodeObject(
|
||||
type: TNodeType, currentView: LViewData, parent: LNode | null,
|
||||
native: RText | RElement | RComment | null,
|
||||
state: any): LElementNode<extNode&LViewNode&LContainerNode&LProjectionNode {
|
||||
native: RText | RElement | RComment | null, state: any,
|
||||
queries: LQueries | null): LElementNode<extNode&LViewNode&LContainerNode&LProjectionNode {
|
||||
return {
|
||||
native: native as any,
|
||||
view: currentView,
|
||||
nodeInjector: parent ? parent.nodeInjector : null,
|
||||
data: state,
|
||||
queries: queries,
|
||||
tNode: null !,
|
||||
dynamicLContainerNode: null
|
||||
};
|
||||
@ -446,9 +442,12 @@ export function createLNode(
|
||||
// so it's only set if the view is the same.
|
||||
const tParent =
|
||||
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 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) {
|
||||
// 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.
|
||||
if (isParent) {
|
||||
currentQueries = null;
|
||||
if (previousOrParentNode.tNode.child == null && previousOrParentNode.view === viewData ||
|
||||
previousOrParentNode.tNode.type === TNodeType.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);
|
||||
|
||||
currentQueries && (currentQueries = currentQueries.addNode(previousOrParentNode));
|
||||
|
||||
const queries = previousOrParentNode.queries;
|
||||
queries && queries.addNode(previousOrParentNode);
|
||||
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 {
|
||||
return (tNode.flags & TNodeFlags.isComponent) === TNodeFlags.isComponent;
|
||||
}
|
||||
@ -920,16 +915,9 @@ export function isComponent(tNode: TNode): boolean {
|
||||
* This function instantiates the given directives.
|
||||
*/
|
||||
function instantiateDirectivesDirectly() {
|
||||
ngDevMode && assertEqual(
|
||||
firstTemplatePass, false,
|
||||
`Directives should only be instantiated directly after first template pass`);
|
||||
const tNode = previousOrParentNode.tNode;
|
||||
const count = tNode.flags & TNodeFlags.DirectiveCountMask;
|
||||
|
||||
if (isContentQueryHost(tNode) && currentQueries) {
|
||||
currentQueries = currentQueries.clone();
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
const start = tNode.flags >> TNodeFlags.DirectiveStartingIndexShift;
|
||||
const end = start + count;
|
||||
@ -1245,7 +1233,8 @@ export function elementEnd(): void {
|
||||
previousOrParentNode = getParentLNode(previousOrParentNode) as LElementNode;
|
||||
}
|
||||
ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Element);
|
||||
currentQueries && (currentQueries = currentQueries.addNode(previousOrParentNode));
|
||||
const queries = previousOrParentNode.queries;
|
||||
queries && queries.addNode(previousOrParentNode);
|
||||
queueLifecycleHooks(previousOrParentNode.tNode.flags, tView);
|
||||
currentElementNode = null;
|
||||
}
|
||||
@ -1666,8 +1655,9 @@ function addComponentLogic<T>(
|
||||
const componentView = addToViewTree(
|
||||
viewData, previousOrParentNode.tNode.index as number,
|
||||
createLViewData(
|
||||
rendererFactory.createRenderer(previousOrParentNode.native as RElement, def), tView,
|
||||
instance, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, getCurrentSanitizer()));
|
||||
rendererFactory.createRenderer(previousOrParentNode.native as RElement, def.rendererType),
|
||||
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 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.
|
||||
*/
|
||||
export function container(
|
||||
index: number, template?: ComponentTemplate<any>| null, tagName?: string | null,
|
||||
attrs?: TAttributes | null, localRefs?: string[] | null): void {
|
||||
index: number, template?: ComponentTemplate<any>, tagName?: string | null, attrs?: TAttributes,
|
||||
localRefs?: string[] | null): void {
|
||||
ngDevMode &&
|
||||
assertEqual(
|
||||
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.
|
||||
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
|
||||
lContainer[QUERIES] = currentQueries.container();
|
||||
lContainer[QUERIES] = queries.container();
|
||||
}
|
||||
|
||||
createDirectivesAndLocals(localRefs);
|
||||
|
||||
isParent = false;
|
||||
ngDevMode && assertNodeType(previousOrParentNode, TNodeType.Container);
|
||||
// check if a given container node matches
|
||||
currentQueries && (currentQueries = currentQueries.addNode(node));
|
||||
queries && queries.addNode(node); // check if a given container node matches
|
||||
queueLifecycleHooks(node.tNode.flags, tView);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,8 @@
|
||||
* 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 {CssSelectorList} from './projection';
|
||||
|
||||
@ -186,42 +187,20 @@ export type ComponentDefInternal<T> = ComponentDef<T, string>;
|
||||
* See: {@link defineComponent}
|
||||
*/
|
||||
export interface ComponentDef<T, Selector extends string> extends DirectiveDef<T, Selector> {
|
||||
/**
|
||||
* Runtime unique component ID.
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The View template of the component.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
readonly viewQuery: ComponentQuery<T>|null;
|
||||
|
||||
/**
|
||||
* The view encapsulation type, which determines how styles are applied to
|
||||
* 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.
|
||||
* Renderer type data of the component.
|
||||
*/
|
||||
readonly encapsulation: ViewEncapsulation;
|
||||
|
||||
/**
|
||||
* 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};
|
||||
readonly rendererType: RendererType2|null;
|
||||
|
||||
/** Whether or not this component's ChangeDetectionStrategy is OnPush */
|
||||
readonly onPush: boolean;
|
||||
|
@ -42,11 +42,8 @@ export const enum TNodeFlags {
|
||||
/** This bit is set if the node has been projected */
|
||||
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 */
|
||||
DirectiveStartingIndexShift = 15,
|
||||
DirectiveStartingIndexShift = 14,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -92,6 +89,13 @@ export interface LNode {
|
||||
/** The injector associated with this node. Necessary for DI. */
|
||||
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
|
||||
* data about this node.
|
||||
|
@ -13,27 +13,26 @@ import {LNode} from './node';
|
||||
/** Used for tracking queries (e.g. ViewChild, ContentChild). */
|
||||
export interface LQueries {
|
||||
/**
|
||||
* The parent LQueries instance.
|
||||
*
|
||||
* 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
|
||||
* Ask queries to prepare copy of itself. This assures that tracking new queries on child nodes
|
||||
* doesn't mutate list of queries tracked on a parent node. We will clone LQueries before
|
||||
* 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
|
||||
* 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
|
||||
|
@ -15,6 +15,7 @@
|
||||
* it will be easy to implement such API.
|
||||
*/
|
||||
|
||||
import {ViewEncapsulation} from '../../metadata/view';
|
||||
import {RendererStyleFlags2, RendererType2} from '../../render/api';
|
||||
|
||||
|
||||
|
@ -10,12 +10,13 @@ import {ConstantPool, R3DirectiveMetadata, WrappedNodeExpr, compileComponentFrom
|
||||
|
||||
import {Component, Directive, HostBinding, HostListener, Input, Output} from '../../metadata/directives';
|
||||
import {componentNeedsResolution, maybeQueueResolutionOfComponentResources} from '../../metadata/resource_loading';
|
||||
import {ReflectionCapabilities} from '../../reflection/reflection_capabilities';
|
||||
import {Type} from '../../type';
|
||||
import {stringify} from '../../util';
|
||||
|
||||
import {angularCoreEnv} from './environment';
|
||||
import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF} from './fields';
|
||||
import {patchComponentDefWithScope, transitiveScopesFor} from './module';
|
||||
import {patchComponentDefWithScope} from './module';
|
||||
import {getReflect, reflectDependencies} from './util';
|
||||
|
||||
type StringMap = {
|
||||
@ -32,12 +33,12 @@ type StringMap = {
|
||||
* until the global queue has been resolved with a call to `resolveComponentResources`.
|
||||
*/
|
||||
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.
|
||||
maybeQueueResolutionOfComponentResources(metadata);
|
||||
Object.defineProperty(type, NG_COMPONENT_DEF, {
|
||||
get: () => {
|
||||
if (ngComponentDef === null) {
|
||||
if (def === null) {
|
||||
if (componentNeedsResolution(metadata)) {
|
||||
const error = [`Component '${stringify(type)}' is not resolved:`];
|
||||
if (metadata.templateUrl) {
|
||||
@ -77,7 +78,7 @@ export function compileComponent(type: Type<any>, metadata: Component): void {
|
||||
constantPool, makeBindingParser());
|
||||
const preStatements = [...constantPool.statements, ...res.statements];
|
||||
|
||||
ngComponentDef = jitExpression(
|
||||
def = jitExpression(
|
||||
res.expression, angularCoreEnv, `ng://${type.name}/ngComponentDef.js`, preStatements);
|
||||
|
||||
// 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
|
||||
// compiling.
|
||||
if (hasSelectorScope(type)) {
|
||||
const scopes = transitiveScopesFor(type.ngSelectorScope);
|
||||
patchComponentDefWithScope(ngComponentDef, scopes);
|
||||
patchComponentDefWithScope(def, type.ngSelectorScope);
|
||||
}
|
||||
}
|
||||
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.
|
||||
*/
|
||||
export function compileDirective(type: Type<any>, directive: Directive): void {
|
||||
let ngDirectiveDef: any = null;
|
||||
let def: any = null;
|
||||
Object.defineProperty(type, NG_DIRECTIVE_DEF, {
|
||||
get: () => {
|
||||
if (ngDirectiveDef === null) {
|
||||
if (def === null) {
|
||||
const constantPool = new ConstantPool();
|
||||
const sourceMapUrl = `ng://${type && type.name}/ngDirectiveDef.js`;
|
||||
const res = compileR3Directive(
|
||||
directiveMetadata(type, directive), constantPool, makeBindingParser());
|
||||
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 {
|
||||
return Object.getPrototypeOf(type.prototype) === Object.prototype;
|
||||
}
|
||||
|
@ -18,28 +18,15 @@ import {reflectDependencies} from './util';
|
||||
|
||||
const EMPTY_ARRAY: Type<any>[] = [];
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
export function compileNgModule(type: Type<any>, ngModule: NgModule): void {
|
||||
const declarations: Type<any>[] = flatten(ngModule.declarations || EMPTY_ARRAY);
|
||||
|
||||
let ngModuleDef: any = null;
|
||||
Object.defineProperty(moduleType, NG_MODULE_DEF, {
|
||||
Object.defineProperty(type, NG_MODULE_DEF, {
|
||||
get: () => {
|
||||
if (ngModuleDef === null) {
|
||||
const meta: R3NgModuleMetadata = {
|
||||
type: wrap(moduleType),
|
||||
type: wrap(type),
|
||||
bootstrap: flatten(ngModule.bootstrap || EMPTY_ARRAY).map(wrap),
|
||||
declarations: declarations.map(wrapReference),
|
||||
imports: flatten(ngModule.imports || EMPTY_ARRAY)
|
||||
@ -51,23 +38,21 @@ export function compileNgModuleDefs(moduleType: Type<any>, ngModule: NgModule):
|
||||
emitInline: true,
|
||||
};
|
||||
const res = compileR3NgModule(meta);
|
||||
ngModuleDef = jitExpression(
|
||||
res.expression, angularCoreEnv, `ng://${moduleType.name}/ngModuleDef.js`, []);
|
||||
ngModuleDef =
|
||||
jitExpression(res.expression, angularCoreEnv, `ng://${type.name}/ngModuleDef.js`, []);
|
||||
}
|
||||
return ngModuleDef;
|
||||
},
|
||||
// Make the property configurable in dev mode to allow overriding in tests
|
||||
configurable: !!ngDevMode,
|
||||
});
|
||||
|
||||
let ngInjectorDef: any = null;
|
||||
Object.defineProperty(moduleType, NG_INJECTOR_DEF, {
|
||||
Object.defineProperty(type, NG_INJECTOR_DEF, {
|
||||
get: () => {
|
||||
if (ngInjectorDef === null) {
|
||||
const meta: R3InjectorMetadata = {
|
||||
name: moduleType.name,
|
||||
type: wrap(moduleType),
|
||||
deps: reflectDependencies(moduleType),
|
||||
name: type.name,
|
||||
type: wrap(type),
|
||||
deps: reflectDependencies(type),
|
||||
providers: new WrappedNodeExpr(ngModule.providers || EMPTY_ARRAY),
|
||||
imports: new WrappedNodeExpr([
|
||||
ngModule.imports || EMPTY_ARRAY,
|
||||
@ -76,36 +61,25 @@ export function compileNgModuleDefs(moduleType: Type<any>, ngModule: NgModule):
|
||||
};
|
||||
const res = compileInjector(meta);
|
||||
ngInjectorDef = jitExpression(
|
||||
res.expression, angularCoreEnv, `ng://${moduleType.name}/ngInjectorDef.js`,
|
||||
res.statements);
|
||||
res.expression, angularCoreEnv, `ng://${type.name}/ngInjectorDef.js`, res.statements);
|
||||
}
|
||||
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 => {
|
||||
// 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)) {
|
||||
// An `ngComponentDef` field exists - go ahead and patch the component directly.
|
||||
const component = declaration as Type<any>& {ngComponentDef: ComponentDefInternal<any>};
|
||||
const componentDef = component.ngComponentDef;
|
||||
patchComponentDefWithScope(componentDef, transitiveScopes);
|
||||
patchComponentDefWithScope(
|
||||
(declaration as Type<any>& {ngComponentDef: ComponentDefInternal<any>}).ngComponentDef,
|
||||
type);
|
||||
} else if (
|
||||
!declaration.hasOwnProperty(NG_DIRECTIVE_DEF) && !declaration.hasOwnProperty(NG_PIPE_DEF)) {
|
||||
// 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
|
||||
* a given module.
|
||||
*/
|
||||
export function patchComponentDefWithScope<C>(
|
||||
componentDef: ComponentDefInternal<C>, transitiveScopes: NgModuleTransitiveScopes) {
|
||||
componentDef.directiveDefs = () => Array.from(transitiveScopes.compilation.directives)
|
||||
export function patchComponentDefWithScope<C, M>(
|
||||
componentDef: ComponentDefInternal<C>, module: Type<M>) {
|
||||
componentDef.directiveDefs = () => Array.from(transitiveScopesFor(module).compilation.directives)
|
||||
.map(dir => dir.ngDirectiveDef || dir.ngComponentDef)
|
||||
.filter(def => !!def);
|
||||
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>) => {
|
||||
const importedTyped = imported as Type<I>& {
|
||||
let importedTyped = imported as Type<I>& {
|
||||
// If imported is an @NgModule:
|
||||
ngModuleDef?: NgModuleDefInternal<I>;
|
||||
};
|
||||
|
@ -17,10 +17,10 @@ import {NG_PIPE_DEF} from './fields';
|
||||
import {reflectDependencies} from './util';
|
||||
|
||||
export function compilePipe(type: Type<any>, meta: Pipe): void {
|
||||
let ngPipeDef: any = null;
|
||||
let def: any = null;
|
||||
Object.defineProperty(type, NG_PIPE_DEF, {
|
||||
get: () => {
|
||||
if (ngPipeDef === null) {
|
||||
if (def === null) {
|
||||
const sourceMapUrl = `ng://${stringify(type)}/ngPipeDef.js`;
|
||||
|
||||
const name = type.name;
|
||||
@ -32,11 +32,9 @@ export function compilePipe(type: Type<any>, meta: Pipe): void {
|
||||
pure: meta.pure !== undefined ? meta.pure : true,
|
||||
});
|
||||
|
||||
ngPipeDef = jitExpression(res.expression, angularCoreEnv, sourceMapUrl, res.statements);
|
||||
def = jitExpression(res.expression, angularCoreEnv, sourceMapUrl, res.statements);
|
||||
}
|
||||
return ngPipeDef;
|
||||
},
|
||||
// Make the property configurable in dev mode to allow overriding in tests
|
||||
configurable: !!ngDevMode,
|
||||
return def;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
|
||||
declare global {
|
||||
const ngDevMode: null|NgDevModePerfCounters;
|
||||
interface NgDevModePerfCounters {
|
||||
@ -32,17 +33,16 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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} =
|
||||
typeof window != 'undefined' && window || typeof global != 'undefined' && global ||
|
||||
typeof self != 'undefined' && self;
|
||||
typeof window != 'undefined' && window || typeof self != 'undefined' && self ||
|
||||
typeof global != 'undefined' && global;
|
||||
|
||||
export function ngDevModeResetPerfCounters(): NgDevModePerfCounters {
|
||||
// Make sure to refer to ngDevMode as ['ngDevMode'] for clousre.
|
||||
return __global['ngDevMode'] = {
|
||||
return __global.ngDevMode = {
|
||||
firstTemplatePass: 0,
|
||||
tNode: 0,
|
||||
tView: 0,
|
||||
@ -75,6 +75,5 @@ export function ngDevModeResetPerfCounters(): NgDevModePerfCounters {
|
||||
* as much early warning and errors as possible.
|
||||
*/
|
||||
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
||||
// Make sure to refer to ngDevMode as ['ngDevMode'] for clousre.
|
||||
__global['ngDevMode'] = ngDevModeResetPerfCounters();
|
||||
__global.ngDevMode = ngDevModeResetPerfCounters();
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import {getSymbolIterator} from '../util';
|
||||
|
||||
import {assertDefined, assertEqual} from './assert';
|
||||
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 {LInjector, unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
|
||||
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 {
|
||||
constructor(
|
||||
public parent: LQueries_|null, private shallow: LQuery<any>|null,
|
||||
private deep: LQuery<any>|null) {}
|
||||
shallow: LQuery<any>|null = null;
|
||||
deep: LQuery<any>|null = null;
|
||||
|
||||
constructor(deep?: LQuery<any>) { this.deep = deep == null ? null : deep; }
|
||||
|
||||
track<T>(
|
||||
queryList: viewEngine_QueryList<T>, predicate: Type<T>|string[], descend?: boolean,
|
||||
@ -100,124 +101,103 @@ 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 {
|
||||
const shallowResults = copyQueriesToContainer(this.shallow);
|
||||
const deepResults = copyQueriesToContainer(this.deep);
|
||||
let result: LQuery<any>|null = null;
|
||||
let query = this.deep;
|
||||
|
||||
return shallowResults || deepResults ? new LQueries_(this, shallowResults, deepResults) : null;
|
||||
while (query) {
|
||||
const containerValues: any[] = []; // prepare room for views
|
||||
query.values.push(containerValues);
|
||||
const clonedQuery: LQuery<any> = {
|
||||
next: null,
|
||||
list: query.list,
|
||||
predicate: query.predicate,
|
||||
values: containerValues,
|
||||
containerValues: null
|
||||
};
|
||||
clonedQuery.next = result;
|
||||
result = clonedQuery;
|
||||
query = query.next;
|
||||
}
|
||||
|
||||
return result ? new LQueries_(result) : null;
|
||||
}
|
||||
|
||||
createView(): LQueries|null {
|
||||
const shallowResults = copyQueriesToView(this.shallow);
|
||||
const deepResults = copyQueriesToView(this.deep);
|
||||
let result: LQuery<any>|null = null;
|
||||
let query = this.deep;
|
||||
|
||||
return shallowResults || deepResults ? new LQueries_(this, shallowResults, deepResults) : null;
|
||||
while (query) {
|
||||
const clonedQuery: LQuery<any> = {
|
||||
next: null,
|
||||
list: query.list,
|
||||
predicate: query.predicate,
|
||||
values: [],
|
||||
containerValues: query.values
|
||||
};
|
||||
clonedQuery.next = result;
|
||||
result = clonedQuery;
|
||||
query = query.next;
|
||||
}
|
||||
|
||||
return result ? new LQueries_(result) : null;
|
||||
}
|
||||
|
||||
insertView(index: number): void {
|
||||
insertView(index, this.shallow);
|
||||
insertView(index, this.deep);
|
||||
let query = this.deep;
|
||||
while (query) {
|
||||
ngDevMode &&
|
||||
assertDefined(
|
||||
query.containerValues, 'View queries need to have a pointer to container values.');
|
||||
query.containerValues !.splice(index, 0, query.values);
|
||||
query = query.next;
|
||||
}
|
||||
}
|
||||
|
||||
addNode(node: LNode): LQueries|null {
|
||||
addNode(node: LNode): void {
|
||||
add(this.shallow, node);
|
||||
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);
|
||||
}
|
||||
}
|
||||
let query = this.deep;
|
||||
while (query) {
|
||||
ngDevMode &&
|
||||
assertDefined(
|
||||
query.containerValues, 'View queries need to have a pointer to container values.');
|
||||
|
||||
function isRootNodeOfQuery(tNode: TNode) {
|
||||
return tNode.parent === null || isContentQueryHost(tNode.parent);
|
||||
}
|
||||
const containerValues = query.containerValues !;
|
||||
const viewValuesIdx = containerValues.indexOf(query.values);
|
||||
const removed = containerValues.splice(viewValuesIdx, 1);
|
||||
|
||||
function copyQueriesToContainer(query: LQuery<any>| null): LQuery<any>|null {
|
||||
let result: LQuery<any>|null = null;
|
||||
// mark a query as dirty only when removed view had matching modes
|
||||
ngDevMode && assertEqual(removed.length, 1, 'removed.length');
|
||||
if (removed[0].length) {
|
||||
query.list.setDirty();
|
||||
}
|
||||
|
||||
while (query) {
|
||||
const containerValues: any[] = []; // prepare room for views
|
||||
query.values.push(containerValues);
|
||||
const clonedQuery: LQuery<any> = {
|
||||
next: result,
|
||||
list: query.list,
|
||||
predicate: query.predicate,
|
||||
values: containerValues,
|
||||
containerValues: null
|
||||
};
|
||||
result = clonedQuery;
|
||||
query = query.next;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function copyQueriesToView(query: LQuery<any>| null): LQuery<any>|null {
|
||||
let result: LQuery<any>|null = null;
|
||||
|
||||
while (query) {
|
||||
const clonedQuery: LQuery<any> = {
|
||||
next: result,
|
||||
list: query.list,
|
||||
predicate: query.predicate,
|
||||
values: [],
|
||||
containerValues: query.values
|
||||
};
|
||||
result = clonedQuery;
|
||||
query = query.next;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function insertView(index: number, query: LQuery<any>| null) {
|
||||
while (query) {
|
||||
ngDevMode &&
|
||||
assertDefined(
|
||||
query.containerValues, 'View queries need to have a pointer to container values.');
|
||||
query.containerValues !.splice(index, 0, query.values);
|
||||
query = query.next;
|
||||
}
|
||||
}
|
||||
|
||||
function removeView(query: LQuery<any>| null) {
|
||||
while (query) {
|
||||
ngDevMode &&
|
||||
assertDefined(
|
||||
query.containerValues, 'View queries need to have a pointer to container values.');
|
||||
|
||||
const containerValues = query.containerValues !;
|
||||
const viewValuesIdx = containerValues.indexOf(query.values);
|
||||
const removed = containerValues.splice(viewValuesIdx, 1);
|
||||
|
||||
// mark a query as dirty only when removed view had matching modes
|
||||
ngDevMode && assertEqual(removed.length, 1, 'removed.length');
|
||||
if (removed[0].length) {
|
||||
query.list.setDirty();
|
||||
query = query.next;
|
||||
}
|
||||
|
||||
query = query.next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Iterates over local names for a given node and returns directive index
|
||||
* (or -1 if a local name points to an element).
|
||||
@ -438,7 +418,7 @@ export function query<T>(
|
||||
read?: QueryReadType<T>| Type<T>): QueryList<T> {
|
||||
ngDevMode && assertPreviousIsParent();
|
||||
const queryList = new QueryList<T>();
|
||||
const queries = getOrCreateCurrentQueries(LQueries_);
|
||||
const queries = getCurrentQueries(LQueries_);
|
||||
queries.track(queryList, predicate, descend, read);
|
||||
storeCleanupWithContext(null, queryList, queryList.destroy);
|
||||
if (memoryIndex != null) {
|
||||
|
@ -22,7 +22,7 @@ import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './err
|
||||
import {resolveDep} from './provider';
|
||||
import {dirtyParentQueries, getQueryValue} from './query';
|
||||
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 {checkAndUpdateNode, checkAndUpdateView, checkNoChangesNode, checkNoChangesView, createComponentView, createEmbeddedView, createRootView, destroyView} from './view';
|
||||
|
||||
@ -509,7 +509,6 @@ class DebugContext_ implements DebugContext {
|
||||
private nodeDef: NodeDef;
|
||||
private elView: ViewData;
|
||||
private elDef: NodeDef;
|
||||
|
||||
constructor(public view: ViewData, public nodeIndex: number|null) {
|
||||
if (nodeIndex == null) {
|
||||
this.nodeIndex = nodeIndex = 0;
|
||||
@ -529,18 +528,13 @@ class DebugContext_ implements DebugContext {
|
||||
this.elDef = elDef;
|
||||
this.elView = elView;
|
||||
}
|
||||
|
||||
private get elOrCompView() {
|
||||
// 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;
|
||||
}
|
||||
|
||||
get injector(): Injector { return createInjector(this.elView, this.elDef); }
|
||||
|
||||
get component(): any { return this.elOrCompView.component; }
|
||||
|
||||
get context(): any { return this.elOrCompView.context; }
|
||||
|
||||
get providerTokens(): any[] {
|
||||
const tokens: any[] = [];
|
||||
if (this.elDef) {
|
||||
@ -555,7 +549,6 @@ class DebugContext_ implements DebugContext {
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
get references(): {[key: string]: any} {
|
||||
const references: {[key: string]: any} = {};
|
||||
if (this.elDef) {
|
||||
@ -572,17 +565,14 @@ class DebugContext_ implements DebugContext {
|
||||
}
|
||||
return references;
|
||||
}
|
||||
|
||||
get componentRenderElement() {
|
||||
const elData = findHostElement(this.elOrCompView);
|
||||
return elData ? elData.renderElement : undefined;
|
||||
}
|
||||
|
||||
get renderNode(): any {
|
||||
return this.nodeDef.flags & NodeFlags.TypeText ? renderNode(this.view, this.nodeDef) :
|
||||
renderNode(this.elView, this.elDef);
|
||||
}
|
||||
|
||||
logError(console: Console, ...values: any[]) {
|
||||
let logViewDef: ViewDefinition;
|
||||
let logNodeIndex: number;
|
||||
@ -663,7 +653,8 @@ export function getCurrentDebugContext(): DebugContext|null {
|
||||
return _currentView ? new DebugContext_(_currentView, _currentNodeIndex) : null;
|
||||
}
|
||||
|
||||
export class DebugRendererFactory2 implements RendererFactory2 {
|
||||
|
||||
class DebugRendererFactory2 implements RendererFactory2 {
|
||||
constructor(private delegate: RendererFactory2) {}
|
||||
|
||||
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};
|
||||
|
||||
/**
|
||||
* 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; }
|
||||
|
||||
destroyNode(node: any) {
|
||||
@ -717,7 +696,7 @@ export class DebugRenderer2 implements Renderer2 {
|
||||
|
||||
createElement(name: string, namespace?: string): any {
|
||||
const el = this.delegate.createElement(name, namespace);
|
||||
const debugCtx = this.debugContext;
|
||||
const debugCtx = getCurrentDebugContext();
|
||||
if (debugCtx) {
|
||||
const debugEl = new DebugElement(el, null, debugCtx);
|
||||
debugEl.name = name;
|
||||
@ -728,7 +707,7 @@ export class DebugRenderer2 implements Renderer2 {
|
||||
|
||||
createComment(value: string): any {
|
||||
const comment = this.delegate.createComment(value);
|
||||
const debugCtx = this.debugContext;
|
||||
const debugCtx = getCurrentDebugContext();
|
||||
if (debugCtx) {
|
||||
indexDebugNode(new DebugNode(comment, null, debugCtx));
|
||||
}
|
||||
@ -737,7 +716,7 @@ export class DebugRenderer2 implements Renderer2 {
|
||||
|
||||
createText(value: string): any {
|
||||
const text = this.delegate.createText(value);
|
||||
const debugCtx = this.debugContext;
|
||||
const debugCtx = getCurrentDebugContext();
|
||||
if (debugCtx) {
|
||||
indexDebugNode(new DebugNode(text, null, debugCtx));
|
||||
}
|
||||
@ -775,7 +754,7 @@ export class DebugRenderer2 implements Renderer2 {
|
||||
|
||||
selectRootElement(selectorOrNode: string|any): any {
|
||||
const el = this.delegate.selectRootElement(selectorOrNode);
|
||||
const debugCtx = this.debugContext;
|
||||
const debugCtx = getCurrentDebugContext();
|
||||
if (debugCtx) {
|
||||
indexDebugNode(new DebugElement(el, null, debugCtx));
|
||||
}
|
||||
|
@ -6,33 +6,44 @@
|
||||
* 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 {BrowserModule} from '@angular/platform-browser';
|
||||
import {BrowserModule, EVENT_MANAGER_PLUGINS, platformBrowser} from '@angular/platform-browser';
|
||||
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';
|
||||
|
||||
ivyEnabled && describe('ApplicationRef bootstrap', () => {
|
||||
@Component({
|
||||
selector: 'hello-world',
|
||||
template: '<div>Hello {{ name }}</div>',
|
||||
})
|
||||
class HelloWorldComponent implements OnInit,
|
||||
DoCheck {
|
||||
describe('ApplicationRef bootstrap', () => {
|
||||
class HelloWorldComponent implements OnInit, DoCheck {
|
||||
log: string[] = [];
|
||||
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'); }
|
||||
|
||||
ngDoCheck(): void { this.log.push('DoCheck'); }
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [HelloWorldComponent],
|
||||
bootstrap: [HelloWorldComponent],
|
||||
imports: [BrowserModule],
|
||||
})
|
||||
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() => {
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
|
@ -27,7 +27,7 @@
|
||||
"name": "EMPTY$1"
|
||||
},
|
||||
{
|
||||
"name": "EMPTY_ARRAY$2"
|
||||
"name": "EMPTY_RENDERER_TYPE_ID"
|
||||
},
|
||||
{
|
||||
"name": "FLAGS"
|
||||
@ -77,9 +77,15 @@
|
||||
{
|
||||
"name": "TVIEW"
|
||||
},
|
||||
{
|
||||
"name": "UNDEFINED_RENDERER_TYPE_ID"
|
||||
},
|
||||
{
|
||||
"name": "VIEWS"
|
||||
},
|
||||
{
|
||||
"name": "ViewEncapsulation$1"
|
||||
},
|
||||
{
|
||||
"name": "_CLEAN_PROMISE"
|
||||
},
|
||||
@ -263,6 +269,9 @@
|
||||
{
|
||||
"name": "resetApplicationState"
|
||||
},
|
||||
{
|
||||
"name": "resolveRendererType2"
|
||||
},
|
||||
{
|
||||
"name": "setHostBindings"
|
||||
},
|
||||
|
@ -45,7 +45,7 @@
|
||||
"name": "EMPTY$1"
|
||||
},
|
||||
{
|
||||
"name": "EMPTY_ARRAY$2"
|
||||
"name": "EMPTY_RENDERER_TYPE_ID"
|
||||
},
|
||||
{
|
||||
"name": "ElementRef"
|
||||
@ -182,6 +182,9 @@
|
||||
{
|
||||
"name": "TodoStore"
|
||||
},
|
||||
{
|
||||
"name": "UNDEFINED_RENDERER_TYPE_ID"
|
||||
},
|
||||
{
|
||||
"name": "VIEWS"
|
||||
},
|
||||
@ -191,6 +194,9 @@
|
||||
{
|
||||
"name": "ViewContainerRef$1"
|
||||
},
|
||||
{
|
||||
"name": "ViewEncapsulation$1"
|
||||
},
|
||||
{
|
||||
"name": "ViewRef"
|
||||
},
|
||||
@ -671,9 +677,6 @@
|
||||
{
|
||||
"name": "isComponent"
|
||||
},
|
||||
{
|
||||
"name": "isContentQueryHost"
|
||||
},
|
||||
{
|
||||
"name": "isContextDirty"
|
||||
},
|
||||
@ -830,6 +833,9 @@
|
||||
{
|
||||
"name": "resolveDirective"
|
||||
},
|
||||
{
|
||||
"name": "resolveRendererType2"
|
||||
},
|
||||
{
|
||||
"name": "restoreView"
|
||||
},
|
||||
|
@ -6,19 +6,19 @@
|
||||
* 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 {ComponentFixture} from '../render_util';
|
||||
import {renderComponent, toHtml} from '../render_util';
|
||||
|
||||
/// See: `normative.md`
|
||||
describe('local references', () => {
|
||||
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;
|
||||
|
||||
@Component(
|
||||
{selector: 'my-component', template: `<input #user value="World">Hello, {{user.value}}!`})
|
||||
@Component({selector: 'my-component', template: `<input #user>Hello {{user.value}}!`})
|
||||
class MyComponent {
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
@ -28,19 +28,19 @@ describe('local references', () => {
|
||||
template: function(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
let l1_user: any;
|
||||
if (rf & 1) {
|
||||
$r3$.ɵEe(0, 'input', ['value', 'World'], ['user', '']);
|
||||
$r3$.ɵEe(0, 'input', null, ['user', '']);
|
||||
$r3$.ɵT(2);
|
||||
}
|
||||
if (rf & 2) {
|
||||
l1_user = $r3$.ɵr<any>(1);
|
||||
$r3$.ɵt(2, $r3$.ɵi1('Hello, ', l1_user.value, '!'));
|
||||
l1_user = $r3$.ɵld<any>(1);
|
||||
$r3$.ɵt(2, $r3$.ɵi1('Hello ', l1_user.value, '!'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// NORMATIVE
|
||||
}
|
||||
|
||||
const fixture = new ComponentFixture(MyComponent);
|
||||
expect(fixture.html).toEqual(`<input value="World">Hello, World!`);
|
||||
expect(toHtml(renderComponent(MyComponent)))
|
||||
.toEqual('<div class="my-app" title="Hello">Hello <b>World</b>!</div>');
|
||||
});
|
||||
});
|
||||
|
@ -204,9 +204,8 @@ describe('encapsulation', () => {
|
||||
}
|
||||
},
|
||||
factory: () => new EncapsulatedComponent,
|
||||
encapsulation: ViewEncapsulation.Emulated,
|
||||
styles: [],
|
||||
data: {},
|
||||
rendererType:
|
||||
createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}),
|
||||
directives: () => [LeafComponent]
|
||||
});
|
||||
}
|
||||
@ -251,9 +250,8 @@ describe('encapsulation', () => {
|
||||
}
|
||||
},
|
||||
factory: () => new WrapperComponentWith,
|
||||
encapsulation: ViewEncapsulation.Emulated,
|
||||
styles: [],
|
||||
data: {},
|
||||
rendererType:
|
||||
createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}),
|
||||
directives: () => [LeafComponentwith]
|
||||
});
|
||||
}
|
||||
@ -270,9 +268,8 @@ describe('encapsulation', () => {
|
||||
}
|
||||
},
|
||||
factory: () => new LeafComponentwith,
|
||||
encapsulation: ViewEncapsulation.Emulated,
|
||||
styles: [],
|
||||
data: {},
|
||||
rendererType:
|
||||
createRendererType2({encapsulation: ViewEncapsulation.Emulated, styles: [], data: {}}),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1159,7 +1159,7 @@ describe('di', () => {
|
||||
/** <div *myIf="showing" dir dirSameInstance #dir="dir"> {{ dir.value }} </div> */
|
||||
template: function(rf: RenderFlags, ctx: MyApp) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
container(0, C1, null, ['myIf', 'showing']);
|
||||
container(0, C1, undefined, ['myIf', 'showing']);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
containerRefreshStart(0);
|
||||
|
@ -9,12 +9,13 @@
|
||||
import {ɵAnimationEngine, ɵNoopAnimationStyleNormalizer} from '@angular/animations/browser';
|
||||
import {MockAnimationDriver} from '@angular/animations/browser/testing';
|
||||
import {NgZone, RendererFactory2} from '@angular/core';
|
||||
import {NoopNgZone} from '@angular/core/src/zone/ng_zone';
|
||||
import {EventManager, ɵDomRendererFactory2, ɵDomSharedStylesHost} from '@angular/platform-browser';
|
||||
import {ɵAnimationRendererFactory} from '@angular/platform-browser/animations';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
import {EventManagerPlugin} from '@angular/platform-browser/src/dom/events/event_manager';
|
||||
|
||||
import {NoopNgZone} from '../../src/zone/ng_zone';
|
||||
|
||||
export class SimpleDomEventsPlugin extends EventManagerPlugin {
|
||||
constructor(doc: any) { super(doc); }
|
||||
|
||||
|
@ -11,7 +11,7 @@ import {ElementRef, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||
|
||||
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 {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 {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {query, queryRefresh} from '../../src/render3/query';
|
||||
@ -49,7 +49,7 @@ function isViewContainerRef(candidate: any): boolean {
|
||||
}
|
||||
|
||||
describe('query', () => {
|
||||
it('should match projected query children', () => {
|
||||
it('should project query children', () => {
|
||||
const Child = createComponent('child', function(rf: RenderFlags, ctx: any) {});
|
||||
|
||||
let child1 = null;
|
||||
@ -510,7 +510,7 @@ describe('query', () => {
|
||||
'cmpt',
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
container(1, null, null, null, ['foo', '']);
|
||||
container(1, undefined, undefined, undefined, ['foo', '']);
|
||||
}
|
||||
},
|
||||
[], [],
|
||||
@ -542,7 +542,7 @@ describe('query', () => {
|
||||
'cmpt',
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
container(1, null, null, null, ['foo', '']);
|
||||
container(1, undefined, undefined, undefined, ['foo', '']);
|
||||
}
|
||||
},
|
||||
[], [],
|
||||
@ -577,7 +577,7 @@ describe('query', () => {
|
||||
'cmpt',
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
container(1, null, null, null, ['foo', '']);
|
||||
container(1, undefined, undefined, undefined, ['foo', '']);
|
||||
}
|
||||
},
|
||||
[], [],
|
||||
@ -609,7 +609,7 @@ describe('query', () => {
|
||||
'cmpt',
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
container(1, null, null, null, ['foo', '']);
|
||||
container(1, undefined, undefined, undefined, ['foo', '']);
|
||||
}
|
||||
},
|
||||
[], [],
|
||||
@ -1129,7 +1129,7 @@ describe('query', () => {
|
||||
}
|
||||
}, null, []);
|
||||
|
||||
container(5, null, null, [AttributeMarker.SelectOnly, 'vc']);
|
||||
container(5, undefined, null, [AttributeMarker.SelectOnly, 'vc']);
|
||||
}
|
||||
|
||||
if (rf & RenderFlags.Update) {
|
||||
@ -1221,8 +1221,8 @@ describe('query', () => {
|
||||
}
|
||||
}, null, []);
|
||||
|
||||
container(2, null, null, [AttributeMarker.SelectOnly, 'vc']);
|
||||
container(3, null, null, [AttributeMarker.SelectOnly, 'vc']);
|
||||
container(2, undefined, null, [AttributeMarker.SelectOnly, 'vc']);
|
||||
container(3, undefined, null, [AttributeMarker.SelectOnly, 'vc']);
|
||||
}
|
||||
|
||||
if (rf & RenderFlags.Update) {
|
||||
@ -1288,7 +1288,7 @@ describe('query', () => {
|
||||
element(0, 'span', ['id', 'from_tpl'], ['foo', '']);
|
||||
}
|
||||
}, undefined, undefined, ['tpl', '']);
|
||||
container(3, null, null, [AttributeMarker.SelectOnly, 'ngTemplateOutlet']);
|
||||
container(3, undefined, null, [AttributeMarker.SelectOnly, 'ngTemplateOutlet']);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
const tplRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(1)));
|
||||
@ -1617,9 +1617,7 @@ describe('query', () => {
|
||||
it('should support combination of deep and shallow queries', () => {
|
||||
/**
|
||||
* % if (exp) { ">
|
||||
* <div #foo>
|
||||
* <div #foo></div>
|
||||
* </div>
|
||||
* <div #foo></div>
|
||||
* % }
|
||||
* <span #foo></span>
|
||||
* class Cmpt {
|
||||
@ -1641,9 +1639,7 @@ describe('query', () => {
|
||||
let rf0 = embeddedViewStart(0);
|
||||
{
|
||||
if (rf0 & RenderFlags.Create) {
|
||||
elementStart(0, 'div', null, ['foo', '']);
|
||||
{ element(2, 'div', null, ['foo', '']); }
|
||||
elementEnd();
|
||||
element(0, 'div', null, ['foo', '']);
|
||||
}
|
||||
}
|
||||
embeddedViewEnd();
|
||||
@ -1675,12 +1671,8 @@ describe('query', () => {
|
||||
|
||||
cmptInstance.exp = true;
|
||||
detectChanges(cmptInstance);
|
||||
expect(deep.length).toBe(3);
|
||||
|
||||
// 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);
|
||||
expect(deep.length).toBe(2);
|
||||
expect(shallow.length).toBe(1);
|
||||
|
||||
cmptInstance.exp = false;
|
||||
detectChanges(cmptInstance);
|
||||
@ -1780,113 +1772,44 @@ 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', () => {
|
||||
let withContentInstance: WithContentDirective|null;
|
||||
let shallowCompInstance: ShallowComp|null;
|
||||
|
||||
beforeEach(() => {
|
||||
withContentInstance = null;
|
||||
shallowCompInstance = null;
|
||||
});
|
||||
|
||||
class WithContentDirective {
|
||||
// @ContentChildren('foo')
|
||||
foos !: QueryList<ElementRef>;
|
||||
contentInitQuerySnapshot = 0;
|
||||
contentCheckedQuerySnapshot = 0;
|
||||
|
||||
ngAfterContentInit() { this.contentInitQuerySnapshot = this.foos ? this.foos.length : 0; }
|
||||
|
||||
ngAfterContentChecked() {
|
||||
this.contentCheckedQuerySnapshot = this.foos ? this.foos.length : 0;
|
||||
}
|
||||
|
||||
static ngComponentDef = defineDirective({
|
||||
type: WithContentDirective,
|
||||
selectors: [['', 'with-content', '']],
|
||||
factory: () => new WithContentDirective(),
|
||||
contentQueries:
|
||||
() => { registerContentQuery(query(null, ['foo'], true, QUERY_READ_FROM_NODE)); },
|
||||
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||
let tmp: any;
|
||||
withContentInstance = loadDirective<WithContentDirective>(dirIndex);
|
||||
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
|
||||
(withContentInstance.foos = tmp);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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', () => {
|
||||
let withContentInstance: WithContentDirective|null = null;
|
||||
|
||||
class WithContentDirective {
|
||||
// @ContentChildren('foo') foos;
|
||||
foos !: QueryList<ElementRef>;
|
||||
contentInitQuerySnapshot = 0;
|
||||
contentCheckedQuerySnapshot = 0;
|
||||
|
||||
ngAfterContentInit() { this.contentInitQuerySnapshot = this.foos ? this.foos.length : 0; }
|
||||
|
||||
ngAfterContentChecked() {
|
||||
this.contentCheckedQuerySnapshot = this.foos ? this.foos.length : 0;
|
||||
}
|
||||
|
||||
static ngComponentDef = defineDirective({
|
||||
type: WithContentDirective,
|
||||
selectors: [['', 'with-content', '']],
|
||||
factory: () => new WithContentDirective(),
|
||||
contentQueries:
|
||||
() => { registerContentQuery(query(null, ['foo'], true, QUERY_READ_FROM_NODE)); },
|
||||
contentQueriesRefresh: (dirIndex: number, queryStartIdx: number) => {
|
||||
let tmp: any;
|
||||
withContentInstance = loadDirective<WithContentDirective>(dirIndex);
|
||||
queryRefresh(tmp = loadQueryList<ElementRef>(queryStartIdx)) &&
|
||||
(withContentInstance.foos = tmp);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* <div with-content>
|
||||
* <span #foo></span>
|
||||
* </div>
|
||||
* class Cmpt {
|
||||
* }
|
||||
*/
|
||||
const AppComponent = createComponent('app-component', function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
@ -1911,65 +1834,37 @@ describe('query', () => {
|
||||
`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]);
|
||||
// https://stackblitz.com/edit/angular-wlenwd?file=src%2Fapp%2Fapp.component.ts
|
||||
it('should support view and content queries matching the same element', () => {
|
||||
let withContentComponentInstance: WithContentComponent;
|
||||
|
||||
const fixture = new ComponentFixture(AppComponent);
|
||||
expect(withContentInstance !.foos.length)
|
||||
.toBe(1, `Expected content query to match <div with-content #foo>.`);
|
||||
});
|
||||
class WithContentComponent {
|
||||
// @ContentChildren('foo') foos;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
foos !: QueryList<ElementRef>;
|
||||
|
||||
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', '']);
|
||||
}
|
||||
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);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* <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
|
||||
it('should support view and content queries matching the same element', () => {
|
||||
/**
|
||||
* <div with-content>
|
||||
* <with-content>
|
||||
* <div #foo></div>
|
||||
* </div>
|
||||
* </with-content>
|
||||
* <div id="after" #bar></div>
|
||||
* class Cmpt {
|
||||
* @ViewChildren('foo, bar') foos;
|
||||
@ -1979,13 +1874,13 @@ describe('query', () => {
|
||||
'app-component',
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(1, 'div', ['with-content', '']);
|
||||
elementStart(1, 'with-content');
|
||||
{ element(2, 'div', null, ['foo', '']); }
|
||||
elementEnd();
|
||||
element(4, 'div', ['id', 'after'], ['bar', '']);
|
||||
}
|
||||
},
|
||||
[WithContentDirective], [],
|
||||
[WithContentComponent], [],
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
query(0, ['foo', 'bar'], true, QUERY_READ_FROM_NODE);
|
||||
@ -2000,48 +1895,14 @@ describe('query', () => {
|
||||
const viewQList = fixture.component.foos;
|
||||
|
||||
expect(viewQList.length).toBe(2);
|
||||
expect(withContentInstance !.foos.length).toBe(1);
|
||||
expect(viewQList.first.nativeElement).toBe(withContentInstance !.foos.first.nativeElement);
|
||||
expect(withContentComponentInstance !.foos.length).toBe(1);
|
||||
expect(viewQList.first.nativeElement)
|
||||
.toBe(withContentComponentInstance !.foos.first.nativeElement);
|
||||
expect(viewQList.last.nativeElement.id).toBe('after');
|
||||
});
|
||||
|
||||
it('should not report deep content query matches found above content children', () => {
|
||||
/**
|
||||
* <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>);
|
||||
}
|
||||
});
|
||||
it('should report results to appropriate queries where content queries are nested', () => {
|
||||
|
||||
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 {
|
||||
fooBars: any;
|
||||
static ngDirectiveDef = defineDirective({
|
||||
@ -2101,63 +1962,6 @@ describe('query', () => {
|
||||
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',
|
||||
() => {
|
||||
class ShallowQueryDirective {
|
||||
@ -2208,9 +2012,6 @@ describe('query', () => {
|
||||
/**
|
||||
* <div shallow-query #shallow="shallow-query" deep-query #deep="deep-query">
|
||||
* <span #foo></span>
|
||||
* <div>
|
||||
* <span #foo></span>
|
||||
* </div>
|
||||
* </div>
|
||||
*/
|
||||
function(rf: RenderFlags, ctx: any) {
|
||||
@ -2218,12 +2019,7 @@ describe('query', () => {
|
||||
elementStart(
|
||||
0, 'div', [AttributeMarker.SelectOnly, 'shallow-query', 'deep-query'],
|
||||
['shallow', 'shallow-query', 'deep', 'deep-query']);
|
||||
{
|
||||
element(3, 'span', null, ['foo', '']);
|
||||
elementStart(5, 'div');
|
||||
{ element(6, 'span', null, ['foo', '']); }
|
||||
elementEnd();
|
||||
}
|
||||
{ element(3, 'span', ['id', 'foo'], ['foo', '']); }
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
@ -2235,7 +2031,7 @@ describe('query', () => {
|
||||
|
||||
const fixture = new ComponentFixture(AppComponent);
|
||||
expect(shallowInstance !.foos.length).toBe(1);
|
||||
expect(deepInstance !.foos.length).toBe(2);
|
||||
expect(deepInstance !.foos.length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -13,7 +13,7 @@ import {CreateComponentOptions} from '../../src/render3/component';
|
||||
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 {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 {RElement, RText, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer';
|
||||
import {Sanitizer} from '../../src/sanitization/security';
|
||||
|
@ -158,22 +158,24 @@ describe('animation renderer factory', () => {
|
||||
}
|
||||
},
|
||||
factory: () => new SomeComponentWithAnimation,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
styles: [],
|
||||
data: {
|
||||
animation: [{
|
||||
type: 7,
|
||||
name: 'myAnimation',
|
||||
definitions: [{
|
||||
type: 1,
|
||||
expr: '* => on',
|
||||
animation:
|
||||
[{type: 4, styles: {type: 6, styles: {opacity: 1}, offset: null}, timings: 10}],
|
||||
options: null
|
||||
}],
|
||||
options: {}
|
||||
}]
|
||||
},
|
||||
rendererType: createRendererType2({
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
styles: [],
|
||||
data: {
|
||||
animation: [{
|
||||
type: 7,
|
||||
name: 'myAnimation',
|
||||
definitions: [{
|
||||
type: 1,
|
||||
expr: '* => on',
|
||||
animation:
|
||||
[{type: 4, styles: {type: 6, styles: {opacity: 1}, offset: null}, timings: 10}],
|
||||
options: null
|
||||
}],
|
||||
options: {}
|
||||
}]
|
||||
}
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ describe('ViewContainerRef', () => {
|
||||
|
||||
it('should work on containers', () => {
|
||||
function createTemplate() {
|
||||
container(0, embeddedTemplate, null, ['vcref', '']);
|
||||
container(0, embeddedTemplate, undefined, ['vcref', '']);
|
||||
element(1, 'footer');
|
||||
}
|
||||
|
||||
@ -246,8 +246,8 @@ describe('ViewContainerRef', () => {
|
||||
template: (rf: RenderFlags, cmp: TestComponent) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
text(0, 'before|');
|
||||
container(1, EmbeddedTemplateA, null, ['testdir', '']);
|
||||
container(2, EmbeddedTemplateB, null, ['testdir', '']);
|
||||
container(1, EmbeddedTemplateA, undefined, ['testdir', '']);
|
||||
container(2, EmbeddedTemplateB, undefined, ['testdir', '']);
|
||||
text(3, '|after');
|
||||
}
|
||||
},
|
||||
@ -315,7 +315,7 @@ describe('ViewContainerRef', () => {
|
||||
template: (rf: RenderFlags, cmp: TestComponent) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
text(0, 'before|');
|
||||
container(1, EmbeddedTemplateA, null, ['testdir', '']);
|
||||
container(1, EmbeddedTemplateA, undefined, ['testdir', '']);
|
||||
container(2);
|
||||
text(3, '|after');
|
||||
}
|
||||
@ -1074,7 +1074,7 @@ describe('ViewContainerRef', () => {
|
||||
|
||||
it('should work on containers', () => {
|
||||
function createTemplate() {
|
||||
container(0, embeddedTemplate, null, ['vcref', '']);
|
||||
container(0, embeddedTemplate, undefined, ['vcref', '']);
|
||||
element(1, 'footer');
|
||||
}
|
||||
|
||||
|
@ -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 !');
|
||||
});
|
||||
});
|
@ -11,7 +11,6 @@ const sourcemaps = require('rollup-plugin-sourcemaps');
|
||||
|
||||
const globals = {
|
||||
'@angular/core': 'ng.core',
|
||||
'@angular/compiler': 'ng.compiler',
|
||||
'rxjs': 'rxjs',
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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; }
|
||||
}
|
@ -6,109 +6,58 @@
|
||||
* 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 {ComponentFixture} from './component_fixture';
|
||||
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';
|
||||
|
||||
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;
|
||||
|
||||
export interface TestBed {
|
||||
platform: PlatformRef;
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export const ComponentFixtureAutoDetect =
|
||||
new InjectionToken<boolean[]>('ComponentFixtureAutoDetect');
|
||||
|
||||
ngModule: Type<any>|Type<any>[];
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export const ComponentFixtureNoNgZone = new InjectionToken<boolean[]>('ComponentFixtureNoNgZone');
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Reset the providers for the test injector.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
resetTestEnvironment(): void;
|
||||
|
||||
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.
|
||||
*/
|
||||
overrideProvider(token: any, provider: {
|
||||
useFactory: Function,
|
||||
deps: any[],
|
||||
}): void;
|
||||
overrideProvider(token: any, provider: {useValue: any;}): void;
|
||||
overrideProvider(token: any, provider: {useFactory?: Function, useValue?: any, deps?: 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>;
|
||||
}
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export type TestModuleMetadata = {
|
||||
providers?: any[],
|
||||
declarations?: any[],
|
||||
imports?: any[],
|
||||
schemas?: Array<SchemaMetadata|any[]>,
|
||||
aotSummaries?: () => any[],
|
||||
};
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* 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
|
||||
* angular module. These are common to every test in the suite.
|
||||
@ -123,9 +72,8 @@ export class TestBedViewEngine implements Injector, TestBed {
|
||||
* @experimental
|
||||
*/
|
||||
static initTestEnvironment(
|
||||
ngModule: Type<any>|Type<any>[], platform: PlatformRef,
|
||||
aotSummaries?: () => any[]): TestBedViewEngine {
|
||||
const testBed = _getTestBedViewEngine();
|
||||
ngModule: Type<any>|Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): TestBed {
|
||||
const testBed = getTestBed();
|
||||
testBed.initTestEnvironment(ngModule, platform, aotSummaries);
|
||||
return testBed;
|
||||
}
|
||||
@ -135,29 +83,29 @@ export class TestBedViewEngine implements Injector, TestBed {
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
static resetTestEnvironment(): void { _getTestBedViewEngine().resetTestEnvironment(); }
|
||||
static resetTestEnvironment() { getTestBed().resetTestEnvironment(); }
|
||||
|
||||
static resetTestingModule(): TestBedStatic {
|
||||
_getTestBedViewEngine().resetTestingModule();
|
||||
return TestBedViewEngine as any as TestBedStatic;
|
||||
static resetTestingModule(): typeof TestBed {
|
||||
getTestBed().resetTestingModule();
|
||||
return TestBed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows overriding default compiler providers and settings
|
||||
* which are defined in test_injector.js
|
||||
*/
|
||||
static configureCompiler(config: {providers?: any[]; useJit?: boolean;}): TestBedStatic {
|
||||
_getTestBedViewEngine().configureCompiler(config);
|
||||
return TestBedViewEngine as any as TestBedStatic;
|
||||
static configureCompiler(config: {providers?: any[]; useJit?: boolean;}): typeof TestBed {
|
||||
getTestBed().configureCompiler(config);
|
||||
return TestBed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows overriding default providers, directives, pipes, modules of the test injector,
|
||||
* which are defined in test_injector.js
|
||||
*/
|
||||
static configureTestingModule(moduleDef: TestModuleMetadata): TestBedStatic {
|
||||
_getTestBedViewEngine().configureTestingModule(moduleDef);
|
||||
return TestBedViewEngine as any as TestBedStatic;
|
||||
static configureTestingModule(moduleDef: TestModuleMetadata): typeof TestBed {
|
||||
getTestBed().configureTestingModule(moduleDef);
|
||||
return TestBed;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -167,31 +115,31 @@ export class TestBedViewEngine implements Injector, TestBed {
|
||||
*/
|
||||
static compileComponents(): Promise<any> { return getTestBed().compileComponents(); }
|
||||
|
||||
static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): TestBedStatic {
|
||||
_getTestBedViewEngine().overrideModule(ngModule, override);
|
||||
return TestBedViewEngine as any as TestBedStatic;
|
||||
static overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): typeof TestBed {
|
||||
getTestBed().overrideModule(ngModule, override);
|
||||
return TestBed;
|
||||
}
|
||||
|
||||
static overrideComponent(component: Type<any>, override: MetadataOverride<Component>):
|
||||
TestBedStatic {
|
||||
_getTestBedViewEngine().overrideComponent(component, override);
|
||||
return TestBedViewEngine as any as TestBedStatic;
|
||||
typeof TestBed {
|
||||
getTestBed().overrideComponent(component, override);
|
||||
return TestBed;
|
||||
}
|
||||
|
||||
static overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>):
|
||||
TestBedStatic {
|
||||
_getTestBedViewEngine().overrideDirective(directive, override);
|
||||
return TestBedViewEngine as any as TestBedStatic;
|
||||
typeof TestBed {
|
||||
getTestBed().overrideDirective(directive, override);
|
||||
return TestBed;
|
||||
}
|
||||
|
||||
static overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): TestBedStatic {
|
||||
_getTestBedViewEngine().overridePipe(pipe, override);
|
||||
return TestBedViewEngine as any as TestBedStatic;
|
||||
static overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): typeof TestBed {
|
||||
getTestBed().overridePipe(pipe, override);
|
||||
return TestBed;
|
||||
}
|
||||
|
||||
static overrideTemplate(component: Type<any>, template: string): TestBedStatic {
|
||||
_getTestBedViewEngine().overrideComponent(component, {set: {template, templateUrl: null !}});
|
||||
return TestBedViewEngine as any as TestBedStatic;
|
||||
static overrideTemplate(component: Type<any>, template: string): typeof TestBed {
|
||||
getTestBed().overrideComponent(component, {set: {template, templateUrl: null !}});
|
||||
return TestBed;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -200,11 +148,13 @@ export class TestBedViewEngine implements Injector, TestBed {
|
||||
*
|
||||
* Note: This works for JIT and AOTed components as well.
|
||||
*/
|
||||
static overrideTemplateUsingTestingModule(component: Type<any>, template: string): TestBedStatic {
|
||||
_getTestBedViewEngine().overrideTemplateUsingTestingModule(component, template);
|
||||
return TestBedViewEngine as any as TestBedStatic;
|
||||
static overrideTemplateUsingTestingModule(component: Type<any>, template: string):
|
||||
typeof TestBed {
|
||||
getTestBed().overrideTemplateUsingTestingModule(component, template);
|
||||
return TestBed;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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: {
|
||||
useFactory: Function,
|
||||
deps: any[],
|
||||
}): TestBedStatic;
|
||||
static overrideProvider(token: any, provider: {useValue: any;}): TestBedStatic;
|
||||
}): typeof TestBed;
|
||||
static overrideProvider(token: any, provider: {useValue: any;}): typeof TestBed;
|
||||
static overrideProvider(token: any, provider: {
|
||||
useFactory?: Function,
|
||||
useValue?: any,
|
||||
deps?: any[],
|
||||
}): TestBedStatic {
|
||||
_getTestBedViewEngine().overrideProvider(token, provider as any);
|
||||
return TestBedViewEngine as any as TestBedStatic;
|
||||
}): typeof TestBed {
|
||||
getTestBed().overrideProvider(token, provider as any);
|
||||
return TestBed;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -238,17 +188,17 @@ export class TestBedViewEngine implements Injector, TestBed {
|
||||
useFactory?: Function,
|
||||
useValue?: any,
|
||||
deps?: any[],
|
||||
}): TestBedStatic {
|
||||
_getTestBedViewEngine().deprecatedOverrideProvider(token, provider as any);
|
||||
return TestBedViewEngine as any as TestBedStatic;
|
||||
}): typeof TestBed {
|
||||
getTestBed().deprecatedOverrideProvider(token, provider as any);
|
||||
return TestBed;
|
||||
}
|
||||
|
||||
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> {
|
||||
return _getTestBedViewEngine().createComponent(component);
|
||||
return getTestBed().createComponent(component);
|
||||
}
|
||||
|
||||
private _instantiated: boolean = false;
|
||||
@ -295,7 +245,7 @@ export class TestBedViewEngine implements Injector, TestBed {
|
||||
* @experimental
|
||||
*/
|
||||
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) {
|
||||
throw new Error('Cannot set base providers because it has already been called');
|
||||
}
|
||||
@ -311,14 +261,14 @@ export class TestBedViewEngine implements Injector, TestBed {
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
resetTestEnvironment(): void {
|
||||
resetTestEnvironment() {
|
||||
this.resetTestingModule();
|
||||
this.platform = null !;
|
||||
this.ngModule = null !;
|
||||
this._testEnvAotSummaries = () => [];
|
||||
}
|
||||
|
||||
resetTestingModule(): void {
|
||||
resetTestingModule() {
|
||||
clearOverrides();
|
||||
this._aotSummaries = [];
|
||||
this._templateOverrides = [];
|
||||
@ -352,12 +302,12 @@ export class TestBedViewEngine implements Injector, TestBed {
|
||||
this._activeFixtures = [];
|
||||
}
|
||||
|
||||
configureCompiler(config: {providers?: any[], useJit?: boolean}): void {
|
||||
configureCompiler(config: {providers?: any[], useJit?: boolean}) {
|
||||
this._assertNotInstantiated('TestBed.configureCompiler', 'configure the compiler');
|
||||
this._compilerOptions.push(config);
|
||||
}
|
||||
|
||||
configureTestingModule(moduleDef: TestModuleMetadata): void {
|
||||
configureTestingModule(moduleDef: TestModuleMetadata) {
|
||||
this._assertNotInstantiated('TestBed.configureTestingModule', 'configure the test module');
|
||||
if (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) {
|
||||
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();
|
||||
if (token === TestBed) {
|
||||
return this;
|
||||
@ -626,32 +576,13 @@ export class TestBedViewEngine implements Injector, TestBed {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
let _testBed: TestBed = null !;
|
||||
|
||||
/**
|
||||
* Returns a singleton of the applicable `TestBed`.
|
||||
*
|
||||
* It will be either an instance of `TestBedViewEngine` or `TestBedRender3`.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
export const getTestBed: () => TestBed = ivyEnabled ? _getTestBedRender3 : _getTestBedViewEngine;
|
||||
|
||||
let testBed: TestBedViewEngine;
|
||||
|
||||
function _getTestBedViewEngine(): TestBedViewEngine {
|
||||
return testBed = testBed || new TestBedViewEngine();
|
||||
export function getTestBed() {
|
||||
return _testBed = _testBed || new TestBed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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>;
|
||||
}
|
@ -15,9 +15,7 @@
|
||||
export * from './async';
|
||||
export * from './component_fixture';
|
||||
export * from './fake_async';
|
||||
export {TestBed, getTestBed, inject, InjectSetupWrapper, withModule} from './test_bed';
|
||||
export * from './test_bed_common';
|
||||
export * from './test_bed';
|
||||
export * from './before_each';
|
||||
export * from './metadata_override';
|
||||
export {MetadataOverrider as ɵMetadataOverrider} from './metadata_overrider';
|
||||
export * from './private_export_testing';
|
||||
|
@ -1,31 +1,27 @@
|
||||
{
|
||||
"extends": "../tsconfig-build.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"rootDir": "../",
|
||||
"paths": {
|
||||
"rxjs/*": [
|
||||
"../../../node_modules/rxjs/*"
|
||||
],
|
||||
"@angular/core": [
|
||||
"../../../dist/packages/core"
|
||||
],
|
||||
"@angular/compiler": [
|
||||
"../../../dist/packages/compiler"
|
||||
],
|
||||
"rxjs/*": ["../../../node_modules/rxjs/*"],
|
||||
"@angular/core": ["../../../dist/packages/core"]
|
||||
},
|
||||
"outDir": "../../../dist/packages/core"
|
||||
},
|
||||
|
||||
"files": [
|
||||
"public_api.ts",
|
||||
"../../../node_modules/zone.js/dist/zone.js.d.ts",
|
||||
"../../system.d.ts",
|
||||
"../../types.d.ts"
|
||||
],
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"strictMetadataEmit": false,
|
||||
"skipTemplateCodegen": true,
|
||||
"flatModuleOutFile": "testing.js",
|
||||
"flatModuleId": "@angular/core/testing"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,22 @@
|
||||
{
|
||||
"extends": "../tsconfig-build.json",
|
||||
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"rootDir": ".",
|
||||
"paths": {
|
||||
"rxjs/*": [
|
||||
"../../node_modules/rxjs/*"
|
||||
],
|
||||
"@angular/core": [
|
||||
"."
|
||||
],
|
||||
"@angular/compiler": [
|
||||
"../../dist/packages/compiler"
|
||||
]
|
||||
"rxjs/*": ["../../node_modules/rxjs/*"],
|
||||
"@angular/core": ["."]
|
||||
},
|
||||
"outDir": "../../dist/packages/core"
|
||||
},
|
||||
|
||||
"files": [
|
||||
"public_api.ts",
|
||||
"../../node_modules/zone.js/dist/zone.js.d.ts",
|
||||
"../system.d.ts"
|
||||
],
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"annotateForClosureCompiler": true,
|
||||
"strictMetadataEmit": false,
|
||||
@ -28,4 +24,4 @@
|
||||
"flatModuleOutFile": "core.js",
|
||||
"flatModuleId": "@angular/core"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ jasmine_node_test(
|
||||
bootstrap = ["angular/tools/testing/init_node_spec.js"],
|
||||
data = [
|
||||
"//packages/common:npm_package",
|
||||
"//packages/compiler:npm_package",
|
||||
"//packages/core:npm_package",
|
||||
"//packages/forms:npm_package",
|
||||
],
|
||||
|
@ -13,7 +13,7 @@ export const MODULE_SUFFIX = '';
|
||||
const builtinExternalReferences = createBuiltinExternalReferencesMap();
|
||||
|
||||
export class JitReflector implements CompileReflector {
|
||||
private reflectionCapabilities = new ReflectionCapabilities();
|
||||
private reflectionCapabilities: ReflectionCapabilities = new ReflectionCapabilities();
|
||||
|
||||
componentModuleUrl(type: any, cmpMetadata: Component): string {
|
||||
const moduleId = cmpMetadata.moduleId;
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
import {CompileReflector, DirectiveResolver, ERROR_COMPONENT_TYPE, NgModuleResolver, PipeResolver} from '@angular/compiler';
|
||||
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 {ɵ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';
|
||||
|
||||
@ -63,7 +63,7 @@ export class TestingCompilerImpl implements TestingCompiler {
|
||||
|
||||
checkOverrideAllowed(type: Type<any>) {
|
||||
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.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ import {DOCUMENT, ɵgetDOM as getDOM} from '@angular/platform-browser';
|
||||
*/
|
||||
@Injectable()
|
||||
export class DOMTestComponentRenderer extends TestComponentRenderer {
|
||||
constructor(@Inject(DOCUMENT) private _doc: any) { super(); }
|
||||
constructor(@Inject(DOCUMENT) private _doc: any /** TODO #9100 */) { super(); }
|
||||
|
||||
insertRootElement(rootElId: string) {
|
||||
const rootEl = <HTMLElement>getDOM().firstChild(
|
||||
|
@ -6,5 +6,126 @@
|
||||
* 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
|
||||
export {ɵMetadataOverrider as MetadataOverrider} from '@angular/core/testing';
|
||||
import {ɵstringify as stringify} from '@angular/core';
|
||||
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;
|
||||
}
|
||||
|
@ -193,10 +193,9 @@ export abstract class AssetGroup {
|
||||
cacheDirectives.forEach(v => v[0] = v[0].toLowerCase());
|
||||
|
||||
// Find the max-age directive, if one exists.
|
||||
const maxAgeDirective = cacheDirectives.find(v => v[0] === 'max-age');
|
||||
const cacheAge = maxAgeDirective ? maxAgeDirective[1] : undefined;
|
||||
const cacheAge = cacheDirectives.filter(v => v[0] === 'max-age').map(v => v[1])[0];
|
||||
|
||||
if (!cacheAge) {
|
||||
if (cacheAge.length === 0) {
|
||||
// No usable TTL defined. Must assume that the response is stale.
|
||||
return true;
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ const dist =
|
||||
.addFile('/lazy/unchanged1.txt', 'this is unchanged (1)')
|
||||
.addFile('/lazy/unchanged2.txt', 'this is unchanged (2)')
|
||||
.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();
|
||||
|
||||
const distUpdate =
|
||||
@ -622,13 +621,6 @@ const manifestUpdateHash = sha1(JSON.stringify(manifestUpdate));
|
||||
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() => {
|
||||
expect(await makeRequest(scope, '/unhashed/a.txt', 'default', {
|
||||
credentials: 'include'
|
||||
|
@ -56,7 +56,7 @@ if [[ ${TRAVIS} &&
|
||||
travisFoldStart "yarn-install.aio"
|
||||
(
|
||||
# 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
|
||||
yarn install --frozen-lockfile --non-interactive
|
||||
|
@ -28,6 +28,7 @@ export interface AnimationAnimateRefMetadata extends AnimationMetadata {
|
||||
options: AnimationOptions | null;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare abstract class AnimationBuilder {
|
||||
abstract build(animation: AnimationMetadata | AnimationMetadata[]): AnimationFactory;
|
||||
}
|
||||
@ -43,6 +44,7 @@ export interface AnimationEvent {
|
||||
triggerName: string;
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare abstract class AnimationFactory {
|
||||
abstract create(element: any, options?: AnimationOptions): AnimationPlayer;
|
||||
}
|
||||
@ -83,6 +85,7 @@ export declare interface AnimationOptions {
|
||||
};
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export interface AnimationPlayer {
|
||||
beforeDestroy?: () => any;
|
||||
parentPlayer: AnimationPlayer | null;
|
||||
@ -171,6 +174,7 @@ export declare function group(steps: AnimationMetadata[], options?: AnimationOpt
|
||||
|
||||
export declare function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSequenceMetadata;
|
||||
|
||||
/** @experimental */
|
||||
export declare class NoopAnimationPlayer implements AnimationPlayer {
|
||||
parentPlayer: AnimationPlayer | null;
|
||||
readonly totalTime: number;
|
||||
|
84
tools/public_api_guard/core/testing.d.ts
vendored
84
tools/public_api_guard/core/testing.d.ts
vendored
@ -37,7 +37,7 @@ export declare function flush(maxTurns?: number): number;
|
||||
export declare function flushMicrotasks(): void;
|
||||
|
||||
/** @experimental */
|
||||
export declare const getTestBed: () => TestBed;
|
||||
export declare function getTestBed(): TestBed;
|
||||
|
||||
export declare function inject(tokens: any[], fn: Function): () => any;
|
||||
|
||||
@ -57,51 +57,71 @@ export declare type MetadataOverride<T> = {
|
||||
/** @experimental */
|
||||
export declare function resetFakeAsyncZone(): void;
|
||||
|
||||
export declare const TestBed: TestBedStatic;
|
||||
|
||||
export interface TestBedStatic {
|
||||
new (...args: any[]): TestBed;
|
||||
export declare class TestBed implements Injector {
|
||||
ngModule: Type<any> | Type<any>[];
|
||||
platform: PlatformRef;
|
||||
compileComponents(): Promise<any>;
|
||||
configureCompiler(config: {
|
||||
providers?: any[];
|
||||
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;
|
||||
configureTestingModule(moduleDef: TestModuleMetadata): void;
|
||||
createComponent<T>(component: Type<T>): ComponentFixture<T>;
|
||||
/** @deprecated */ deprecatedOverrideProvider(token: any, provider: {
|
||||
useFactory: Function;
|
||||
deps: any[];
|
||||
}): void;
|
||||
get(token: any, notFoundValue?: any): any;
|
||||
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: {
|
||||
deprecatedOverrideProvider(token: any, provider: {
|
||||
useValue: any;
|
||||
}): TestBedStatic;
|
||||
overrideProvider(token: any, provider: {
|
||||
useFactory?: Function;
|
||||
useValue?: any;
|
||||
deps?: any[];
|
||||
}): TestBedStatic;
|
||||
}): void;
|
||||
execute(tokens: any[], fn: Function, context?: any): any;
|
||||
get(token: any, notFoundValue?: any): any;
|
||||
/** @experimental */ initTestEnvironment(ngModule: Type<any> | Type<any>[], platform: PlatformRef, aotSummaries?: () => any[]): void;
|
||||
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void;
|
||||
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: {
|
||||
useFactory: Function;
|
||||
deps: any[];
|
||||
}): TestBedStatic;
|
||||
overrideTemplate(component: Type<any>, template: string): TestBedStatic;
|
||||
overrideTemplateUsingTestingModule(component: Type<any>, template: string): TestBedStatic;
|
||||
}): void;
|
||||
overrideProvider(token: any, provider: {
|
||||
useValue: any;
|
||||
}): void;
|
||||
overrideTemplateUsingTestingModule(component: Type<any>, template: string): 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 */
|
||||
|
Reference in New Issue
Block a user