Compare commits

...

36 Commits
6.0.6 ... 6.0.7

Author SHA1 Message Date
2880cf9ef1 release: cut the v6.0.7 release 2018-06-27 16:54:22 -07:00
64a8584a92 fix(common): use correct ICU plural for locale mk (#24659)
PR Close #24659
2018-06-27 15:03:34 -07:00
97897ab738 refactor(upgrade): fix examples for strictPropertyInitialization and remove internal comments (#18487)
PR Close #18487
2018-06-27 14:57:54 -07:00
9378f44d6d docs(aio): tech edits to upgrade-lazy (#18487)
PR Close #18487
2018-06-27 14:53:24 -07:00
afa46af4c6 docs(upgrade): use a class for upgraded service (#18487)
This makes the resulting use in Angular more ideomatic, since we can just
use the class type as the injection indicator.

PR Close #18487
2018-06-27 14:53:24 -07:00
dbffdcc442 docs(upgrade): fix sub-ordered-list syntax (#18487)
We must always use 1., 2. etc, to indicate ordered lists, even for sub-lists.
We can change the sublist to display as a., b. etc, via CSS.

PR Close #18487
2018-06-27 14:53:24 -07:00
06d68a1b9f docs(upgrade): add guide about downgradeModule() (#18487)
PR Close #18487
2018-06-27 14:53:24 -07:00
75dd3c5ca5 docs(upgrade): add API docs for downgradeModule() (#18487)
PR Close #18487
2018-06-27 14:53:24 -07:00
23f56198a5 docs(upgrade): add API docs for propagateDigest (#18487)
PR Close #18487
2018-06-27 14:53:24 -07:00
69167e4519 docs(upgrade): update API docs for upgrade/static (#18487)
PR Close #18487
2018-06-27 14:53:22 -07:00
10feafcf27 docs: minor fixes in docs-style-guide (#18487)
PR Close #18487
2018-06-27 14:49:14 -07:00
3f3fed95be test: minor improvements in examples e2e tests script (#18487)
PR Close #18487
2018-06-27 14:49:13 -07:00
94433f3b9e docs: fix unit tests in toh-pt6 (#24491)
Resolves #20373

PR Close #24491
2018-06-27 14:33:26 -07:00
aa753878e6 docs: add explanation for enableResourceInlining (#24644)
PR Close #24644
2018-06-27 14:31:54 -07:00
3513ae40cc docs(aio): fix adding to template driven forms (#23743)
PR Close #23743
2018-06-26 11:03:36 -07:00
979e7b6086 docs(aio): fix issues suggested by Brandon (#23743)
PR Close #23743
2018-06-26 11:03:36 -07:00
41f008d109 docs(aio): fix issues suggested by Kara (#23743)
PR Close #23743
2018-06-26 11:03:36 -07:00
c950410ee1 docs(aio): address pr review issues (#23743)
PR Close #23743
2018-06-26 11:03:36 -07:00
b0fa504d39 docs(aio): add cross field validation example (#23743)
PR Close #23743
2018-06-26 11:03:36 -07:00
426f9710a0 build(docs-infra): support hiding constructors of injectable classes (#24529)
Classes that are injectable often have constructors that should not be
called by the application developer. It is the responsibility of the
injector to instantiate the class and the constructor often contains
private implementation details that may need to change.

This commit removes constructors from the docs for API items that are
both:

a) Marked with an injectable decorator (e.g. Injectable, Directive,
Component, Pipe), and
b) Have no constructor description

This second rule allows the developer to override the removal if there
is something useful to say about the constructor.

Note that "normal" classes such as `angimations/HttpHeaders` do not have
their constructor removed, despite (at this time) having no description.

PR Close #24529
2018-06-26 10:58:11 -07:00
ae01c70bba ci: fix broken build do to bad merge (#24662)
PR Close #24662
2018-06-25 13:11:16 -07:00
3341a97154 docs: test doc for decorator templates (#23902)
PR Close #23902
2018-06-25 10:58:00 -07:00
2056e1f05c Revert "docs: test doc for decorator templates (#23902)"
This reverts commit 3938a8be75.
2018-06-25 10:54:47 -07:00
3938a8be75 docs: test doc for decorator templates (#23902)
PR Close #23902
2018-06-25 10:09:15 -07:00
393db94b8d docs: fix misdirected group links (#24569)
PR Close #24569
2018-06-25 10:03:43 -07:00
65744e4ae1 docs: test api doc for pipes (#24141)
PR Close #24141
2018-06-25 09:37:30 -07:00
56bc86992c test(aio): fix upgrade-phonecat examples e2e tests (#24583)
Closes #19625

PR Close #24583
2018-06-25 09:30:46 -07:00
9426c02648 build: upgrade AngularJS typings (#24583)
Previously, we were using [@types/angularjs][1], which is deprecated and
outdated (hasn't been updated for over two years). This PR switches to
[@types/angular][2], which is regularly updated (based on the
definitions on [DefinitelyTyped][3]).

[1]: https://www.npmjs.com/package/@types/angularjs
[2]: https://www.npmjs.com/package/@types/angular
[3]: https://github.com/DefinitelyTyped/DefinitelyTyped

PR Close #24583
2018-06-25 09:30:46 -07:00
0b356d4163 fix(animations): set animations styles properly on platform-server (#24624)
Animations styles weren't getting properly set on platform-server because of erroneous checks and absence of reflection of style property to attribute on the server.

The fix corrects the check for platform and explicitly reflects the style property to the attribute.

PR Close #24624
2018-06-25 07:58:12 -07:00
a886659444 docs: add guide-angular.wishtack.io to education resources (#24585)
PR Close #24585
2018-06-25 07:57:34 -07:00
3649958595 build(docs-infra): move overload short description above syntax (#24526)
PR Close #24526
2018-06-25 07:56:37 -07:00
8919577c54 docs: update Angular CLI option for sourcemaps (#24437)
PR Close #24437
2018-06-25 07:53:27 -07:00
84eff4219e docs: update lowercase pipe example in "AngularJS to Angular" guide (#24588)
PR Close #24588
2018-06-21 13:14:31 -07:00
c11e84ee18 build(docs-infra): redirect removed webpack guide to v5.angular.io (#24595)
The outdated webpack guide has been removed in #24478, but people might
still try to access it (via direct links or search-engine results).

Instead of returning 404, we will now redirect `/guide/webpack` to the
archived version of the guide at `v5.angular.io/guide/webpack`.

PR Close #24595
2018-06-20 16:51:34 -07:00
1ce5a495d3 docs(aio): add elana olson to contributor.json file (#24579)
Add new contributor, elana olson, to the contributors list.

PR Close #24579
2018-06-20 16:50:54 -07:00
1f532aaa5a feat(docs-infra): Add GitHub and Twitter external icon links to topnav toolbar (#24542)
PR Close #24542
2018-06-20 16:50:15 -07:00
90 changed files with 2416 additions and 902 deletions

View File

@ -1,3 +1,13 @@
<a name="6.0.7"></a>
## [6.0.7](https://github.com/angular/angular/compare/6.0.6...6.0.7) (2018-06-27)
### Bug Fixes
* **animations:** set animations styles properly on platform-server ([#24624](https://github.com/angular/angular/issues/24624)) ([0b356d4](https://github.com/angular/angular/commit/0b356d4))
* **common:** use correct ICU plural for locale mk ([#24659](https://github.com/angular/angular/issues/24659)) ([64a8584](https://github.com/angular/angular/commit/64a8584))
<a name="6.0.6"></a>
## [6.0.6](https://github.com/angular/angular/compare/6.0.5...6.0.6) (2018-06-20)

View File

@ -60,6 +60,8 @@ dist/
!rollup-config.js
aot-compiler/**/*.d.ts
aot-compiler/**/*.factory.d.ts
upgrade-phonecat-2-hybrid/aot/**/*
!upgrade-phonecat-2-hybrid/aot/index.html
# i18n
!i18n/src/systemjs-text-plugin.js

View File

@ -16,6 +16,7 @@ describe('Form Validation Tests', function () {
tests('Template-Driven Form');
bobTests();
crossValidationTests();
});
describe('Reactive form', () => {
@ -25,6 +26,7 @@ describe('Form Validation Tests', function () {
tests('Reactive Form');
bobTests();
crossValidationTests();
});
});
@ -42,7 +44,8 @@ let page: {
powerOption: ElementFinder,
errorMessages: ElementArrayFinder,
heroFormButtons: ElementArrayFinder,
heroSubmitted: ElementFinder
heroSubmitted: ElementFinder,
crossValidationErrorMessage: ElementFinder,
};
function getPage(sectionTag: string) {
@ -59,7 +62,8 @@ function getPage(sectionTag: string) {
powerOption: section.element(by.css('#power option')),
errorMessages: section.all(by.css('div.alert')),
heroFormButtons: buttons,
heroSubmitted: section.element(by.css('.submitted-message'))
heroSubmitted: section.element(by.css('.submitted-message')),
crossValidationErrorMessage: section.element(by.css('.cross-validation-error-message')),
};
}
@ -172,3 +176,29 @@ function bobTests() {
expectFormIsValid();
});
}
function crossValidationTests() {
const emsg = 'Name cannot match alter ego.';
it(`should produce "${emsg}" error after setting name and alter ego to the same value`, function () {
page.nameInput.clear();
page.nameInput.sendKeys('Batman');
page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('Batman');
expectFormIsInvalid();
expect(page.crossValidationErrorMessage.getText()).toBe(emsg);
});
it('should be ok again with different values', function () {
page.nameInput.clear();
page.nameInput.sendKeys('Batman');
page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('Superman');
expectFormIsValid();
expect(page.crossValidationErrorMessage.isPresent()).toBe(false);
});
}

View File

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

View File

@ -0,0 +1,42 @@
/* 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';
@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;
// #docregion form-group
ngOnInit(): void {
// #docregion custom-validator
this.heroForm = new FormGroup({
'name': new FormControl(this.hero.name, [
Validators.required,
Validators.minLength(4),
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
]),
'alterEgo': new FormControl(this.hero.alterEgo),
'power': new FormControl(this.hero.power, Validators.required)
});
// #enddocregion custom-validator
}
get name() { return this.heroForm.get('name'); }
get power() { return this.heroForm.get('power'); }
// #enddocregion form-group
}
// #enddocregion

View File

@ -0,0 +1,5 @@
/* #docregion cross-validation-error-css */
.cross-validation-error input {
border-left: 5px solid red;
}
/* #enddocregion cross-validation-error-css */

View File

@ -7,33 +7,41 @@
<div [hidden]="formDir.submitted">
<div class="form-group">
<div class="cross-validation" [class.cross-validation-error]="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)">
<div class="form-group">
<label for="name">Name</label>
<!-- #docregion name-with-error-msg -->
<input id="name" class="form-control"
formControlName="name" required >
<label for="name">Name</label>
<!-- #docregion name-with-error-msg -->
<input id="name" class="form-control"
formControlName="name" required >
<div *ngIf="name.invalid && (name.dirty || name.touched)"
class="alert alert-danger">
<div *ngIf="name.invalid && (name.dirty || name.touched)"
class="alert alert-danger">
<div *ngIf="name.errors.required">
Name is required.
</div>
<div *ngIf="name.errors.minlength">
Name must be at least 4 characters long.
</div>
<div *ngIf="name.errors.forbiddenName">
Name cannot be Bob.
<div *ngIf="name.errors.required">
Name is required.
</div>
<div *ngIf="name.errors.minlength">
Name must be at least 4 characters long.
</div>
<div *ngIf="name.errors.forbiddenName">
Name cannot be Bob.
</div>
</div>
<!-- #enddocregion name-with-error-msg -->
</div>
<!-- #enddocregion name-with-error-msg -->
</div>
<div class="form-group">
<label for="alterEgo">Alter Ego</label>
<input id="alterEgo" class="form-control"
formControlName="alterEgo" >
<div class="form-group">
<label for="alterEgo">Alter Ego</label>
<input id="alterEgo" class="form-control"
formControlName="alterEgo" >
</div>
<!-- #docregion cross-validation-error-message -->
<div *ngIf="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)" class="cross-validation-error-message alert alert-danger">
Name cannot match alter ego.
</div>
<!-- #enddocregion cross-validation-error-message -->
</div>
<div class="form-group">

View File

@ -1,40 +1,36 @@
/* tslint:disable: member-ordering forin */
// #docplaster
// #docregion
import { Component, OnInit } from '@angular/core';
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';
@Component({
selector: 'app-hero-form-reactive',
templateUrl: './hero-form-reactive.component.html'
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]};
hero = { name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0] };
heroForm: FormGroup;
// #docregion form-group
ngOnInit(): void {
// #docregion custom-validator
this.heroForm = new FormGroup({
'name': new FormControl(this.hero.name, [
Validators.required,
Validators.minLength(4),
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
forbiddenNameValidator(/bob/i)
]),
'alterEgo': new FormControl(this.hero.alterEgo),
'power': new FormControl(this.hero.power, Validators.required)
});
// #enddocregion custom-validator
}, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level
}
get name() { return this.heroForm.get('name'); }
get power() { return this.heroForm.get('power'); }
// #enddocregion form-group
}
// #enddocregion

View File

@ -0,0 +1,25 @@
// #docregion
import { Directive } from '@angular/core';
import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, Validator, ValidatorFn } from '@angular/forms';
// #docregion cross-validation-validator
/** A hero's name can't match the hero's alter ego */
export const identityRevealedValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
const name = control.get('name');
const alterEgo = control.get('alterEgo');
return name && alterEgo && name.value === alterEgo.value ? { 'identityRevealed': true } : null;
};
// #enddocregion cross-validation-validator
// #docregion cross-validation-directive
@Directive({
selector: '[appIdentityRevealed]',
providers: [{ provide: NG_VALIDATORS, useExisting: IdentityRevealedValidatorDirective, multi: true }]
})
export class IdentityRevealedValidatorDirective implements Validator {
validate(control: AbstractControl): ValidationErrors {
return identityRevealedValidator(control)
}
}
// #enddocregion cross-validation-directive

View File

@ -0,0 +1,4 @@
/* #docregion */
.cross-validation-error input {
border-left: 5px solid red;
}

View File

@ -2,41 +2,48 @@
<div class="container">
<h1>Template-Driven Form</h1>
<!-- #docregion form-tag-->
<form #heroForm="ngForm">
<!-- #enddocregion form-tag-->
<!-- #docregion cross-validation-register-validator -->
<form #heroForm="ngForm" appIdentityRevealed>
<!-- #enddocregion cross-validation-register-validator -->
<div [hidden]="heroForm.submitted">
<div class="cross-validation" [class.cross-validation-error]="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)">
<div class="form-group">
<label for="name">Name</label>
<!-- #docregion name-with-error-msg -->
<!-- #docregion name-input -->
<input id="name" name="name" class="form-control"
required minlength="4" appForbiddenName="bob"
[(ngModel)]="hero.name" #name="ngModel" >
<!-- #enddocregion name-input -->
<div class="form-group">
<label for="name">Name</label>
<!-- #docregion name-with-error-msg -->
<!-- #docregion name-input -->
<input id="name" name="name" class="form-control"
required minlength="4" appForbiddenName="bob"
[(ngModel)]="hero.name" #name="ngModel" >
<!-- #enddocregion name-input -->
<div *ngIf="name.invalid && (name.dirty || name.touched)"
class="alert alert-danger">
<div *ngIf="name.invalid && (name.dirty || name.touched)"
class="alert alert-danger">
<div *ngIf="name.errors.required">
Name is required.
</div>
<div *ngIf="name.errors.minlength">
Name must be at least 4 characters long.
</div>
<div *ngIf="name.errors.forbiddenName">
Name cannot be Bob.
</div>
<div *ngIf="name.errors.required">
Name is required.
</div>
<div *ngIf="name.errors.minlength">
Name must be at least 4 characters long.
</div>
<div *ngIf="name.errors.forbiddenName">
Name cannot be Bob.
</div>
<!-- #enddocregion name-with-error-msg -->
</div>
<!-- #enddocregion name-with-error-msg -->
</div>
<div class="form-group">
<label for="alterEgo">Alter Ego</label>
<input id="alterEgo" class="form-control"
name="alterEgo" [(ngModel)]="hero.alterEgo" >
<div class="form-group">
<label for="alterEgo">Alter Ego</label>
<input id="alterEgo" class="form-control"
name="alterEgo" [(ngModel)]="hero.alterEgo" >
</div>
<!-- #docregion cross-validation-error-message -->
<div *ngIf="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)" class="cross-validation-error-message alert alert-danger">
Name cannot match alter ego.
</div>
<!-- #enddocregion cross-validation-error-message -->
</div>
<div class="form-group">
@ -62,5 +69,4 @@
<button (click)="heroForm.resetForm({})">Add new hero</button>
</div>
</form>
</div>

View File

@ -3,9 +3,11 @@
// #docregion
import { Component } from '@angular/core';
// #docregion component
@Component({
selector: 'app-hero-form-template',
templateUrl: './hero-form-template.component.html'
templateUrl: './hero-form-template.component.html',
styleUrls: ['./hero-form-template.component.css'],
})
export class HeroFormTemplateComponent {
@ -14,3 +16,4 @@ export class HeroFormTemplateComponent {
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
}
// #enddocregion

View File

@ -2,6 +2,7 @@
"description": "Validation",
"files":[
"!**/*.d.ts",
"!**/*.js"
"!**/*.js",
"!**/*.[1].*"
]
}

View File

@ -1,16 +1,36 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DashboardComponent } from './dashboard.component';
import { HeroSearchComponent } from '../hero-search/hero-search.component';
import { RouterTestingModule } from '@angular/router/testing';
import { of } from 'rxjs';
import { HEROES } from '../mock-heroes';
import { HeroService } from '../hero.service';
describe('DashboardComponent', () => {
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
let heroService;
let getHeroesSpy;
beforeEach(async(() => {
heroService = jasmine.createSpyObj('HeroService', ['getHeroes']);
getHeroesSpy = heroService.getHeroes.and.returnValue( of(HEROES) );
TestBed.configureTestingModule({
declarations: [ DashboardComponent ]
declarations: [
DashboardComponent,
HeroSearchComponent
],
imports: [
RouterTestingModule.withRoutes([])
],
providers: [
{ provide: HeroService, useValue: heroService }
]
})
.compileComponents();
}));
beforeEach(() => {
@ -22,4 +42,17 @@ describe('DashboardComponent', () => {
it('should be created', () => {
expect(component).toBeTruthy();
});
it('should display "Top Heroes" as headline', () => {
expect(fixture.nativeElement.querySelector('h3').textContent).toEqual('Top Heroes');
});
it('should call heroService', async(() => {
expect(getHeroesSpy.calls.any()).toBe(true);
}));
it('should display 4 links', async(() => {
expect(fixture.nativeElement.querySelectorAll('a').length).toEqual(4);
}));
});

View File

@ -1,14 +1,18 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { HeroSearchComponent } from './hero-search.component';
describe('HeroSearchComponent', () => {
let component: HeroSearchComponent;
let fixture: ComponentFixture<HeroSearchComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HeroSearchComponent ]
declarations: [ HeroSearchComponent ],
imports: [RouterTestingModule.withRoutes([]), HttpClientTestingModule]
})
.compileComponents();
}));

View File

@ -1,6 +1,7 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { HeroesComponent } from './heroes.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
describe('HeroesComponent', () => {
let component: HeroesComponent;
@ -8,7 +9,8 @@ describe('HeroesComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ HeroesComponent ]
declarations: [ HeroesComponent ],
imports: [RouterTestingModule.withRoutes([]), HttpClientTestingModule],
})
.compileComponents();
}));

View File

@ -8,6 +8,7 @@
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false,
"skipLibCheck": true,
"suppressImplicitAnyIndexErrors": true
}
}

View File

@ -8,10 +8,8 @@
"experimentalDecorators": true,
"lib": [ "es2015", "dom" ],
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"typeRoots": [
"./node_modules/@types/"
]
"skipLibCheck": true,
"suppressImplicitAnyIndexErrors": true
},
"compileOnSave": true,
"exclude": [

View File

@ -1,6 +1,6 @@
// #docregion
import { platformBrowser } from '@angular/platform-browser';
import { AppModuleNgFactory } from '../aot/app/app.module.ngfactory';
import { AppModuleNgFactory } from './app.module.ngfactory';
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);

View File

@ -3,7 +3,7 @@
import { ActivatedRoute } from '@angular/router';
// #enddocregion activatedroute
import { Observable } from 'rxjs';
import { Observable, of } from 'rxjs';
import { async, TestBed } from '@angular/core/testing';
@ -21,7 +21,7 @@ function xyzPhoneData(): PhoneData {
class MockPhone {
get(id: string): Observable<PhoneData> {
return Observable.of(xyzPhoneData());
return of(xyzPhoneData());
}
}

View File

@ -2,7 +2,7 @@
// #docregion
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
import { Observable, of } from 'rxjs';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SpyLocation } from '@angular/common/testing';
@ -15,7 +15,7 @@ class ActivatedRouteMock {
class MockPhone {
query(): Observable<PhoneData[]> {
return Observable.of([
return of([
{name: 'Nexus S', snippet: '', images: []},
{name: 'Motorola DROID', snippet: '', images: []}
]);

View File

@ -9,10 +9,8 @@
"lib": ["es2015", "dom"],
"removeComments": false,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"typeRoots": [
"./node_modules/@types/"
]
"skipLibCheck": true,
"suppressImplicitAnyIndexErrors": true
},
"files": [
@ -21,7 +19,6 @@
],
"angularCompilerOptions": {
"genDir": "aot",
"skipMetadataEmit" : true
"skipMetadataEmit" : true
}
}

View File

@ -8,10 +8,8 @@
"experimentalDecorators": true,
"lib": [ "es2015", "dom" ],
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"typeRoots": [
"./node_modules/@types/"
]
"skipLibCheck": true,
"suppressImplicitAnyIndexErrors": true
},
"compileOnSave": true,
"exclude": [

View File

@ -3,7 +3,7 @@
import { ActivatedRoute } from '@angular/router';
// #enddocregion activatedroute
import { Observable } from 'rxjs';
import { Observable, of } from 'rxjs';
import { async, TestBed } from '@angular/core/testing';
@ -21,7 +21,7 @@ function xyzPhoneData(): PhoneData {
class MockPhone {
get(id: string): Observable<PhoneData> {
return Observable.of(xyzPhoneData());
return of(xyzPhoneData());
}
}

View File

@ -2,7 +2,7 @@
// #docregion routestuff
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
import { Observable, of } from 'rxjs';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SpyLocation } from '@angular/common/testing';
@ -17,7 +17,7 @@ class ActivatedRouteMock {
class MockPhone {
query(): Observable<PhoneData[]> {
return Observable.of([
return of([
{name: 'Nexus S', snippet: '', images: []},
{name: 'Motorola DROID', snippet: '', images: []}
]);

View File

@ -8,10 +8,8 @@
"experimentalDecorators": true,
"lib": [ "es2015", "dom" ],
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"typeRoots": [
"./node_modules/@types/"
]
"skipLibCheck": true,
"suppressImplicitAnyIndexErrors": true
},
"compileOnSave": true,
"exclude": [

View File

@ -895,7 +895,7 @@ For more information on pipes, see [Pipes](guide/pipes).
### lowercase
<code-example hideCopy>
&lt;div>{{movie.title | lowercase}}&lt;/div>
&lt;td>{{movie.title | lowercase}}&lt;/td>
</code-example>

View File

@ -97,6 +97,9 @@ You can control your app compilation by providing template compiler options in t
}
}
```
### *enableResourceInlining*
This options tell the compiler to replace the `templateUrl` and `styleUrls` property in all `@Component` decorators with inlined contents in `template` and `styles` properties.
When enabled, the `.js` output of ngc will have no lazy-loaded `templateUrl` or `styleUrls`.
### *skipMetadataEmit*
@ -236,14 +239,14 @@ Tells the compiler to generate all the possible generated files even if they are
how `bazel` rules track file dependencies. It is not recommended to use this option outside of the `bazel`
rules.
### *enableIvy*
### *enableIvy*
Tells the compiler to generate definitions using the Render3 style code generation. This option defaults to `false`.
Tells the compiler to generate definitions using the Render3 style code generation. This option defaults to `false`.
Not all features are supported with this option enabled. It is only supported
for experimentation and testing of Render3 style code generation.
Not all features are supported with this option enabled. It is only supported
for experimentation and testing of Render3 style code generation.
*Note*: Is it not recommended to use this option as it is not yet feature complete with the Render2 code generation.
*Note*: Is it not recommended to use this option as it is not yet feature complete with the Render2 code generation.
## Angular Metadata and AOT

View File

@ -152,7 +152,7 @@ Install `source-map-explorer`:
Build your app for production _including the source maps_
<code-example language="none" class="code-shell">
ng build --prod --sourcemaps
ng build --prod --source-map
</code-example>
List the generated bundles in the `dist/` folder.

View File

@ -31,6 +31,8 @@ Here are a few essential commands for guide page authors.
1. http://localhost:4200/ &mdash; browse to the app running locally.
You can combine `yarn docs-watch` and `yarn start` into one command with `yarn serve-and-sync`.
## Guide pages
All but a few guide pages are [markdown](https://daringfireball.net/projects/markdown/syntax "markdown") files with an `.md` extension.

View File

@ -92,7 +92,7 @@ built-in validators&mdash;this time, in function form. See below:
{@a reactive-component-class}
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.ts" region="form-group" title="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="form-group" title="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
</code-example>
Note that:
@ -148,7 +148,7 @@ at which point the form uses the last value emitted for validation.
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.ts" region="custom-validator" title="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
<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">
</code-example>
### Adding to template-driven forms
@ -208,5 +208,80 @@ set the color of each form control's border.
</code-example>
## Cross field validation
This section shows how to perform cross field validation. It assumes some basic knowledge of creating custom validators.
<div class="l-sub-section">
If you haven't created custom validators before, start by reviewing the [custom validators section](guide/form-validation#custom-validators).
</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.
### Adding to reactive forms
The form has the following structure:
```javascript
const heroForm = new FormGroup({
'name': new FormControl(),
'alterEgo': new FormControl(),
'power': new FormControl()
});
```
Notice that the name and alterEgo are sibling controls. To evaluate both controls in a single custom validator, we should perform the validation in a common ancestor control: the `FormGroup`. That way, we can query the `FormGroup` for the child controls which will allow us to compare their values.
To add a validator to the `FormGroup`, pass the new validator in as the second argument on creation.
```javascript
const heroForm = new FormGroup({
'name': new FormControl(),
'alterEgo': new FormControl(),
'power': new FormControl()
}, { validators: identityRevealedValidator });
```
The validator code is as follows:
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-validator" title="shared/identity-revealed.directive.ts" linenums="false">
</code-example>
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.
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.
Next, to provide better user experience, we show an appropriate error message when the form is invalid.
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="cross-validation-error-message" title="reactive/hero-form-template.component.html" linenums="false">
</code-example>
Note that we check if:
- 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
First we must create a directive that will wrap the validator function. We provide it as the validator using the `NG_VALIDATORS` token. If you are not sure why, or you do not fully understand the syntax, revisit the previous [section](guide/form-validation#adding-to-template-driven-forms).
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-directive" title="shared/identity-revealed.directive.ts" linenums="false">
</code-example>
Next, we have to add the directive to the html template. Since the validator must be registered at the highest level in the form, we put the directive on the `form` tag.
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-register-validator" title="template/hero-form-template.component.html" linenums="false">
</code-example>
To provide better user experience, we show an appropriate error message when the form is invalid.
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-error-message" title="template/hero-form-template.component.html" linenums="false">
</code-example>
Note that we check if:
- 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,
- show a descriptive error message after the user interacted with the form and the validation failed.
**You can run the <live-example></live-example> to see the complete reactive and template-driven example code.**

View File

@ -0,0 +1,352 @@
# Upgrading for Performance
<div class="alert is-helpful">
_Angular_ is the name for the Angular of today and tomorrow.<br />
_AngularJS_ is the name for all 1.x versions of Angular.
</div>
This guide describes some of the built-in tools for efficiently migrating AngularJS projects over to
the Angular platform, one piece at a time. It is very similar to
[Upgrading from AngularJS](guide/upgrade) with the exception that this one uses the {@link
downgradeModule downgradeModule()} helper function instead of the {@link UpgradeModule
UpgradeModule} class. This affects how the app is bootstrapped and how change detection is
propagated between the two frameworks. It allows you to upgrade incrementally while improving the
speed of your hybrid apps and leveraging the latest of Angular in AngularJS apps early in the
process of upgrading.
## Preparation
Before discussing how you can use `downgradeModule()` to create hybrid apps, there are things that
you can do to ease the upgrade process even before you begin upgrading. Because the steps are the
same regardless of how you upgrade, refer to the [Preparation](guide/upgrade#preparation) section of
[Upgrading from AngularJS](guide/upgrade).
## Upgrading with `ngUpgrade`
With the `ngUpgrade` library in Angular you can upgrade an existing AngularJS app incrementally by
building a hybrid app where you can run both frameworks side-by-side. In these hybrid apps you can
mix and match AngularJS and Angular components and services and have them interoperate seamlessly.
That means you don't have to do the upgrade work all at once as there is a natural coexistence
between the two frameworks during the transition period.
### How `ngUpgrade` Works
Regardless of whether you choose `downgradeModule()` or `UpgradeModule`, the basic principles of
upgrading, the mental model behind hybrid apps, and how you use the {@link upgrade/static
upgrade/static} utilities remain the same. For more information, see the
[How `ngUpgrade` Works](guide/upgrade#how-ngupgrade-works) section of
[Upgrading from AngularJS](guide/upgrade).
<div class="alert is-helpful">
The [Change Detection](guide/upgrade#change-detection) section of
[Upgrading from AngularJS](guide/upgrade) only applies to apps that use `UpgradeModule`. Though
you handle change detection differently with `downgradeModule()`, which is the focus of this
guide, reading the [Change Detection](guide/upgrade#change-detection) section provides helpful
context for what follows.
</div>
#### Change Detection with `downgradeModule()`
As mentioned before, one of the key differences between `downgradeModule()` and `UpgradeModule` has
to do with change detection and how it is propagated between the two frameworks.
With `UpgradeModule`, the two change detection systems are tied together more tightly. Whenever
something happens in the AngularJS part of the app, change detection is automatically triggered on
the Angular part and vice versa. This is convenient as it ensures that neither framework misses an
important change. Most of the time, though, these extra change detection runs are unnecessary.
`downgradeModule()`, on the other side, avoids explicitly triggering change detection unless it
knows the other part of the app is interested in the changes. For example, if a downgraded component
defines an `@Input()`, chances are that the app needs to be aware when that value changes. Thus,
`downgradeComponent()` automatically triggers change detection on that component.
In most cases, though, the changes made locally in a particular component are of no interest to the
rest of the app. For example, if the user clicks a button that submits a form, the component usually
handles the result of this action. That being said, there _are_ cases where you want to propagate
changes to some other part of the app that may be controlled by the other framework. In such cases,
you are responsible for notifying the interested parties by manually triggering change detection.
If you want a particular piece of code to trigger change detection in the AngularJS part of the app,
you need to wrap it in
[scope.$apply()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply). Similarly, for
triggering change detection in Angular you would use {@link NgZone#run ngZone.run()}.
In many cases, a few extra change detection runs may not matter much. However, on larger or
change-detection-heavy apps they can have a noticeable impact. By giving you more fine-grained
control over the change detection propagation, `downgradeModule()` allows you to achieve better
performance for your hybrid apps.
## Using `downgradeModule()`
Both AngularJS and Angular have their own concept of modules to help organize an app into cohesive
blocks of functionality.
Their details are quite different in architecture and implementation. In AngularJS, you create a
module by specifying its name and dependencies with
[angular.module()](https://docs.angularjs.org/api/ng/function/angular.module). Then you can add
assets using its various methods. In Angular, you create a class adorned with an {@link NgModule
NgModule} decorator that describes assets in metadata.
In a hybrid app you run both frameworks at the same time. This means that you need at least one
module each from both AngularJS and Angular.
For the most part, you specify the modules in the same way you would for a regular app. Then, you
use the `upgrade/static` helpers to let the two frameworks know about assets they can use from each
other. This is known as "upgrading" and "downgrading".
<div class="alert is-helpful">
<b>Definitions:</b>
- _Upgrading_: The act of making an AngularJS asset, such as a component or service, available to
the Angular part of the app.
- _Downgrading_: The act of making an Angular asset, such as a component or service, available to
the AngularJS part of the app.
</div>
An important part of inter-linking dependencies is linking the two main modules together. This is
where `downgradeModule()` comes in. Use it to create an AngularJS module&mdash;one that you can use
as a dependency in your main AngularJS module&mdash;that will bootstrap your main Angular module and
kick off the Angular part of the hybrid app. In a sense, it "downgrades" an Angular module to an
AngularJS module.
There are a few things to note, though:
1. You don't pass the Angular module directly to `downgradeModule()`. All `downgradeModule()` needs
is a "recipe", for example, a factory function, to create an instance for your module.
2. The Angular module is not instantiated until the app actually needs it.
The following is an example of how you can use `downgradeModule()` to link the two modules.
```ts
// Import `downgradeModule()`.
import { downgradeModule } from '@angular/upgrade/static';
// Use it to downgrade the Angular module to an AngularJS module.
const downgradedModule = downgradeModule(MainAngularModuleFactory);
// Use the downgraded module as a dependency to the main AngularJS module.
angular.module('mainAngularJsModule', [
downgradedModule
]);
```
#### Specifying a factory for the Angular module
As mentioned earlier, `downgradeModule()` needs to know how to instantiate the Angular module. It
needs a recipe. You define that recipe by providing a factory function that can create an instance
of the Angular module. `downgradeModule()` accepts two types of factory functions:
1. `NgModuleFactory`
2. `(extraProviders: StaticProvider[]) => Promise<NgModuleRef>`
When you pass an `NgModuleFactory`, `downgradeModule()` uses it to instantiate the module using
{@link platformBrowser platformBrowser}'s {@link PlatformRef#bootstrapModuleFactory
bootstrapModuleFactory()}, which is compatible with ahead-of-time (AOT) compilation. AOT compilation
helps make your apps load faster. For more about AOT and how to create an `NgModuleFactory`, see the
[Ahead-of-Time Compilation](guide/aot-compiler) guide.
Alternatively, you can pass a plain function, which is expected to return a promise resolving to an
{@link NgModuleRef NgModuleRef} (i.e. an instance of your Angular module). The function is called
with an array of extra {@link StaticProvider Providers} that are expected to be available on the
returned `NgModuleRef`'s {@link Injector Injector}. For example, if you are using {@link
platformBrowser platformBrowser} or {@link platformBrowserDynamic platformBrowserDynamic}, you can
pass the `extraProviders` array to them:
```ts
const bootstrapFn = (extraProviders: StaticProvider[]) => {
const platformRef = platformBrowserDynamic(extraProviders);
return platformRef.bootstrapModule(MainAngularModule);
};
// or
const bootstrapFn = (extraProviders: StaticProvider[]) => {
const platformRef = platformBrowser(extraProviders);
return platformRef.bootstrapModuleFactory(MainAngularModuleFactory);
};
```
Using an `NgModuleFactory` requires less boilerplate and is a good default option as it supports AOT
out-of-the-box. Using a custom function requires slightly more code, but gives you greater
flexibility.
#### Instantiating the Angular module on-demand
Another key difference between `downgradeModule()` and `UpgradeModule` is that the latter requires
you to instantiate both the AngularJS and Angular modules up-front. This means that you have to pay
the cost of instantiating the Angular part of the app, even if you don't use any Angular assets
until later. `downgradeModule()` is again less aggressive. It will only instantiate the Angular part
when it is required for the first time; that is, as soon as it needs to create a downgraded
component.
You could go a step further and not even download the code for the Angular part of the app to the
user's browser until it is needed. This is especially useful when you use Angular on parts of the
hybrid app that are not necessary for the initial rendering or that the user doesn't reach.
A few examples are:
- You use Angular on specific routes only and you don't need it until/if a user visits such a route.
- You use Angular for features that are only visible to specific types of users; for example,
logged-in users, administrators, or VIP members. You don't need to load Angular until a user is
authenticated.
- You use Angular for a feature that is not critical for the initial rendering of the app and you
can afford a small delay in favor of better initial load performance.
### Bootstrapping with `downgradeModule()`
As you might have guessed, you don't need to change anything in the way you bootstrap your existing
AngularJS app. Unlike `UpgradeModule`&mdash;which requires some extra steps&mdash;
`downgradeModule()` is able to take care of bootstrapping the Angular module, as long as you provide
the recipe.
In order to start using any `upgrade/static` APIs, you still need to load the Angular framework as
you would in a normal Angular app. You can see how this can be done with SystemJS by following the
instructions in the [Setup](guide/setup) guide, selectively copying code from the
[QuickStart github repository](https://github.com/angular/quickstart).
You also need to install the `@angular/upgrade` package via `npm install @angular/upgrade --save`
and add a mapping for the `@angular/upgrade/static` package:
<code-example title="system.config.js">
'@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js',
</code-example>
Next, create an `app.module.ts` file and add the following `NgModule` class:
<code-example title="app.module.ts">
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
imports: [
BrowserModule
]
})
export class MainAngularModule {
// Empty placeholder method to satisfy the `Compiler`.
ngDoBootstrap() {}
}
</code-example>
This bare minimum `NgModule` imports `BrowserModule`, the module every Angular browser-based app
must have. It also defines an empty `ngDoBootstrap()` method, to prevent the {@link Compiler
Compiler} from returning errors. This is necessary because the module will not have a `bootstrap`
declaration on its `NgModule` decorator.
<div class="alert is-important">
You do not add a `bootstrap` declaration to the `NgModule` decorator since AngularJS owns the root
template of the app and `ngUpgrade` bootstraps the necessary components.
</div>
You can now link the AngularJS and Angular modules together using `downgradeModule()`.
<code-example title="app.module.ts">
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { downgradeModule } from '@angular/upgrade/static';
const bootstrapFn = (extraProviders: StaticProvider[]) => {
const platformRef = platformBrowserDynamic(extraProviders);
return platformRef.bootstrapModule(MainAngularModule);
};
const downgradedModule = downgradeModule(bootstrapFn);
angular.module('mainAngularJsModule', [
downgradedModule
]);
</code-example>
The existing AngularJS code works as before _and_ you are ready to start adding Angular code.
### Using Components and Injectables
The differences between `downgradeModule()` and `UpgradeModule` end here. The rest of the
`upgrade/static` APIs and concepts work in the exact same way for both types of hybrid apps.
See [Upgrading from AngularJS](guide/upgrade) to learn about:
- [Using Angular Components from AngularJS Code](guide/upgrade#using-angular-components-from-angularjs-code).
- [Using AngularJS Component Directives from Angular Code](guide/upgrade#using-angularjs-component-directives-from-angular-code).
- [Projecting AngularJS Content into Angular Components](guide/upgrade#projecting-angularjs-content-into-angular-components).
- [Transcluding Angular Content into AngularJS Component Directives](guide/upgrade#transcluding-angular-content-into-angularjs-component-directives).
- [Making AngularJS Dependencies Injectable to Angular](guide/upgrade#making-angularjs-dependencies-injectable-to-angular).
- [Making Angular Dependencies Injectable to AngularJS](guide/upgrade#making-angular-dependencies-injectable-to-angularjs).
<div class="alert is-important">
While it is possible to downgrade injectables, downgraded injectables will not be available until
the Angular module is instantiated. In order to be safe, you need to ensure that the downgraded
injectables are not used anywhere _outside_ the part of the app that is controlled by Angular.
For example, it is _OK_ to use a downgraded service in an upgraded component that is only used
from Angular components, but it is _not OK_ to use it in an AngularJS component that may be used
independently of Angular.
</div>
## Using ahead-of-time compilation with hybrid apps
You can take advantage of ahead-of-time (AOT) compilation in hybrid apps just like in any other
Angular app. The setup for a hybrid app is mostly the same as described in the
[Ahead-of-Time Compilation](guide/aot-compiler) guide save for differences in `index.html` and
`main-aot.ts`.
AOT needs to load any AngularJS files that are in the `<script>` tags in the AngularJS `index.html`.
An easy way to copy them is to add each to the `copy-dist-files.js`file.
You also need to pass the generated `MainAngularModuleFactory` to `downgradeModule()` instead of the
custom bootstrap function:
<code-example title="app/main-aot.ts">
import { downgradeModule } from '@angular/upgrade/static';
import { MainAngularModuleNgFactory } from '../aot/app/app.module.ngfactory';
const downgradedModule = downgradeModule(MainAngularModuleNgFactory);
angular.module('mainAngularJsModule', [
downgradedModule
]);
</code-example>
And that is all you need to do to get the full benefit of AOT for hybrid Angular apps.
## Conclusion
This page covered how to use the {@link upgrade/static upgrade/static} package to incrementally
upgrade existing AngularJS apps at your own pace and without impeding further development of the app
for the duration of the upgrade process.
Specifically, this guide showed how you can achieve better performance and greater flexibility in
your hybrid apps by using {@link downgradeModule downgradeModule()} instead of {@link UpgradeModule
UpgradeModule}.
To summarize, the key differentiating factors of `downgradeModule()` are:
1. It allows instantiating or even loading the Angular part lazily, which improves the initial
loading time. In some cases this may waive the cost of running a second framework altogether.
2. It improves performance by avoiding unnecessary change detection runs while giving the developer
greater ability to customize.
3. It does not require you to change how you bootstrap your AngularJS app.
Using `downgradeModule()` is a good option for hybrid apps when you want to keep the AngularJS and
Angular parts less coupled. You can still mix and match components and services from both
frameworks, but you might need to manually propagate change detection. In return,
`downgradeModule()` offers more control and better performance.

View File

@ -1,7 +1,7 @@
# Upgrading from AngularJS to Angular
_Angular_ is the name for the Angular of today and tomorrow.
_AngularJS_ is the name for all v1.x versions of Angular.
_Angular_ is the name for the Angular of today and tomorrow.<br />
_AngularJS_ is the name for all 1.x versions of Angular.
AngularJS apps are great.
Always consider the business case before moving to Angular.
@ -195,7 +195,7 @@ transition period.
### How ngUpgrade Works
The primary tool provided by ngUpgrade is called the `UpgradeModule`.
One of the primary tools provided by ngUpgrade is called the `UpgradeModule`.
This is a module that contains utilities for bootstrapping and managing hybrid
applications that support both Angular and AngularJS code.
@ -252,7 +252,7 @@ frameworks in how it actually works.
</table>
Even accounting for these differences you can still have dependency injection
interoperability. The `UpgradeModule` resolves the differences and makes
interoperability. `upgrade/static` resolves the differences and makes
everything work seamlessly:
* You can make AngularJS services available for injection to Angular code
@ -569,7 +569,7 @@ So, you can write an Angular component and then use it from AngularJS
code. This is useful when you start to migrate from lower-level
components and work your way up. But in some cases it is more convenient
to do things in the opposite order: To start with higher-level components
and work your way down. This too can be done using the `UpgradeModule`.
and work your way down. This too can be done using the `upgrade/static`.
You can *upgrade* AngularJS component directives and then use them from
Angular.
@ -710,7 +710,7 @@ and then provide the input and output using Angular template syntax:
When you are using a downgraded Angular component from an AngularJS
template, the need may arise to *transclude* some content into it. This
is also possible. While there is no such thing as transclusion in Angular,
there is a very similar concept called *content projection*. The `UpgradeModule`
there is a very similar concept called *content projection*. `upgrade/static`
is able to make these two features interoperate.
Angular components that support content projection make use of an `<ng-content>`

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -645,5 +645,13 @@
"website": "https://kmaida.io/",
"bio": "Kim is an an Angular consultant, developer, speaker, writer, and Google Developer Expert. She is passionate about learning from and sharing knowledge with other developers through blogging, speaking, workshops, and open source.",
"group": "GDE"
},
"elanaolson": {
"name": "Elana Olson",
"picture": "elanaolson.jpg",
"twitter": "elanathellama",
"bio": "Elana is a Developer Relations intern on the Angular team at Google. She is working on migration paths from AngularJS to Angular and would love to chat about your experience with upgrading.",
"group": "Angular"
}
}

View File

@ -490,6 +490,13 @@
"rev": true,
"title": "Angular-Buch (German)",
"url": "https://angular-buch.com/"
},
"wishtack-guide-angular": {
"desc": "The free, open-source and up-to-date Angular guide. This pragmatic guide is focused on best practices and will drive you from scratch to cloud.",
"logo": "https://raw.githubusercontent.com/wishtack/gitbook-guide-angular/master/.gitbook/assets/wishtack-logo-with-text.png",
"rev": true,
"title": "The Angular Guide by Wishtack (Français)",
"url": "https://guide-angular.wishtack.io/"
}
}
},

View File

@ -514,6 +514,11 @@
"title": "Upgrading Instructions",
"tooltip": "Incrementally upgrade an AngularJS application to Angular."
},
{
"url": "guide/upgrade-performance",
"title": "Upgrading for Performance",
"tooltip": "Upgrade from AngularJS to Angular in a more flexible way."
},
{
"url": "guide/ajs-quick-reference",
"title": "AngularJS-Angular Concepts",

View File

@ -15,7 +15,7 @@
// A random bad indexed page that used `api/api`
{"type": 301, "source": "/api/api/:rest*", "destination": "/api/:rest*"},
// Guide renames
// Guide renames/removals
{"type": 301, "source": "/docs/*/latest/cli-quickstart.html", "destination": "/guide/quickstart"},
{"type": 301, "source": "/docs/*/latest/glossary.html", "destination": "/guide/glossary"},
{"type": 301, "source": "/docs/*/latest/quickstart.html", "destination": "/guide/quickstart"},
@ -25,6 +25,7 @@
{"type": 301, "source": "/guide/service-worker-getstart", "destination": "/guide/service-worker-getting-started"},
{"type": 301, "source": "/guide/service-worker-comm", "destination": "/guide/service-worker-communications"},
{"type": 301, "source": "/guide/service-worker-configref", "destination": "/guide/service-worker-config"},
{"type": 301, "source": "/guide/webpack", "destination": "https://v5.angular.io/guide/webpack"},
// some top level guide pages on old site were moved below the guide folder
{"type": 301, "source": "/styleguide", "destination": "/guide/styleguide"},

View File

@ -18,7 +18,7 @@
"routing": {
"index": "/index.html",
"routes": {
"^(?!/styleguide|/docs/.|(?:/guide/(?:cli-quickstart|metadata|ngmodule|service-worker-(?:getstart|comm|configref)|learning-angular)|/news)(?:\\.html|/)?$|/testing|/api/(?:.+/[^/]+-|platform-browser/AnimationDriver|testing/|api/|animate/|(?:common/(?:NgModel|Control|MaxLengthValidator))|(?:[^/]+/)?(?:NgFor(?:$|-)|AnimationStateDeclarationMetadata|CORE_DIRECTIVES|PLATFORM_PIPES|DirectiveMetadata|HTTP_PROVIDERS))|.*/stackblitz(?:\\.html)?(?:\\?.*)?$|.*\\.[^\/.]+$)": {
"^(?!/styleguide|/docs/.|(?:/guide/(?:cli-quickstart|metadata|ngmodule|service-worker-(?:getstart|comm|configref)|learning-angular|webpack)|/news)(?:\\.html|/)?$|/testing|/api/(?:.+/[^/]+-|platform-browser/AnimationDriver|testing/|api/|animate/|(?:common/(?:NgModel|Control|MaxLengthValidator))|(?:[^/]+/)?(?:NgFor(?:$|-)|AnimationStateDeclarationMetadata|CORE_DIRECTIVES|PLATFORM_PIPES|DirectiveMetadata|HTTP_PROVIDERS))|.*/stackblitz(?:\\.html)?(?:\\?.*)?$|.*\\.[^\/.]+$)": {
"match": "regex"
}
}

View File

@ -32,7 +32,7 @@
"test-pwa-score": "node scripts/test-pwa-score",
"example-e2e": "yarn example-check-local && node ./tools/examples/run-example-e2e",
"example-lint": "tslint -c \"content/examples/tslint.json\" \"content/examples/**/*.ts\" -e \"content/examples/styleguide/**/*.avoid.ts\"",
"example-use-local": "node tools/ng-packages-installer overwrite ./tools/examples/shared",
"example-use-local": "node tools/ng-packages-installer overwrite ./tools/examples/shared --debug",
"example-use-npm": "node tools/ng-packages-installer restore ./tools/examples/shared",
"example-check-local": "node tools/ng-packages-installer check ./tools/examples/shared",
"deploy-preview": "scripts/deploy-preview.sh",

View File

@ -27,6 +27,12 @@
</a>
<aio-top-menu *ngIf="isSideBySide" [nodes]="topMenuNodes"></aio-top-menu>
<aio-search-box class="search-container" #searchBox (onSearch)="doSearch($event)" (onFocus)="doSearch($event)"></aio-search-box>
<div class="toolbar-external-icons-container">
<a href="https://twitter.com/angular" title="Twitter">
<img src="assets/images/logos/twitter-icon.svg"></a>
<a href="https://github.com/angular/angular" title="GitHub">
<img src="assets/images/logos/github-icon.svg"></a>
</div>
</mat-toolbar-row>
</mat-toolbar>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 51.8 50.4" style="enable-background:new 0 0 51.8 50.4;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M25.9,0.2C11.8,0.2,0.3,11.7,0.3,25.8c0,11.3,7.3,20.9,17.5,24.3c1.3,0.2,1.7-0.6,1.7-1.2c0-0.6,0-2.6,0-4.8
c-7.1,1.5-8.6-3-8.6-3c-1.2-3-2.8-3.7-2.8-3.7c-2.3-1.6,0.2-1.6,0.2-1.6c2.6,0.2,3.9,2.6,3.9,2.6c2.3,3.9,6,2.8,7.5,2.1
c0.2-1.7,0.9-2.8,1.6-3.4c-5.7-0.6-11.7-2.8-11.7-12.7c0-2.8,1-5.1,2.6-6.9c-0.3-0.7-1.1-3.3,0.3-6.8c0,0,2.1-0.7,7,2.6
c2-0.6,4.2-0.9,6.4-0.9c2.2,0,4.4,0.3,6.4,0.9c4.9-3.3,7-2.6,7-2.6c1.4,3.5,0.5,6.1,0.3,6.8c1.6,1.8,2.6,4.1,2.6,6.9
c0,9.8-6,12-11.7,12.6c0.9,0.8,1.7,2.4,1.7,4.7c0,3.4,0,6.2,0,7c0,0.7,0.5,1.5,1.8,1.2c10.2-3.4,17.5-13,17.5-24.3
C51.5,11.7,40.1,0.2,25.9,0.2z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 49.7" style="enable-background:new 0 0 50 49.7;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<path class="st0" d="M50,9.3c-1.8,0.8-3.8,1.4-5.9,1.6c2.1-1.3,3.7-3.3,4.5-5.7c-2,1.2-4.2,2-6.5,2.5c-1.9-2-4.5-3.2-7.5-3.2
c-5.7,0-10.3,4.6-10.3,10.3c0,0.8,0.1,1.6,0.3,2.3C16.1,16.7,8.5,12.6,3.5,6.4c-0.9,1.5-1.4,3.3-1.4,5.2c0,3.6,1.8,6.7,4.6,8.5
C5,20,3.4,19.6,2,18.8c0,0,0,0.1,0,0.1c0,5,3.5,9.1,8.2,10.1c-0.9,0.2-1.8,0.4-2.7,0.4c-0.7,0-1.3-0.1-1.9-0.2
c1.3,4.1,5.1,7,9.6,7.1c-3.5,2.8-7.9,4.4-12.7,4.4c-0.8,0-1.6,0-2.4-0.1c4.5,2.9,9.9,4.6,15.7,4.6c18.9,0,29.2-15.6,29.2-29.2
c0-0.4,0-0.9,0-1.3C46.9,13.2,48.6,11.4,50,9.3z"/>
</svg>

After

Width:  |  Height:  |  Size: 937 B

View File

@ -37,5 +37,9 @@
padding: 16px 24px;
}
}
.short-description {
margin-left: 0;
}
}
}

View File

@ -2,7 +2,6 @@
$hamburgerShownMargin: 0 8px 0 0;
$hamburgerHiddenMargin: 0 16px 0 -88px;
// DOCS PAGE / STANDARD: TOPNAV TOOLBAR FIXED
mat-toolbar.mat-toolbar {
position: fixed;
@ -10,7 +9,7 @@ mat-toolbar.mat-toolbar {
right: 0;
left: 0;
z-index: 10;
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.30);
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.3);
mat-toolbar-row {
padding: 0 16px 0 0;
@ -21,7 +20,6 @@ mat-toolbar.mat-toolbar {
}
}
// HOME PAGE OVERRIDE: TOPNAV TOOLBAR
aio-shell.page-home mat-toolbar.mat-toolbar {
background-color: $blue;
@ -29,12 +27,11 @@ aio-shell.page-home mat-toolbar.mat-toolbar {
@media (min-width: 481px) {
&:not(.transitioning) {
background-color: transparent;
transition: background-color .2s linear;
transition: background-color 0.2s linear;
}
}
}
// MARKETING PAGES OVERRIDE: TOPNAV TOOLBAR AND HAMBURGER
aio-shell.page-home mat-toolbar.mat-toolbar,
aio-shell.page-features mat-toolbar.mat-toolbar,
@ -48,7 +45,6 @@ aio-shell.page-resources mat-toolbar.mat-toolbar {
}
}
// DOCS PAGES OVERRIDE: HAMBURGER
aio-shell.folder-api mat-toolbar.mat-toolbar,
aio-shell.folder-docs mat-toolbar.mat-toolbar,
@ -62,7 +58,6 @@ aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
}
}
// HAMBURGER BUTTON
.hamburger.mat-button {
height: 100%;
@ -70,9 +65,9 @@ aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
padding: 0;
&:not(.starting) {
transition-duration: .4s;
transition-duration: 0.4s;
transition-property: color, margin;
transition-timing-function: cubic-bezier(.25, .8, .25, 1);
transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
}
@media (min-width: 992px) {
@ -91,7 +86,6 @@ aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
}
}
// HOME NAV-LINK
.nav-link.home {
cursor: pointer;
@ -104,7 +98,7 @@ aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
top: 12px;
height: 40px;
@media(max-width: 992px) {
@media (max-width: 992px) {
&:hover {
transform: scale(1.1);
}
@ -112,7 +106,6 @@ aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
}
}
// TOP MENU
aio-top-menu {
display: flex;
@ -158,7 +151,6 @@ aio-top-menu {
}
}
// SEARCH BOX
aio-search-box.search-container {
display: flex;
@ -181,7 +173,7 @@ aio-search-box.search-container {
-webkit-appearance: none;
&:focus {
outline: none;
outline: none;
}
@include bp(big) {
@ -196,3 +188,25 @@ aio-search-box.search-container {
}
}
}
// EXTERNAL LINK ICONS
.app-toolbar {
.toolbar-external-icons-container {
display: flex;
flex-direction: row;
a {
display: flex;
align-items: center;
margin-left: 16px;
&:hover {
opacity: 0.8;
}
img {
height: 24px;
}
}
}
}

View File

@ -178,6 +178,7 @@
/guide/service-worker-getstart /guide/service-worker-getting-started
/guide/service-worker-comm /guide/service-worker-communications
/guide/service-worker-configref /guide/service-worker-config
/guide/webpack https://v5.angular.io/guide/webpack
/news https://blog.angular.io/
/news.html https://blog.angular.io/
/testing /guide/testing

View File

@ -18,7 +18,6 @@ const EXAMPLE_CONFIG_FILENAME = 'example-config.json';
const IGNORED_EXAMPLES = [ // temporary ignores
'quickstart',
'setup',
'upgrade-p'
];
/**

View File

@ -8,10 +8,8 @@
"experimentalDecorators": true,
"lib": [ "es2015", "dom" ],
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"typeRoots": [
"../node_modules/@types/"
]
"skipLibCheck": true,
"suppressImplicitAnyIndexErrors": true
},
"compileOnSave": true,
"exclude": [

View File

@ -45,16 +45,15 @@
"@angular/compiler-cli": "^6.0.0",
"@angular/language-service": "^6.0.0",
"@angular/platform-server": "^6.0.0",
"@types/angular": "^1.5.16",
"@types/angular-animate": "^1.5.5",
"@types/angular-cookies": "^1.4.2",
"@types/angular-mocks": "^1.5.5",
"@types/angular-resource": "^1.5.6",
"@types/angular-route": "^1.3.2",
"@types/angular-sanitize": "^1.3.3",
"@types/angular": "^1.6.47",
"@types/angular-animate": "^1.5.10",
"@types/angular-mocks": "^1.6.0",
"@types/angular-resource": "^1.5.14",
"@types/angular-route": "^1.3.5",
"@types/express": "^4.0.35",
"@types/jasmine": "~2.8.0",
"@types/jasminewd2": "^2.0.3",
"@types/jquery": "^3.3.4",
"@types/node": "^6.0.45",
"canonical-path": "0.0.2",
"concurrently": "^3.0.0",

View File

@ -240,45 +240,33 @@
version "0.7.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
"@types/angular-animate@^1.5.5":
version "1.5.8"
resolved "https://registry.yarnpkg.com/@types/angular-animate/-/angular-animate-1.5.8.tgz#578e058ee0ca5539e1795421a91ae2f52581dc8f"
"@types/angular-animate@^1.5.10":
version "1.5.10"
resolved "https://registry.yarnpkg.com/@types/angular-animate/-/angular-animate-1.5.10.tgz#b94b45358c61163f1478768e8b081c76439c515f"
dependencies:
"@types/angular" "*"
"@types/angular-cookies@^1.4.2":
version "1.4.5"
resolved "https://registry.yarnpkg.com/@types/angular-cookies/-/angular-cookies-1.4.5.tgz#f5ccf5f42a7b9f4d13e77afb8722034ea9f40bd3"
"@types/angular-mocks@^1.6.0":
version "1.6.0"
resolved "https://registry.yarnpkg.com/@types/angular-mocks/-/angular-mocks-1.6.0.tgz#bd32f55b678c239880d2d0d9a3a79b5cad45547e"
dependencies:
"@types/angular" "*"
"@types/angular-mocks@^1.5.5":
version "1.5.11"
resolved "https://registry.yarnpkg.com/@types/angular-mocks/-/angular-mocks-1.5.11.tgz#d5bbefbf742f2196071bda0fe051878b6f4fd72c"
dependencies:
"@types/angular" "*"
"@types/angular-resource@^1.5.6":
"@types/angular-resource@^1.5.14":
version "1.5.14"
resolved "https://registry.yarnpkg.com/@types/angular-resource/-/angular-resource-1.5.14.tgz#902f34e8c98f708ae99493c6d416b39b4a22d9fe"
dependencies:
"@types/angular" "*"
"@types/angular-route@^1.3.2":
version "1.3.4"
resolved "https://registry.yarnpkg.com/@types/angular-route/-/angular-route-1.3.4.tgz#10d3f7eb313fb8a4b832041f9401869803dcd4df"
"@types/angular-route@^1.3.5":
version "1.3.5"
resolved "https://registry.yarnpkg.com/@types/angular-route/-/angular-route-1.3.5.tgz#78b8e0b069d5efe55ec7072461f4e2f6ae20767b"
dependencies:
"@types/angular" "*"
"@types/angular-sanitize@^1.3.3":
version "1.3.6"
resolved "https://registry.yarnpkg.com/@types/angular-sanitize/-/angular-sanitize-1.3.6.tgz#fec2bd040d38708e46f02e66fba5199e8a64b22e"
dependencies:
"@types/angular" "*"
"@types/angular@*", "@types/angular@^1.5.16":
version "1.6.36"
resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.6.36.tgz#15e73d632274b5655a391470844863548c7755f4"
"@types/angular@*", "@types/angular@^1.6.47":
version "1.6.47"
resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.6.47.tgz#f7a31279a02c0892ed9aa76aae2da1b17791bacd"
"@types/body-parser@*":
version "1.16.7"
@ -315,6 +303,10 @@
dependencies:
"@types/jasmine" "*"
"@types/jquery@^3.3.4":
version "3.3.4"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.4.tgz#f1850fb9a70041a14ace4f81a7ed782db8548317"
"@types/mime@*":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b"

View File

@ -32,6 +32,7 @@ module.exports = new Package('angular-api', [basePackage, typeScriptPackage])
.processor(require('./processors/computeSearchTitle'))
.processor(require('./processors/simplifyMemberAnchors'))
.processor(require('./processors/computeStability'))
.processor(require('./processors/removeInjectableConstructors'))
/**
* These are the API doc types that will be rendered to actual files.

View File

@ -0,0 +1,18 @@
module.exports = function removeInjectableConstructors() {
return {
$runAfter: ['processing-docs', 'splitDescription'],
$runBefore: ['docs-processed'],
injectableDecorators: ['Injectable', 'Directive', 'Component', 'Pipe', 'NgModule'],
$process(docs) {
docs.forEach(doc => {
if (
doc.constructorDoc &&
!doc.constructorDoc.shortDescription &&
doc.decorators &&
doc.decorators.some(decorator => this.injectableDecorators.indexOf(decorator.name) !== -1)) {
delete doc.constructorDoc;
}
});
}
};
};

View File

@ -0,0 +1,58 @@
const processorFactory = require('./removeInjectableConstructors');
const testPackage = require('../../helpers/test-package');
const Dgeni = require('dgeni');
describe('removeInjectableConstructors processor', () => {
it('should be available on the injector', () => {
const dgeni = new Dgeni([testPackage('angular-api-package')]);
const injector = dgeni.configureInjector();
const processor = injector.get('removeInjectableConstructors');
expect(processor.$process).toBeDefined();
expect(processor.$runAfter).toEqual(['processing-docs', 'splitDescription']);
expect(processor.$runBefore).toEqual(['docs-processed']);
});
it('should remove undocumented constructors from docs that have an "Injectable" decorator on them', () => {
const processor = processorFactory();
const docs = [
{ constructorDoc: {} },
{ constructorDoc: {}, decorators: [] },
{ constructorDoc: {}, decorators: [{ name: 'Injectable' }] },
{ constructorDoc: {}, decorators: [{ name: 'Component' }] },
{ constructorDoc: {}, decorators: [{ name: 'Directive' }] },
{ constructorDoc: {}, decorators: [{ name: 'Pipe' }] },
{ constructorDoc: {}, decorators: [{ name: 'Other' }, { name: 'Injectable' }] },
{ constructorDoc: {}, decorators: [{ name: 'Other' }] },
{ constructorDoc: { shortDescription: 'Blah' } },
{ constructorDoc: { shortDescription: 'Blah' }, decorators: [] },
{ constructorDoc: { shortDescription: 'Blah' }, decorators: [{ name: 'Injectable' }] },
{ constructorDoc: { shortDescription: 'Blah' }, decorators: [{ name: 'Component' }] },
{ constructorDoc: { shortDescription: 'Blah' }, decorators: [{ name: 'Directive' }] },
{ constructorDoc: { shortDescription: 'Blah' }, decorators: [{ name: 'Pipe' }] },
{ constructorDoc: { shortDescription: 'Blah' }, decorators: [{ name: 'Other' }, { name: 'Injectable' }] },
{ constructorDoc: { shortDescription: 'Blah' }, decorators: [{ name: 'Other' }] },
];
processor.$process(docs);
expect(docs[0].constructorDoc).toBeDefined();
expect(docs[1].constructorDoc).toBeDefined();
expect(docs[2].constructorDoc).toBeUndefined();
expect(docs[3].constructorDoc).toBeUndefined();
expect(docs[4].constructorDoc).toBeUndefined();
expect(docs[5].constructorDoc).toBeUndefined();
expect(docs[6].constructorDoc).toBeUndefined();
expect(docs[7].constructorDoc).toBeDefined();
expect(docs[8].constructorDoc).toBeDefined();
expect(docs[9].constructorDoc).toBeDefined();
expect(docs[10].constructorDoc).toBeDefined();
expect(docs[11].constructorDoc).toBeDefined();
expect(docs[12].constructorDoc).toBeDefined();
expect(docs[13].constructorDoc).toBeDefined();
expect(docs[14].constructorDoc).toBeDefined();
expect(docs[15].constructorDoc).toBeDefined();
});
});

View File

@ -1,7 +1,7 @@
{% import "lib/memberHelpers.html" as memberHelper -%}
<section class="{$ doc.docType $}-overview">
<code-example language="ts" hideCopy="true">
<code-example language="ts" hideCopy="true" linenums="false">
{% if doc.isAbstract %}abstract {% endif%}{$ doc.docType $} {$ doc.name $}{$ doc.typeParams | escape $}{$ memberHelper.renderHeritage(doc) $} {{$ memberHelper.renderMembers(doc) $}
}
</code-example>

View File

@ -40,13 +40,13 @@
{%- macro renderOverloadInfo(overload, cssClass, method) -%}
<code-example language="ts" hideCopy="true" class="no-box api-heading">{$ renderMemberSyntax(overload) $}</code-example>
{% if overload.shortDescription and (overload.shortDescription != method.shortDescription) %}
<div class="short-description">
{$ overload.shortDescription | marked $}
</div>{% endif %}
<code-example language="ts" hideCopy="true" linenums="false" class="no-box api-heading">{$ renderMemberSyntax(overload) $}</code-example>
<h4 class="no-anchor">Parameters</h4>
{$ params.renderParameters(overload.parameterDocs, cssClass + '-parameters', cssClass + '-parameter') $}

View File

@ -20,7 +20,7 @@
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"target": "es5",
"types": ["angularjs"]
"types": ["angular"]
},
"exclude": [
"angular1_router",

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "6.0.6",
"version": "6.0.7",
"private": true,
"branchPattern": "2.0.*",
"description": "Angular - a web framework for modern web apps",
@ -40,7 +40,7 @@
},
"devDependencies": {
"@bazel/ibazel": "^0.1.1",
"@types/angularjs": "1.5.14-alpha",
"@types/angular": "^1.6.47",
"@types/base64-js": "1.2.5",
"@types/chai": "^4.1.2",
"@types/chokidar": "1.7.3",
@ -65,7 +65,7 @@
"canonical-path": "0.0.2",
"chokidar": "1.7.0",
"clang-format": "1.0.41",
"cldr": "4.8.0",
"cldr": "4.10.0",
"cldr-data-downloader": "0.3.2",
"cldrjs": "0.5.0",
"conventional-changelog": "1.1.0",

View File

@ -14,6 +14,10 @@ export function isBrowser() {
return (typeof window !== 'undefined' && typeof window.document !== 'undefined');
}
export function isNode() {
return (typeof process !== 'undefined');
}
export function optimizeGroupPlayer(players: AnimationPlayer[]): AnimationPlayer {
switch (players.length) {
case 0:
@ -142,11 +146,14 @@ let _query: (element: any, selector: string, multi: boolean) => any[] =
return [];
};
if (isBrowser()) {
// Define utility methods for browsers and platform-server(domino) where Element
// and utility methods exist.
const _isNode = isNode();
if (_isNode || typeof Element !== 'undefined') {
// this is well supported in all browsers
_contains = (elm1: any, elm2: any) => { return elm1.contains(elm2) as boolean; };
if (Element.prototype.matches) {
if (_isNode || Element.prototype.matches) {
_matches = (element: any, selector: string) => element.matches(selector);
} else {
const proto = Element.prototype as any;

View File

@ -8,6 +8,7 @@
import {AnimateTimings, AnimationMetadata, AnimationMetadataType, AnimationOptions, sequence, ɵStyleData} from '@angular/animations';
import {Ast as AnimationAst, AstVisitor as AnimationAstVisitor} from './dsl/animation_ast';
import {AnimationDslVisitor} from './dsl/animation_dsl_visitor';
import {isNode} from './render/shared';
export const ONE_SECOND = 1000;
@ -125,12 +126,47 @@ export function copyStyles(
return destination;
}
function getStyleAttributeString(element: any, key: string, value: string) {
// Return the key-value pair string to be added to the style attribute for the
// given CSS style key.
if (value) {
return key + ':' + value + ';';
} else {
return '';
}
}
function writeStyleAttribute(element: any) {
// Read the style property of the element and manually reflect it to the
// style attribute. This is needed because Domino on platform-server doesn't
// understand the full set of allowed CSS properties and doesn't reflect some
// of them automatically.
let styleAttrValue = '';
for (let i = 0; i < element.style.length; i++) {
const key = element.style.item(i);
styleAttrValue += getStyleAttributeString(element, key, element.style.getPropertyValue(key));
}
for (const key in element.style) {
// Skip internal Domino properties that don't need to be reflected.
if (!element.style.hasOwnProperty(key) || key.startsWith('_')) {
continue;
}
const dashKey = camelCaseToDashCase(key);
styleAttrValue += getStyleAttributeString(element, dashKey, element.style[key]);
}
element.setAttribute('style', styleAttrValue);
}
export function setStyles(element: any, styles: ɵStyleData) {
if (element['style']) {
Object.keys(styles).forEach(prop => {
const camelProp = dashCaseToCamelCase(prop);
element.style[camelProp] = styles[prop];
});
// On the server set the 'style' attribute since it's not automatically reflected.
if (isNode()) {
writeStyleAttribute(element);
}
}
}
@ -140,6 +176,10 @@ export function eraseStyles(element: any, styles: ɵStyleData) {
const camelProp = dashCaseToCamelCase(prop);
element.style[camelProp] = '';
});
// On the server set the 'style' attribute since it's not automatically reflected.
if (isNode()) {
writeStyleAttribute(element);
}
}
}
@ -231,6 +271,10 @@ export function dashCaseToCamelCase(input: string): string {
return input.replace(DASH_CASE_REGEXP, (...m: any[]) => m[1].toUpperCase());
}
function camelCaseToDashCase(input: string): string {
return input.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
export function allowPreviousPlayerStylesMerge(duration: number, delay: number) {
return duration === 0 || delay === 0;
}

View File

@ -13,6 +13,7 @@
"files": [
"public_api.ts",
"../../../node_modules/@types/node/index.d.ts",
"../../../node_modules/zone.js/dist/zone.js.d.ts",
"../../system.d.ts"
],

View File

@ -40,11 +40,12 @@ export declare type AnimateTimings = {
/**
* @description Options that control animation styling and timing.
*
* The following animation functions accept `AnimationOptions` data:
*
* - `transition()`
* - `sequence()`
* - `group()`
* - `{@link animations/group group()}`
* - `query()`
* - `animation()`
* - `useAnimation()`
@ -100,7 +101,7 @@ export const enum AnimationMetadataType {
Sequence = 2,
/**
* Contains a set of animation steps.
* See `group()`
* See `{@link animations/group group()}`
*/
Group = 3,
/**
@ -352,7 +353,7 @@ export interface AnimationSequenceMetadata extends AnimationMetadata {
/**
* Encapsulates an animation group.
* Instantiated and returned by the `group()` function.
* Instantiated and returned by the `{@link animations/group group()}` function.
*/
export interface AnimationGroupMetadata extends AnimationMetadata {
/**
@ -579,7 +580,7 @@ export function trigger(name: string, definitions: AnimationMetadata[]): Animati
* @returns An object that encapsulates the animation step.
*
* @usageNotes
* Call within an animation `sequence()`, `group()`, or
* Call within an animation `sequence()`, `{@link animations/group group()}`, or
* `transition()` call to specify an animation step
* that applies given style data to the parent animation for a given amount of time.
*
@ -676,9 +677,9 @@ export function group(
* @usageNotes
* When you pass an array of steps to a
* `transition()` call, the steps run sequentially by default.
* Compare this to the `group()` call, which runs animation steps in parallel.
* Compare this to the `{@link animations/group group()}` call, which runs animation steps in parallel.
*
* When a sequence is used within a `group()` or a `transition()` call,
* When a sequence is used within a `{@link animations/group group()}` or a `transition()` call,
* execution continues to the next instruction only after each of the inner animation
* steps have completed.
*
@ -863,7 +864,7 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
* ...]
* ```
*
* Note that when you call the `sequence()` function within a `group()`
* Note that when you call the `sequence()` function within a `{@link animations/group group()}`
* or a `transition()` call, execution does not continue to the next instruction
* until each of the inner animation steps have completed.
*

View File

@ -19,12 +19,13 @@ import {BrowserXhr, HttpXhrBackend, XhrFactory} from './xhr';
import {HttpXsrfCookieExtractor, HttpXsrfInterceptor, HttpXsrfTokenExtractor, XSRF_COOKIE_NAME, XSRF_HEADER_NAME} from './xsrf';
/**
* An `HttpHandler` that applies a bunch of `HttpInterceptor`s
* An injectable `HttpHandler` that applies multiple interceptors
* to a request before passing it to the given `HttpBackend`.
*
* The interceptors are loaded lazily from the injector, to allow
* interceptors to themselves inject classes depending indirectly
* on `HttpInterceptingHandler` itself.
* @see `HttpInterceptor`
*/
@Injectable()
export class HttpInterceptingHandler implements HttpHandler {
@ -42,6 +43,23 @@ export class HttpInterceptingHandler implements HttpHandler {
}
}
/**
* Constructs an `HttpHandler` that applies interceptors
* to a request before passing it to the given `HttpBackend`.
*
* Use as a factory function within `HttpClientModule`.
*
*
*/
export function interceptingHandler(
backend: HttpBackend, interceptors: HttpInterceptor[] | null = []): HttpHandler {
if (!interceptors) {
return backend;
}
return interceptors.reduceRight(
(next, interceptor) => new HttpInterceptorHandler(next, interceptor), backend);
}
/**
* Factory function that determines where to store JSONP callbacks.
*
@ -58,14 +76,14 @@ export function jsonpCallbackContext(): Object {
}
/**
* `NgModule` which adds XSRF protection support to outgoing requests.
* An NgModule that adds XSRF protection support to outgoing requests.
*
* Provided the server supports a cookie-based XSRF protection system, this
* module can be used directly to configure XSRF protection with the correct
* For a server that supports a cookie-based XSRF protection system,
* use directly to configure XSRF protection with the correct
* cookie and header names.
*
* If no such names are provided, the default is to use `X-XSRF-TOKEN` for
* the header name and `XSRF-TOKEN` for the cookie name.
* If no names are supplied, the default cookie name is `XSRF-TOKEN`
* and the default header name is `X-XSRF-TOKEN`.
*
*
*/
@ -92,8 +110,12 @@ export class HttpClientXsrfModule {
}
/**
* Configure XSRF protection to use the given cookie name or header name,
* or the default names (as described above) if not provided.
* Configure XSRF protection.
* @param options An object that can specify either or both
* cookie name or header name.
* - Cookie name default is `XSRF-TOKEN`.
* - Header name default is `X-XSRF-TOKEN`.
*
*/
static withOptions(options: {
cookieName?: string,
@ -110,7 +132,7 @@ export class HttpClientXsrfModule {
}
/**
* `NgModule` which provides the `HttpClient` and associated services.
* An NgModule that provides the `HttpClient` and associated services.
*
* Interceptors can be added to the chain behind `HttpClient` by binding them
* to the multiprovider for `HTTP_INTERCEPTORS`.
@ -118,12 +140,18 @@ export class HttpClientXsrfModule {
*
*/
@NgModule({
/**
* Optional configuration for XSRF protection.
*/
imports: [
HttpClientXsrfModule.withOptions({
cookieName: 'XSRF-TOKEN',
headerName: 'X-XSRF-TOKEN',
}),
],
/**
* The module provides `HttpClient` itself, and supporting services.
*/
providers: [
HttpClient,
{provide: HttpHandler, useClass: HttpInterceptingHandler},
@ -137,7 +165,7 @@ export class HttpClientModule {
}
/**
* `NgModule` which enables JSONP support in `HttpClient`.
* An NgModule that enables JSONP support in `HttpClient`.
*
* Without this module, Jsonp requests will reach the backend
* with method JSONP, where they'll be rejected.

View File

@ -2947,7 +2947,7 @@ export const locale_lv = [
function plural_mk(n: number): number {
let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length,
f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0;
if (v === 0 && i % 10 === 1 || f % 10 === 1) return 1;
if (v === 0 && i % 10 === 1 && !(i % 100 === 11) || f % 10 === 1 && !(f % 100 === 11)) return 1;
return 5;
}

View File

@ -14,7 +14,7 @@ const u = undefined;
function plural(n: number): number {
let i = Math.floor(Math.abs(n)), v = n.toString().replace(/^[^.]*\.?/, '').length,
f = parseInt(n.toString().replace(/^[^.]*\.?/, ''), 10) || 0;
if (v === 0 && i % 10 === 1 || f % 10 === 1) return 1;
if (v === 0 && i % 10 === 1 && !(i % 100 === 11) || f % 10 === 1 && !(f % 100 === 11)) return 1;
return 5;
}

File diff suppressed because one or more lines are too long

View File

@ -15,11 +15,48 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
* @ngModule CommonModule
* @description
*
* Uses the function {@link formatDate} to format a date according to locale rules.
* Formats a date value according to locale rules.
*
* The following tabled describes the formatting options.
* Only the `en-US` locale data comes with Angular. To localize dates
* in another language, you must import the corresponding locale data.
* See the [I18n guide](guide/i18n#i18n-pipes) for more information.
*
* | Field Type | Format | Description | Example Value |
* @see `formatDate()`
*
*
* @usageNotes
*
* The result of this pipe is not reevaluated when the input is mutated. To avoid the need to
* reformat the date on every change-detection cycle, treat the date as an immutable object
* and change the reference when the pipe needs to run again.
*
* ### Pre-defined format options
*
* Examples are given in `en-US` locale.
*
* - `'short'`: equivalent to `'M/d/yy, h:mm a'` (`6/15/15, 9:03 AM`).
* - `'medium'`: equivalent to `'MMM d, y, h:mm:ss a'` (`Jun 15, 2015, 9:03:01 AM`).
* - `'long'`: equivalent to `'MMMM d, y, h:mm:ss a z'` (`June 15, 2015 at 9:03:01 AM
* GMT+1`).
* - `'full'`: equivalent to `'EEEE, MMMM d, y, h:mm:ss a zzzz'` (`Monday, June 15, 2015 at
* 9:03:01 AM GMT+01:00`).
* - `'shortDate'`: equivalent to `'M/d/yy'` (`6/15/15`).
* - `'mediumDate'`: equivalent to `'MMM d, y'` (`Jun 15, 2015`).
* - `'longDate'`: equivalent to `'MMMM d, y'` (`June 15, 2015`).
* - `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` (`Monday, June 15, 2015`).
* - `'shortTime'`: equivalent to `'h:mm a'` (`9:03 AM`).
* - `'mediumTime'`: equivalent to `'h:mm:ss a'` (`9:03:01 AM`).
* - `'longTime'`: equivalent to `'h:mm:ss a z'` (`9:03:01 AM GMT+1`).
* - `'fullTime'`: equivalent to `'h:mm:ss a zzzz'` (`9:03:01 AM GMT+01:00`).
*
* ### Custom format options
*
* You can construct a format string using symbols to specify the components
* of a date-time value, as described in the following table.
* Format details depend on the locale.
* Fields marked with (*) are only available in the extra data set for the given locale.
*
* | Field type | Format | Description | Example Value |
* |--------------------|-------------|---------------------------------------------------------------|------------------------------------------------------------|
* | Era | G, GG & GGG | Abbreviated | AD |
* | | GGGG | Wide | Anno Domini |
@ -75,30 +112,40 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
* | | O, OO & OOO | Short localized GMT format | GMT-8 |
* | | OOOO | Long localized GMT format | GMT-08:00 |
*
* Note that timezone correction is not applied to an ISO string that has no time component, such as "2016-09-19"
*
* When the expression is a ISO string without time (e.g. 2016-09-19) the time zone offset is not
* applied and the formatted text will have the same day, month and year of the expression.
* ### Format examples
*
* WARNINGS:
* - this pipe has only access to en-US locale data by default. If you want to localize the dates
* in another language, you will have to import data for other locales.
* See the ["I18n guide"](guide/i18n#i18n-pipes) to know how to import additional locale
* data.
* - Fields suffixed with * are only available in the extra dataset.
* See the ["I18n guide"](guide/i18n#i18n-pipes) to know how to import extra locale
* data.
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
* Instead users should treat the date as an immutable object and change the reference when the
* pipe needs to re-run (this is to avoid reformatting the date on every change detection run
* which would be an expensive operation).
* These examples transform a date into various formats,
* assuming that `dateObj` is a JavaScript `Date` object for
* year: 2015, month: 6, day: 15, hour: 21, minute: 43, second: 11,
* given in the local time for the `en-US` locale.
*
* ### Examples
* ```
* {{ dateObj | date }} // output is 'Jun 15, 2015'
* {{ dateObj | date:'medium' }} // output is 'Jun 15, 2015, 9:43:11 PM'
* {{ dateObj | date:'shortTime' }} // output is '9:43 PM'
* {{ dateObj | date:'mmss' }} // output is '43:11'
* ```
*
* Assuming `dateObj` is (year: 2015, month: 6, day: 15, hour: 21, minute: 43, second: 11)
* in the _local_ time and locale is 'en-US':
* ### Usage example
*
* {@example common/pipes/ts/date_pipe.ts region='DatePipe'}
* The following component uses a date pipe to display the current date in different formats.
*
* ```
* @Component({
* selector: 'date-pipe',
* template: `<div>
* <p>Today is {{today | date}}</p>
* <p>Or if you prefer, {{today | date:'fullDate'}}</p>
* <p>The time is {{today | date:'h:mm a z'}}</p>
* </div>`
* })
* // Get the current date and time as a date-time value.
* export class DatePipeComponent {
* today: number = Date.now();
* }
* ```
*
*/
// clang-format on
@ -107,29 +154,17 @@ export class DatePipe implements PipeTransform {
constructor(@Inject(LOCALE_ID) private locale: string) {}
/**
* @param value a date object or a number (milliseconds since UTC epoch) or an ISO string
* (https://www.w3.org/TR/NOTE-datetime).
* @param format indicates which date/time components to include. The format can be predefined as
* shown below (all examples are given for `en-US`) or custom as shown in the table.
* - `'short'`: equivalent to `'M/d/yy, h:mm a'` (e.g. `6/15/15, 9:03 AM`).
* - `'medium'`: equivalent to `'MMM d, y, h:mm:ss a'` (e.g. `Jun 15, 2015, 9:03:01 AM`).
* - `'long'`: equivalent to `'MMMM d, y, h:mm:ss a z'` (e.g. `June 15, 2015 at 9:03:01 AM
* GMT+1`).
* - `'full'`: equivalent to `'EEEE, MMMM d, y, h:mm:ss a zzzz'` (e.g. `Monday, June 15, 2015 at
* 9:03:01 AM GMT+01:00`).
* - `'shortDate'`: equivalent to `'M/d/yy'` (e.g. `6/15/15`).
* - `'mediumDate'`: equivalent to `'MMM d, y'` (e.g. `Jun 15, 2015`).
* - `'longDate'`: equivalent to `'MMMM d, y'` (e.g. `June 15, 2015`).
* - `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` (e.g. `Monday, June 15, 2015`).
* - `'shortTime'`: equivalent to `'h:mm a'` (e.g. `9:03 AM`).
* - `'mediumTime'`: equivalent to `'h:mm:ss a'` (e.g. `9:03:01 AM`).
* - `'longTime'`: equivalent to `'h:mm:ss a z'` (e.g. `9:03:01 AM GMT+1`).
* - `'fullTime'`: equivalent to `'h:mm:ss a zzzz'` (e.g. `9:03:01 AM GMT+01:00`).
* @param timezone to be used for formatting the time. It understands UTC/GMT and the continental
* US time zone
* abbreviations, but for general use, use a time zone offset (e.g. `'+0430'`).
* @param locale a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
* default).
* @param value The date expression: a `Date` object, a number
* (milliseconds since UTC epoch), or an ISO string (https://www.w3.org/TR/NOTE-datetime).
* @param format The date/time components to include, using predefined options or a
* custom format string.
* @param timezone A timezone offset (such as `'+0430'`), or a standard
* UTC/GMT or continental US timezone abbreviation. Default is
* the local system timezone of the end-user's machine.
* @param locale A locale code for the locale format rules to use.
* When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default.
* See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app).
* @returns A date string in the desired format.
*/
transform(value: any, format = 'mediumDate', timezone?: string, locale?: string): string|null {
if (value == null || value === '' || value !== value) return null;

View File

@ -12,14 +12,21 @@ import {Pipe, PipeTransform} from '@angular/core';
* @ngModule CommonModule
* @description
*
* Converts value into string using `JSON.stringify`. Useful for debugging.
* Converts a value into its JSON-format representation. Useful for debugging.
*
* ### Example
* @usageNotes
*
* The following component uses a JSON pipe to convert an object
* to JSON format, and displays the string in both formats for comparison.
* {@example common/pipes/ts/json_pipe.ts region='JsonPipe'}
*
*
*/
@Pipe({name: 'json', pure: false})
export class JsonPipe implements PipeTransform {
/**
* @param value A value of any type to convert into a JSON-format string.
*/
transform(value: any): string { return JSON.stringify(value, null, 2); }
}

View File

@ -15,14 +15,19 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
* @ngModule CommonModule
* @description
*
* Uses the function {@link formatNumber} to format a number according to locale rules.
* Transforms a number into a string,
* formatted according to locale rules that determine group sizing and
* separator, decimal-point character, and other locale-specific
* configurations.
*
* Formats a number as text. Group sizing and separator and other locale-specific
* configurations are based on the locale.
* @see `formatNumber()`
*
* ### Example
* @usageNotes
* The following code shows how the pipe transforms numbers
* into text strings, according to various format specifications,
* where the caller's default locale is `en-US`.
*
* {@example common/pipes/ts/number_pipe.ts region='NumberPipe'}
* <code-example path="common/pipes/ts/number_pipe.ts" region='NumberPipe'></code-example>
*
*
*/
@ -31,16 +36,19 @@ export class DecimalPipe implements PipeTransform {
constructor(@Inject(LOCALE_ID) private _locale: string) {}
/**
* @param value a number to be formatted.
* @param digitsInfo a `string` which has a following format: <br>
* @param value The number to be formatted.
* @param digitsInfo Decimal representation options, specified by a string
* in the following format:<br>
* <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>.
* - `minIntegerDigits` is the minimum number of integer digits to use. Defaults to `1`.
* - `minFractionDigits` is the minimum number of digits after the decimal point. Defaults to
* `0`.
* - `maxFractionDigits` is the maximum number of digits after the decimal point. Defaults to
* `3`.
* @param locale a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
* default).
* - `minIntegerDigits`: The minimum number of integer digits before the decimal point.
* Default is `1`.
* - `minFractionDigits`: The minimum number of digits after the decimal point.
* Default is `0`.
* - `maxFractionDigits`: The maximum number of digits after the decimal point.
* Default is `3`.
* @param locale A locale code for the locale format rules to use.
* When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default.
* See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app).
*/
transform(value: any, digitsInfo?: string, locale?: string): string|null {
if (isEmpty(value)) return null;
@ -60,12 +68,19 @@ export class DecimalPipe implements PipeTransform {
* @ngModule CommonModule
* @description
*
* Uses the function {@link formatPercent} to format a number as a percentage according
* to locale rules.
* Transforms a number to a percentage
* string, formatted according to locale rules that determine group sizing and
* separator, decimal-point character, and other locale-specific
* configurations.
*
* ### Example
* @see `formatPercent()`
*
* {@example common/pipes/ts/percent_pipe.ts region='PercentPipe'}
* @usageNotes
* The following code shows how the pipe transforms numbers
* into text strings, according to various format specifications,
* where the caller's default locale is `en-US`.
*
* <code-example path="common/pipes/ts/percent_pipe.ts" region='PercentPipe'></code-example>
*
*
*/
@ -75,10 +90,19 @@ export class PercentPipe implements PipeTransform {
/**
*
* @param value a number to be formatted as a percentage.
* @param digitsInfo see {@link DecimalPipe} for more details.
* @param locale a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
* default).
* @param value The number to be formatted as a percentage.
* @param digitsInfo Decimal representation options, specified by a string
* in the following format:<br>
* <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>.
* - `minIntegerDigits`: The minimum number of integer digits before the decimal point.
* Default is `1`.
* - `minFractionDigits`: The minimum number of digits after the decimal point.
* Default is `0`.
* - `maxFractionDigits`: The maximum number of digits after the decimal point.
* Default is `3`.
* @param locale A locale code for the locale format rules to use.
* When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default.
* See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app).
*/
transform(value: any, digitsInfo?: string, locale?: string): string|null {
if (isEmpty(value)) return null;
@ -98,12 +122,19 @@ export class PercentPipe implements PipeTransform {
* @ngModule CommonModule
* @description
*
* Uses the functions {@link getCurrencySymbol} and {@link formatCurrency} to format a
* number as currency using locale rules.
* Transforms a number to a currency string, formatted according to locale rules
* that determine group sizing and separator, decimal-point character,
* and other locale-specific configurations.
*
* ### Example
* @see `getCurrencySymbol()`
* @see `formatCurrency()`
*
* {@example common/pipes/ts/currency_pipe.ts region='CurrencyPipe'}
* @usageNotes
* The following code shows how the pipe transforms numbers
* into text strings, according to various format specifications,
* where the caller's default locale is `en-US`.
*
* <code-example path="common/pipes/ts/currency_pipe.ts" region='CurrencyPipe'></code-example>
*
*
*/
@ -113,20 +144,31 @@ export class CurrencyPipe implements PipeTransform {
/**
*
* @param value a number to be formatted as currency.
* @param currencyCodeis the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code,
* @param value The number to be formatted as currency.
* @param currencyCode The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code,
* such as `USD` for the US dollar and `EUR` for the euro.
* @param display indicates whether to show the currency symbol, the code or a custom value:
* - `code`: use code (e.g. `USD`).
* - `symbol`(default): use symbol (e.g. `$`).
* - `symbol-narrow`: some countries have two symbols for their currency, one regular and one
* narrow (e.g. the canadian dollar CAD has the symbol `CA$` and the symbol-narrow `$`).
* - `string`: use this value instead of a code or a symbol.
* - boolean (deprecated from v5): `true` for symbol and false for `code`.
* If there is no narrow symbol for the chosen currency, the regular symbol will be used.
* @param digitsInfo see {@link DecimalPipe} for more details.
* @param locale a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
* default).
* @param display The format for the currency indicator. One of the following:
* - `code`: Show the code (such as `USD`).
* - `symbol`(default): Show the symbol (such as `$`).
* - `symbol-narrow`: Use the narrow symbol for locales that have two symbols for their
* currency.
* For example, the Canadian dollar CAD has the symbol `CA$` and the symbol-narrow `$`. If the
* locale has no narrow symbol, uses the standard symbol for the locale.
* - String: Use the given string value instead of a code or a symbol.
* - Boolean (marked deprecated in v5): `true` for symbol and false for `code`.
*
* @param digitsInfo Decimal representation options, specified by a string
* in the following format:<br>
* <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>.
* - `minIntegerDigits`: The minimum number of integer digits before the decimal point.
* Default is `1`.
* - `minFractionDigits`: The minimum number of digits after the decimal point.
* Default is `0`.
* - `maxFractionDigits`: The maximum number of digits after the decimal point.
* Default is `3`.
* @param locale A locale code for the locale format rules to use.
* When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default.
* See [Setting your app locale](guide/i18n#setting-up-the-locale-of-your-app).
*/
transform(
value: any, currencyCode?: string,
@ -167,7 +209,7 @@ function isEmpty(value: any): boolean {
}
/**
* Transforms a string into a number (if needed)
* Transforms a string into a number (if needed).
*/
function strToNumber(value: number | string): number {
// Convert strings to numbers

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ import {TypeDecorator, makeDecorator} from '../util/decorators';
/**
* A wrapper around a module that also includes the providers.
* A wrapper around an NgModule that associates it with the providers.
*
*
*/
@ -24,17 +24,21 @@ export interface ModuleWithProviders {
}
/**
* Interface for schema definitions in @NgModules.
* A schema definition associated with an NgModule.
*
* @see `@NgModule`, `CUSTOM_ELEMENTS_SCHEMA`, `NO_ERRORS_SCHEMA`
*
* @param name The name of a defined schema.
*
* @experimental
*/
export interface SchemaMetadata { name: string; }
/**
* Defines a schema that will allow:
* - any non-Angular elements with a `-` in their name,
* - any properties on elements with a `-` in their name which is the common rule for custom
* elements.
* Defines a schema that allows an NgModule to contain the following:
* - Non-Angular elements named with dash case (`-`).
* - Element properties named with dash case (`-`).
* Dash case is the naming convention for custom elements.
*
*
*/
@ -43,7 +47,7 @@ export const CUSTOM_ELEMENTS_SCHEMA: SchemaMetadata = {
};
/**
* Defines a schema that will allow any property on any element.
* Defines a schema that allows any property on any element.
*
* @experimental
*/
@ -59,7 +63,7 @@ export const NO_ERRORS_SCHEMA: SchemaMetadata = {
*/
export interface NgModuleDecorator {
/**
* Defines an NgModule.
* Marks a class as an NgModule and supplies configuration metadata.
*/
(obj?: NgModule): TypeDecorator;
new (obj?: NgModule): NgModule;
@ -72,12 +76,13 @@ export interface NgModuleDecorator {
*/
export interface NgModule {
/**
* Defines the set of injectable objects that are available in the injector
* The set of injectable objects that are available in the injector
* of this module.
*
* ## Simple Example
* @usageNotes
*
* Here is an example of a class that can be injected:
* The following example defines a class that is injected in
* the HelloWorld NgModule:
*
* ```
* class Greeter {
@ -103,9 +108,12 @@ export interface NgModule {
providers?: Provider[];
/**
* Specifies a list of directives/pipes that belong to this module.
* The set of directives and pipes that belong to this module.
*
* ### Example
* @usageNotes
*
* The following example allows the CommonModule to use the `NgFor`
* directive.
*
* ```javascript
* @NgModule({
@ -118,11 +126,13 @@ export interface NgModule {
declarations?: Array<Type<any>|any[]>;
/**
* Specifies a list of modules whose exported directives/pipes
* should be available to templates in this module.
* This can also contain {@link ModuleWithProviders}.
* The set of NgModules, with or without providers,
* whose exported directives/pipes
* are available to templates in this module.
*
* ### Example
* @usageNotes
*
* The following example allows MainModule to use CommonModule:
*
* ```javascript
* @NgModule({
@ -131,15 +141,18 @@ export interface NgModule {
* class MainModule {
* }
* ```
* @see {@link ModuleWithProviders}
*/
imports?: Array<Type<any>|ModuleWithProviders|any[]>;
/**
* Specifies a list of directives/pipes/modules that can be used within the template
* of any component that is part of an Angular module
* that imports this Angular module.
* The set of directives, pipe, and NgModules that can be used
* within the template of any component that is part of an
* NgModule that imports this NgModule.
*
* ### Example
* @usageNotes
*
* The following example exports the `NgFor` directive from CommonModule.
*
* ```javascript
* @NgModule({
@ -152,49 +165,51 @@ export interface NgModule {
exports?: Array<Type<any>|any[]>;
/**
* Specifies a list of components that should be compiled when this module is defined.
* For each component listed here, Angular will create a {@link ComponentFactory}
* and store it in the {@link ComponentFactoryResolver}.
* The set of components to compile when this NgModule is defined.
* For each component listed here, Angular creates a `ComponentFactory`
* and stores it in the `ComponentFactoryResolver`.
*/
entryComponents?: Array<Type<any>|any[]>;
/**
* Defines the components that should be bootstrapped when
* The set of components that are bootstrapped when
* this module is bootstrapped. The components listed here
* will automatically be added to `entryComponents`.
* are automatically added to `entryComponents`.
*/
bootstrap?: Array<Type<any>|any[]>;
/**
* Elements and properties that are not Angular components nor directives have to be declared in
* the schema.
* The set of schemas that declare elements to be allowed in the NgModule.
* Elements and properties that are neither Angular components nor directives
* must be declared in a schema.
*
* Available schemas:
* - `NO_ERRORS_SCHEMA`: any elements and properties are allowed,
* - `CUSTOM_ELEMENTS_SCHEMA`: any custom elements (tag name has "-") with any properties are
* allowed.
* Allowed value are `NO_ERRORS_SCHEMA` and `CUSTOM_ELEMENTS_SCHEMA`.
*
* @security When using one of `NO_ERRORS_SCHEMA` or `CUSTOM_ELEMENTS_SCHEMA` we're trusting that
* allowed elements (and its properties) securely escape inputs.
* @security When using one of `NO_ERRORS_SCHEMA` or `CUSTOM_ELEMENTS_SCHEMA`
* you must ensure that allowed elements and properties securely escape inputs.
*/
schemas?: Array<SchemaMetadata|any[]>;
/**
* An opaque ID for this module, e.g. a name or a path. Used to identify modules in
* `getModuleFactory`. If left `undefined`, the `NgModule` will not be registered with
* A name or path that uniquely identifies this NgModule in `getModuleFactory`.
* If left `undefined`, the NgModule is not registered with
* `getModuleFactory`.
*/
id?: string;
}
/**
* NgModule decorator and metadata.
*
* Decorator that marks the following class as an NgModule, and supplies
* configuration metadata for it.
*
* @Annotation
*/
export const NgModule: NgModuleDecorator = makeDecorator(
'NgModule', (ngModule: NgModule) => ngModule, undefined, undefined,
/**
* Decorator that marks the following class as an NgModule, and supplies
* configuration metadata for it.
*/
(moduleType: InjectorType<any>, metadata: NgModule) => {
let imports = (metadata && metadata.imports) || [];
if (metadata && metadata.exports) {

View File

@ -9,34 +9,36 @@ set -u -e -o pipefail
# can be used to run each of the examples in isolation via http as well.
#
cd `dirname $0`
(
cd `dirname $0`
DIST="../../dist/examples";
rm -rf -- $DIST
$(npm bin)/tsc -p ./tsconfig-build.json --importHelpers false
DIST="../../dist/examples";
rm -rf -- $DIST
$(npm bin)/tsc -p ./tsconfig-build.json --importHelpers false
mkdir $DIST/vendor/
mkdir $DIST/vendor/
ln -s ../../../dist/packages-dist/ $DIST/vendor/@angular
ln -s ../../../dist/packages-dist/ $DIST/vendor/@angular
for FILE in \
../../../node_modules/angular/angular.js \
../../../node_modules/zone.js/dist/zone.js \
../../../node_modules/zone.js/dist/task-tracking.js \
../../../node_modules/systemjs/dist/system.js \
../../../node_modules/reflect-metadata/Reflect.js \
../../../node_modules/rxjs
do
ln -s $FILE $DIST/vendor/`basename $FILE`
done
for FILE in \
../../../node_modules/angular/angular.js \
../../../node_modules/zone.js/dist/zone.js \
../../../node_modules/zone.js/dist/task-tracking.js \
../../../node_modules/systemjs/dist/system.js \
../../../node_modules/reflect-metadata/Reflect.js \
../../../node_modules/rxjs
do
ln -s $FILE $DIST/vendor/`basename $FILE`
done
for MODULE in `find . -name module.ts`; do
FINAL_DIR_PATH=$DIST/`dirname $MODULE`
for MODULE in `find . -name module.ts`; do
FINAL_DIR_PATH=$DIST/`dirname $MODULE`
echo "==== $MODULE"
cp _common/*.html $FINAL_DIR_PATH
cp $DIST/_common/*.js $FINAL_DIR_PATH
cp $DIST/_common/*.js.map $FINAL_DIR_PATH
echo "==== $MODULE"
cp _common/*.html $FINAL_DIR_PATH
cp $DIST/_common/*.js $FINAL_DIR_PATH
cp $DIST/_common/*.js.map $FINAL_DIR_PATH
find `dirname $MODULE` -name \*.css -exec cp {} $FINAL_DIR_PATH \;
done
find `dirname $MODULE` -name \*.css -exec cp {} $FINAL_DIR_PATH \;
done
)

View File

@ -0,0 +1,66 @@
/**
* @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
*/
/* tslint:disable:no-console */
import {Component, Directive, EventEmitter} from '@angular/core';
// #docregion component-input
@Component({
selector: 'app-bank-account',
inputs: ['bankName', 'id: account-id'],
template: `
Bank Name: {{ bankName }}
Account Id: {{ id }}
`
})
export class BankAccountComponent {
bankName: string;
id: string;
// this property is not bound, and won't be automatically updated by Angular
normalizedBankName: string;
}
@Component({
selector: 'app-my-input',
template: `
<app-bank-account
bankName="RBC"
account-id="4747">
</app-bank-account>
`
})
export class MyInputComponent {
}
// #enddocregion component-input
// #docregion component-output-interval
@Directive({selector: 'app-interval-dir', outputs: ['everySecond', 'fiveSecs: everyFiveSeconds']})
export class IntervalDirComponent {
everySecond = new EventEmitter<string>();
fiveSecs = new EventEmitter<string>();
constructor() {
setInterval(() => this.everySecond.emit('event'), 1000);
setInterval(() => this.fiveSecs.emit('event'), 5000);
}
}
@Component({
selector: 'app-my-output',
template: `
<app-interval-dir
(everySecond)="onEverySecond()"
(everyFiveSeconds)="onEveryFiveSeconds()">
</app-interval-dir>
`
})
export class MyOutputComponent {
onEverySecond() { console.log('second'); }
onEveryFiveSeconds() { console.log('five seconds'); }
}
// #enddocregion component-output-interval

View File

@ -1,8 +1,17 @@
#!/bin/sh
#!/usr/bin/env bash
cd `dirname $0`
./build.sh
set -u -e -o pipefail
gulp serve-examples &
(
cd `dirname $0`
./build.sh
(cd ../../ && NODE_PATH=$NODE_PATH:dist/all $(npm bin)/protractor protractor-examples-e2e.conf.js --bundles=true)
gulp serve-examples &
trap "kill $!" EXIT
(
cd ../../
NODE_PATH=${NODE_PATH:-}:dist/all
$(npm bin)/protractor protractor-examples-e2e.conf.js --bundles=true
)
)

View File

@ -11,7 +11,7 @@
"rxjs/*": ["../../node_modules/rxjs/*"]
},
"outDir": "../../dist/examples",
"types": ["jasmine", "node", "angularjs", "systemjs"]
"types": ["jasmine", "node", "angular", "systemjs"]
},
"include": [

View File

@ -7,16 +7,15 @@
*/
import {browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
import {verifyNoBrowserErrors} from '../../../../../_common/e2e_util';
function loadPage(url: string) {
browser.ng12Hybrid = true;
function loadPage() {
browser.rootEl = 'example-app';
browser.get(url);
browser.get('/upgrade/static/ts/full/');
}
describe('upgrade(static)', () => {
beforeEach(() => { loadPage('/upgrade/static/ts/'); });
describe('upgrade/static (full)', () => {
beforeEach(loadPage);
afterEach(verifyNoBrowserErrors);
it('should render the `ng2-heroes` component', () => {

View File

@ -5,16 +5,25 @@
* 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, DoCheck, ElementRef, EventEmitter, Inject, Injectable, Injector, Input, NgModule, OnChanges, OnDestroy, OnInit, Output, SimpleChanges} from '@angular/core';
// #docplaster
import {Component, Directive, ElementRef, EventEmitter, Inject, Injectable, Injector, Input, NgModule, Output} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {UpgradeComponent, UpgradeModule, downgradeComponent, downgradeInjectable} from '@angular/upgrade/static';
declare var angular: ng.IAngularStatic;
interface Hero {
name: string;
description: string;
}
// #docregion ng1-text-formatter-service
class TextFormatter {
titleCase(value: string) { return value.replace(/((^|\s)[a-z])/g, (_, c) => c.toUpperCase()); }
}
// #enddocregion
// #docregion Angular Stuff
// #docregion ng2-heroes
// This Angular component will be "downgraded" to be used in AngularJS
@ -30,7 +39,7 @@ interface Hero {
<button (click)="addHero.emit()">Add Hero</button>`,
})
class Ng2HeroesComponent {
@Input() heroes: Hero[];
@Input() heroes !: Hero[];
@Output() addHero = new EventEmitter();
@Output() removeHero = new EventEmitter();
}
@ -47,9 +56,9 @@ class HeroesService {
];
// #docregion use-ng1-upgraded-service
constructor(@Inject('titleCase') titleCase: (v: string) => string) {
constructor(textFormatter: TextFormatter) {
// Change all the hero names to title case, using the "upgraded" AngularJS service
this.heroes.forEach((hero: Hero) => hero.name = titleCase(hero.name));
this.heroes.forEach((hero: Hero) => hero.name = textFormatter.titleCase(hero.name));
}
// #enddocregion
@ -65,26 +74,15 @@ class HeroesService {
// #docregion ng1-hero-wrapper
// This Angular directive will act as an interface to the "upgraded" AngularJS component
@Directive({selector: 'ng1-hero'})
class Ng1HeroComponentWrapper extends UpgradeComponent implements OnInit, OnChanges, DoCheck,
OnDestroy {
class Ng1HeroComponentWrapper extends UpgradeComponent {
// The names of the input and output properties here must match the names of the
// `<` and `&` bindings in the AngularJS component that is being wrapped
@Input() hero: Hero;
@Output() onRemove: EventEmitter<void>;
@Input() hero !: Hero;
@Output() onRemove !: EventEmitter<void>;
constructor(@Inject(ElementRef) elementRef: ElementRef, @Inject(Injector) injector: Injector) {
// We must pass the name of the directive as used by AngularJS to the super
super('ng1Hero', elementRef, injector);
}
// For this class to work when compiled with AoT, we must implement these lifecycle hooks
// because the AoT compiler will not realise that the super class implements them
ngOnInit() { super.ngOnInit(); }
ngOnChanges(changes: SimpleChanges) { super.ngOnChanges(changes); }
ngDoCheck() { super.ngDoCheck(); }
ngOnDestroy() { super.ngOnDestroy(); }
}
// #enddocregion
@ -96,7 +94,7 @@ class Ng1HeroComponentWrapper extends UpgradeComponent implements OnInit, OnChan
HeroesService,
// #docregion upgrade-ng1-service
// Register an Angular provider whose value is the "upgraded" AngularJS service
{provide: 'titleCase', useFactory: (i: any) => i.get('titleCase'), deps: ['$injector']}
{provide: TextFormatter, useFactory: (i: any) => i.get('textFormatter'), deps: ['$injector']}
// #enddocregion
],
// All components that are to be "downgraded" must be declared as `entryComponents`
@ -104,18 +102,25 @@ class Ng1HeroComponentWrapper extends UpgradeComponent implements OnInit, OnChan
// We must import `UpgradeModule` to get access to the AngularJS core services
imports: [BrowserModule, UpgradeModule]
})
// #docregion bootstrap-ng1
class Ng2AppModule {
ngDoBootstrap() { /* this is a placeholder to stop the bootstrapper from complaining */
// #enddocregion ng2-module
constructor(private upgrade: UpgradeModule) {}
ngDoBootstrap() {
// We bootstrap the AngularJS app.
this.upgrade.bootstrap(document.body, [ng1AppModule.name]);
}
// #docregion ng2-module
}
// #enddocregion
// #enddocregion bootstrap-ng1
// #enddocregion ng2-module
// #enddocregion
// #docregion Angular 1 Stuff
// #docregion ng1-module
// This Angular 1 module represents the AngularJS pieces of the application
declare var angular: ng.IAngularStatic;
const ng1AppModule = angular.module('ng1AppModule', []);
// #enddocregion
@ -131,11 +136,9 @@ ng1AppModule.component('ng1Hero', {
});
// #enddocregion
// #docregion ng1-title-case-service
// #docregion ng1-text-formatter-service
// This AngularJS service will be "upgraded" to be used in Angular
ng1AppModule.factory(
'titleCase',
(() => (value: string) => value.replace(/((^|\s)[a-z])/g, (_, c) => c.toUpperCase())) as any);
ng1AppModule.service('textFormatter', [TextFormatter]);
// #enddocregion
// #docregion downgrade-ng2-heroes-service
@ -144,7 +147,7 @@ ng1AppModule.factory('heroesService', downgradeInjectable(HeroesService) as any)
// #enddocregion
// #docregion ng2-heroes-wrapper
// This is directive will act as the interface to the "downgraded" Angular component
// This directive will act as the interface to the "downgraded" Angular component
ng1AppModule.directive('ng2Heroes', downgradeComponent({component: Ng2HeroesComponent}));
// #enddocregion
@ -154,30 +157,24 @@ ng1AppModule.component('exampleApp', {
// We inject the "downgraded" HeroesService into this AngularJS component
// (We don't need the `HeroesService` type for AngularJS DI - it just helps with TypeScript
// compilation)
controller:
[
'heroesService',
function(heroesService: HeroesService) { this.heroesService = heroesService; }
],
// This template make use of the downgraded `ng2-heroes` component
// Note that because its element is compiled by AngularJS we must use kebab-case attributes
// for inputs and outputs
template: `<link rel="stylesheet" href="./styles.css">
<ng2-heroes [heroes]="$ctrl.heroesService.heroes" (add-hero)="$ctrl.heroesService.addHero()" (remove-hero)="$ctrl.heroesService.removeHero($event)">
<h1>Heroes</h1>
<p class="extra">There are {{ $ctrl.heroesService.heroes.length }} heroes.</p>
</ng2-heroes>`
} as any);
// #enddocregion
// #enddocregion
// #docregion bootstrap
// First we bootstrap the Angular HybridModule
// (We are using the dynamic browser platform as this example has not been compiled AoT)
platformBrowserDynamic().bootstrapModule(Ng2AppModule).then(ref => {
// Once Angular bootstrap is complete then we bootstrap the AngularJS module
const upgrade = ref.injector.get(UpgradeModule) as UpgradeModule;
upgrade.bootstrap(document.body, [ng1AppModule.name]);
controller: [
'heroesService', function(heroesService: HeroesService) { this.heroesService = heroesService; }
],
// This template makes use of the downgraded `ng2-heroes` component
// Note that because its element is compiled by AngularJS we must use kebab-case attributes
// for inputs and outputs
template: `<link rel="stylesheet" href="./styles.css">
<ng2-heroes [heroes]="$ctrl.heroesService.heroes" (add-hero)="$ctrl.heroesService.addHero()" (remove-hero)="$ctrl.heroesService.removeHero($event)">
<h1>Heroes</h1>
<p class="extra">There are {{ $ctrl.heroesService.heroes.length }} heroes.</p>
</ng2-heroes>`
});
// #enddocregion
// #enddocregion
// #docregion bootstrap-ng2
// We bootstrap the Angular module as we would do in a normal Angular app.
// (We are using the dynamic browser platform as this example has not been compiled AoT.)
platformBrowserDynamic().bootstrapModule(Ng2AppModule);
// #enddocregion

View File

@ -0,0 +1,64 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ElementFinder, by} from 'protractor';
declare global {
namespace jasmine {
interface Matchers {
toBeAHero(): Promise<void>;
toHaveName(exectedName: string): Promise<void>;
}
}
}
const isTitleCased = (text: string) =>
text.split(/\s+/).every(word => word[0] === word[0].toUpperCase());
export function addCustomMatchers() {
jasmine.addMatchers({
toBeAHero:
() => ({
compare(actualNg1Hero: ElementFinder | undefined) {
const getText = (selector: string) =>
actualNg1Hero !.element(by.css(selector)).getText();
const result = {
message: 'Expected undefined to be an `ng1Hero` ElementFinder.',
pass: !!actualNg1Hero &&
Promise.all(['.title', 'h2', 'p'].map(getText) as PromiseLike<string>[])
.then(([actualTitle, actualName, actualDescription]) => {
const pass = (actualTitle === 'Super Hero') && isTitleCased(actualName) &&
(actualDescription.length > 0);
const actualHero =
`Hero(${actualTitle}, ${actualName}, ${actualDescription})`;
result.message =
`Expected ${actualHero}'${pass ? ' not' : ''} to be a real hero.`;
return pass;
})
};
return result;
}
}),
toHaveName: () => ({
compare(actualNg1Hero: ElementFinder | undefined, expectedName: string) {
const result = {
message: 'Expected undefined to be an `ng1Hero` ElementFinder.',
pass: !!actualNg1Hero && actualNg1Hero.element(by.css('h2')).getText().then(actualName => {
const pass = actualName === expectedName;
result.message =
`Expected Hero(${actualName})${pass ? ' not' : ''} to have name '${expectedName}'.`;
return pass;
})
};
return result;
}
}),
} as any);
}

View File

@ -0,0 +1,89 @@
/**
* @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 {ElementArrayFinder, ElementFinder, browser, by, element} from 'protractor';
import {verifyNoBrowserErrors} from '../../../../../_common/e2e_util';
import {addCustomMatchers} from './e2e_util';
function loadPage() {
browser.rootEl = 'example-app';
browser.get('/upgrade/static/ts/lite/');
}
describe('upgrade/static (lite)', () => {
let showHideBtn: ElementFinder;
let ng2Heroes: ElementFinder;
let ng2HeroesHeader: ElementFinder;
let ng2HeroesExtra: ElementFinder;
let ng2HeroesAddBtn: ElementFinder;
let ng1Heroes: ElementArrayFinder;
const expectHeroes = (isShown: boolean, ng1HeroCount = 3, statusMessage = 'Ready') => {
// Verify the show/hide button text.
expect(showHideBtn.getText()).toBe(isShown ? 'Hide heroes' : 'Show heroes');
// Verify the `<ng2-heroes>` component.
expect(ng2Heroes.isPresent()).toBe(isShown);
if (isShown) {
expect(ng2HeroesHeader.getText()).toBe('Heroes');
expect(ng2HeroesExtra.getText()).toBe(`Status: ${statusMessage}`);
}
// Verify the `<ng1-hero>` components.
expect(ng1Heroes.count()).toBe(isShown ? ng1HeroCount : 0);
if (isShown) {
ng1Heroes.each(ng1Hero => expect(ng1Hero).toBeAHero());
}
};
beforeEach(() => {
showHideBtn = element(by.binding('toggleBtnText'));
ng2Heroes = element(by.css('.ng2-heroes'));
ng2HeroesHeader = ng2Heroes.element(by.css('h1'));
ng2HeroesExtra = ng2Heroes.element(by.css('.extra'));
ng2HeroesAddBtn = ng2Heroes.element(by.buttonText('Add Hero'));
ng1Heroes = element.all(by.css('.ng1-hero'));
});
beforeEach(addCustomMatchers);
beforeEach(loadPage);
afterEach(verifyNoBrowserErrors);
it('should initially not render the heroes', () => expectHeroes(false));
it('should toggle the heroes when clicking the "show/hide" button', () => {
showHideBtn.click();
expectHeroes(true);
showHideBtn.click();
expectHeroes(false);
});
it('should add a new hero when clicking the "add" button', () => {
showHideBtn.click();
ng2HeroesAddBtn.click();
expectHeroes(true, 4, 'Added hero Kamala Khan');
expect(ng1Heroes.last()).toHaveName('Kamala Khan');
});
it('should remove a hero when clicking its "remove" button', () => {
showHideBtn.click();
const firstHero = ng1Heroes.first();
expect(firstHero).toHaveName('Superman');
const removeBtn = firstHero.element(by.buttonText('Remove'));
removeBtn.click();
expectHeroes(true, 2, 'Removed hero Superman');
expect(ng1Heroes.first()).not.toHaveName('Superman');
});
});

View File

@ -0,0 +1,220 @@
/**
* @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
*/
// #docplaster
import {Component, Directive, ElementRef, EventEmitter, Inject, Injectable, Injector, Input, NgModule, Output, StaticProvider} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
// #docregion basic-how-to
// Alternatively, we could import and use an `NgModuleFactory` instead:
// import {MyLazyAngularModuleNgFactory} from './my-lazy-angular-module.ngfactory';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
// #enddocregion
/* tslint:disable: no-duplicate-imports */
import {UpgradeComponent} from '@angular/upgrade/static';
import {downgradeComponent} from '@angular/upgrade/static';
import {downgradeInjectable} from '@angular/upgrade/static';
// #docregion basic-how-to
import {downgradeModule} from '@angular/upgrade/static';
// #enddocregion
/* tslint:enable: no-duplicate-imports */
declare var angular: ng.IAngularStatic;
interface Hero {
name: string;
description: string;
}
// This Angular service will use an "upgraded" AngularJS service.
@Injectable()
class HeroesService {
heroes: Hero[] = [
{name: 'superman', description: 'The man of steel'},
{name: 'wonder woman', description: 'Princess of the Amazons'},
{name: 'thor', description: 'The hammer-wielding god'}
];
constructor(@Inject('titleCase') titleCase: (v: string) => string) {
// Change all the hero names to title case, using the "upgraded" AngularJS service.
this.heroes.forEach((hero: Hero) => hero.name = titleCase(hero.name));
}
addHero() {
const newHero: Hero = {name: 'Kamala Khan', description: 'Epic shape-shifting healer'};
this.heroes = this.heroes.concat([newHero]);
return newHero;
}
removeHero(hero: Hero) { this.heroes = this.heroes.filter((item: Hero) => item !== hero); }
}
// This Angular component will be "downgraded" to be used in AngularJS.
@Component({
selector: 'ng2-heroes',
// This template uses the "upgraded" `ng1-hero` component
// (Note that because its element is compiled by Angular we must use camelCased attribute names.)
template: `
<div class="ng2-heroes">
<header><ng-content selector="h1"></ng-content></header>
<ng-content selector=".extra"></ng-content>
<div *ngFor="let hero of this.heroesService.heroes">
<ng1-hero [hero]="hero" (onRemove)="onRemoveHero(hero)">
<strong>Super Hero</strong>
</ng1-hero>
</div>
<button (click)="onAddHero()">Add Hero</button>
</div>
`,
})
class Ng2HeroesComponent {
@Output() private addHero = new EventEmitter<Hero>();
@Output() private removeHero = new EventEmitter<Hero>();
constructor(
@Inject('$rootScope') private $rootScope: ng.IRootScopeService,
public heroesService: HeroesService) {}
onAddHero() {
const newHero = this.heroesService.addHero();
this.addHero.emit(newHero);
// When a new instance of an "upgraded" component - such as `ng1Hero` - is created, we want to
// run a `$digest` to initialize its bindings. Here, the component will be created by `ngFor`
// asynchronously, thus we have to schedule the `$digest` to also happen asynchronously.
this.$rootScope.$applyAsync();
}
onRemoveHero(hero: Hero) {
this.heroesService.removeHero(hero);
this.removeHero.emit(hero);
}
}
// This Angular directive will act as an interface to the "upgraded" AngularJS component.
@Directive({selector: 'ng1-hero'})
class Ng1HeroComponentWrapper extends UpgradeComponent {
// The names of the input and output properties here must match the names of the
// `<` and `&` bindings in the AngularJS component that is being wrapped.
@Input() hero !: Hero;
@Output() onRemove !: EventEmitter<void>;
constructor(elementRef: ElementRef, injector: Injector) {
// We must pass the name of the directive as used by AngularJS to the super.
super('ng1Hero', elementRef, injector);
}
}
// This Angular module represents the Angular pieces of the application.
@NgModule({
imports: [BrowserModule],
declarations: [Ng2HeroesComponent, Ng1HeroComponentWrapper],
providers: [
HeroesService,
// Register an Angular provider whose value is the "upgraded" AngularJS service.
{provide: 'titleCase', useFactory: (i: any) => i.get('titleCase'), deps: ['$injector']}
],
// All components that are to be "downgraded" must be declared as `entryComponents`.
entryComponents: [Ng2HeroesComponent]
// Note that there are no `bootstrap` components, since the "downgraded" component
// will be instantiated by ngUpgrade.
})
class MyLazyAngularModule {
// Empty placeholder method to prevent the `Compiler` from complaining.
ngDoBootstrap() {}
}
// #docregion basic-how-to
// The function that will bootstrap the Angular module (when/if necessary).
// (This would be omitted if we provided an `NgModuleFactory` directly.)
const ng2BootstrapFn = (extraProviders: StaticProvider[]) =>
platformBrowserDynamic(extraProviders).bootstrapModule(MyLazyAngularModule);
// #enddocregion
// (We are using the dynamic browser platform, as this example has not been compiled AoT.)
// #docregion basic-how-to
// This AngularJS module represents the AngularJS pieces of the application.
const myMainAngularJsModule = angular.module('myMainAngularJsModule', [
// We declare a dependency on the "downgraded" Angular module.
downgradeModule(ng2BootstrapFn)
// or
// downgradeModule(MyLazyAngularModuleFactory)
]);
// #enddocregion
// This AngularJS component will be "upgraded" to be used in Angular.
myMainAngularJsModule.component('ng1Hero', {
bindings: {hero: '<', onRemove: '&'},
transclude: true,
template: `
<div class="ng1-hero">
<div class="title" ng-transclude></div>
<h2>{{ $ctrl.hero.name }}</h2>
<p>{{ $ctrl.hero.description }}</p>
<button ng-click="$ctrl.onRemove()">Remove</button>
</div>
`
});
// This AngularJS service will be "upgraded" to be used in Angular.
myMainAngularJsModule.factory(
'titleCase', () => (value: string) => value.replace(/(^|\s)[a-z]/g, m => m.toUpperCase()));
// This directive will act as the interface to the "downgraded" Angular component.
myMainAngularJsModule.directive(
'ng2Heroes', downgradeComponent({
component: Ng2HeroesComponent,
// Optionally, disable `$digest` propagation to avoid unnecessary change detection.
// (Change detection is still run when the inputs of a "downgraded" component change.)
propagateDigest: false
}));
// This is our top level application component.
myMainAngularJsModule.component('exampleApp', {
// This template makes use of the "downgraded" `ng2-heroes` component,
// but loads it lazily only when/if the user clicks the button.
// (Note that because its element is compiled by AngularJS,
// we must use kebab-case attributes for inputs and outputs.)
template: `
<link rel="stylesheet" href="./styles.css">
<button ng-click="$ctrl.toggleHeroes()">{{ $ctrl.toggleBtnText() }}</button>
<ng2-heroes
ng-if="$ctrl.showHeroes"
(add-hero)="$ctrl.setStatusMessage('Added hero ' + $event.name)"
(remove-hero)="$ctrl.setStatusMessage('Removed hero ' + $event.name)">
<h1>Heroes</h1>
<p class="extra">Status: {{ $ctrl.statusMessage }}</p>
</ng2-heroes>
`,
controller: function() {
this.showHeroes = false;
this.statusMessage = 'Ready';
this.setStatusMessage = (msg: string) => this.statusMessage = msg;
this.toggleHeroes = () => this.showHeroes = !this.showHeroes;
this.toggleBtnText = () => `${this.showHeroes ? 'Hide' : 'Show'} heroes`;
}
});
// We bootstrap the Angular module as we would do in a normal Angular app.
angular.bootstrap(document.body, [myMainAngularJsModule.name]);

View File

@ -0,0 +1,17 @@
ng2-heroes {
border: solid black 2px;
display: block;
padding: 5px;
}
ng1-hero {
border: solid green 2px;
margin-top: 5px;
padding: 5px;
display: block;
}
.title {
background-color: blue;
color: white;
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AnimationBuilder, animate, style, transition, trigger} from '@angular/animations';
import {AnimationBuilder, animate, state, style, transition, trigger} from '@angular/animations';
import {APP_BASE_HREF, PlatformLocation, isPlatformServer} from '@angular/common';
import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
@ -136,12 +136,21 @@ class SVGServerModule {
@Component({
selector: 'app',
template: '<div @myAnimation>{{text}}</div>',
template: `<div [@myAnimation]="state">{{text}}</div>`,
animations: [trigger(
'myAnimation',
[transition('void => *', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
[
state('void', style({'opacity': '0'})),
state('active', style({
'opacity': '1', // simple supported property
'font-weight': 'bold', // property with dashed name
'transform': 'translate3d(0, 0, 0)', // not natively supported by Domino
})),
transition('void => *', [animate('0ms')]),
], )]
})
class MyAnimationApp {
state = 'active';
constructor(private builder: AnimationBuilder) {}
text = 'Works!';
@ -509,6 +518,8 @@ class EscapedTransferStoreModule {
// PlatformConfig takes in a parsed document so that it can be cached across requests.
doc = '<html><head></head><body><app></app></body></html>';
called = false;
(global as any)['window'] = undefined;
(global as any)['document'] = undefined;
});
afterEach(() => { expect(called).toBe(true); });
@ -561,6 +572,9 @@ class EscapedTransferStoreModule {
renderModule(AnimationServerModule, {document: doc}).then(output => {
expect(output).toContain('Works!');
expect(output).toContain('ng-trigger-myAnimation');
expect(output).toContain('opacity:1;');
expect(output).toContain('transform:translate3d(0, 0, 0);');
expect(output).toContain('font-weight:bold;');
called = true;
});
}));

View File

@ -22,7 +22,7 @@
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"target": "es5",
"types": ["angularjs"]
"types": ["angular"]
},
"bazelOptions": {
"suppressTsconfigOverrideWarnings": true

View File

@ -10,7 +10,7 @@ export type Ng1Token = string;
export type Ng1Expression = string | Function;
export interface IAnnotatedFunction extends Function { $inject?: Ng1Token[]; }
export interface IAnnotatedFunction extends Function { $inject?: ReadonlyArray<Ng1Token>; }
export type IInjectable = (Ng1Token | Function)[] | IAnnotatedFunction;

View File

@ -34,18 +34,23 @@ interface Thenable<T> {
* Let's assume that you have an Angular component called `ng2Heroes` that needs
* to be made available in AngularJS templates.
*
* {@example upgrade/static/ts/module.ts region="ng2-heroes"}
* {@example upgrade/static/ts/full/module.ts region="ng2-heroes"}
*
* We must create an AngularJS [directive](https://docs.angularjs.org/guide/directive)
* that will make this Angular component available inside AngularJS templates.
* The `downgradeComponent()` function returns a factory function that we
* can use to define the AngularJS directive that wraps the "downgraded" component.
*
* {@example upgrade/static/ts/module.ts region="ng2-heroes-wrapper"}
* {@example upgrade/static/ts/full/module.ts region="ng2-heroes-wrapper"}
*
* @param info contains information about the Component that is being downgraded:
*
* * `component: Type<any>`: The type of the Component that will be downgraded
* * `propagateDigest?: boolean`: Whether to perform {@link ChangeDetectorRef#detectChanges
* change detection} on the component on every
* [$digest](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest). If set to `false`,
* change detection will still be performed when any of the component's inputs changes.
* (Default: true)
*
* @returns a factory function that can be used to register the component in an
* AngularJS module.

View File

@ -26,21 +26,21 @@ import {INJECTOR_KEY} from './constants';
* that will be part of the upgrade application. For example, let's assume we have
* defined `HeroesService`
*
* {@example upgrade/static/ts/module.ts region="ng2-heroes-service"}
* {@example upgrade/static/ts/full/module.ts region="ng2-heroes-service"}
*
* and that we have included this in our upgrade app `NgModule`
*
* {@example upgrade/static/ts/module.ts region="ng2-module"}
* {@example upgrade/static/ts/full/module.ts region="ng2-module"}
*
* Now we can register the `downgradeInjectable` factory function for the service
* on an AngularJS module.
*
* {@example upgrade/static/ts/module.ts region="downgrade-ng2-heroes-service"}
* {@example upgrade/static/ts/full/module.ts region="downgrade-ng2-heroes-service"}
*
* Inside an AngularJS component's controller we can get hold of the
* downgraded service via the name we gave when downgrading.
*
* {@example upgrade/static/ts/module.ts region="example-app"}
* {@example upgrade/static/ts/full/module.ts region="example-app"}
*
* @param token an `InjectionToken` that identifies a service provided from Angular.
*

View File

@ -17,7 +17,90 @@ import {angular1Providers, setTempInjectorRef} from './angular1_providers';
import {NgAdapterInjector} from './util';
/** @experimental */
/**
* @description
*
* A helper function for creating an AngularJS module that can bootstrap an Angular module
* "on-demand" (possibly lazily) when a {@link downgradeComponent downgraded component} needs to be
* instantiated.
*
* *Part of the [upgrade/static](api?query=upgrade/static) library for hybrid upgrade apps that
* support AoT compilation.*
*
* It allows loading/bootstrapping the Angular part of a hybrid application lazily and not having to
* pay the cost up-front. For example, you can have an AngularJS application that uses Angular for
* specific routes and only instantiate the Angular modules if/when the user visits one of these
* routes.
*
* The Angular module will be bootstrapped once (when requested for the first time) and the same
* reference will be used from that point onwards.
*
* `downgradeModule()` requires either an `NgModuleFactory` or a function:
* - `NgModuleFactory`: If you pass an `NgModuleFactory`, it will be used to instantiate a module
* using `platformBrowser`'s {@link PlatformRef#bootstrapModuleFactory bootstrapModuleFactory()}.
* - `Function`: If you pass a function, it is expected to return a promise resolving to an
* `NgModuleRef`. The function is called with an array of extra {@link StaticProvider Providers}
* that are expected to be available from the returned `NgModuleRef`'s `Injector`.
*
* `downgradeModule()` returns the name of the created AngularJS wrapper module. You can use it to
* declare a dependency in your main AngularJS module.
*
* {@example upgrade/static/ts/lite/module.ts region="basic-how-to"}
*
* For more details on how to use `downgradeModule()` see
* [Upgrading for Performance](guide/upgrade-performance).
*
* @usageNotes
*
* Apart from `UpgradeModule`, you can use the rest of the `upgrade/static` helpers as usual to
* build a hybrid application. Note that the Angular pieces (e.g. downgraded services) will not be
* available until the downgraded module has been bootstrapped, i.e. by instantiating a downgraded
* component.
*
* <div class="alert is-important">
*
* You cannot use `downgradeModule()` and `UpgradeModule` in the same hybrid application.<br />
* Use one or the other.
*
* </div>
*
* ### Differences with `UpgradeModule`
*
* Besides their different API, there are two important internal differences between
* `downgradeModule()` and `UpgradeModule` that affect the behavior of hybrid applications:
*
* 1. Unlike `UpgradeModule`, `downgradeModule()` does not bootstrap the main AngularJS module
* inside the {@link NgZone Angular zone}.
* 2. Unlike `UpgradeModule`, `downgradeModule()` does not automatically run a
* [$digest()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest) when changes are
* detected in the Angular part of the application.
*
* What this means is that applications using `UpgradeModule` will run change detection more
* frequently in order to ensure that both frameworks are properly notified about possible changes.
* This will inevitably result in more change detection runs than necessary.
*
* `downgradeModule()`, on the other side, does not try to tie the two change detection systems as
* tightly, restricting the explicit change detection runs only to cases where it knows it is
* necessary (e.g. when the inputs of a downgraded component change). This improves performance,
* especially in change-detection-heavy applications, but leaves it up to the developer to manually
* notify each framework as needed.
*
* For a more detailed discussion of the differences and their implications, see
* [Upgrading for Performance](guide/upgrade-performance).
*
* <div class="alert is-helpful">
*
* You can manually trigger a change detection run in AngularJS using
* [scope.$apply(...)](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply) or
* [$rootScope.$digest()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest).
*
* You can manually trigger a change detection run in Angular using {@link NgZone#run
* ngZone.run(...)}.
*
* </div>
*
* @experimental
*/
export function downgradeModule<T>(
moduleFactoryOrBootstrapFn: NgModuleFactory<T>|
((extraProviders: StaticProvider[]) => Promise<NgModuleRef<T>>)): string {

View File

@ -42,12 +42,12 @@ class Bindings {
* Let's assume that you have an AngularJS component called `ng1Hero` that needs
* to be made available in Angular templates.
*
* {@example upgrade/static/ts/module.ts region="ng1-hero"}
* {@example upgrade/static/ts/full/module.ts region="ng1-hero"}
*
* We must create a `Directive` that will make this AngularJS component
* available inside Angular templates.
*
* {@example upgrade/static/ts/module.ts region="ng1-hero-wrapper"}
* {@example upgrade/static/ts/full/module.ts region="ng1-hero-wrapper"}
*
* In this example you can see that we must derive from the `UpgradeComponent`
* base class but also provide an {@link Directive `@Directive`} decorator. This is
@ -90,16 +90,11 @@ export class UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy {
* Instead you should derive a new class from this one and call the super constructor
* from the base class.
*
* {@example upgrade/static/ts/module.ts region="ng1-hero-wrapper" }
* {@example upgrade/static/ts/full/module.ts region="ng1-hero-wrapper" }
*
* * The `name` parameter should be the name of the AngularJS directive.
* * The `elementRef` and `injector` parameters should be acquired from Angular by dependency
* injection into the base class constructor.
*
* Note that we must manually implement lifecycle hooks that call through to the super class.
* This is because, at the moment, the AoT compiler is not able to tell that the
* `UpgradeComponent`
* already implements them and so does not wire up calls to them at runtime.
*/
constructor(private name: string, private elementRef: ElementRef, private injector: Injector) {
this.helper = new UpgradeHelper(injector, name, elementRef);

View File

@ -22,13 +22,14 @@ import {NgAdapterInjector} from './util';
* An `NgModule`, which you import to provide AngularJS core services,
* and has an instance method used to bootstrap the hybrid upgrade application.
*
* *Part of the [upgrade/static](api?query=upgrade%2Fstatic)
* *Part of the [upgrade/static](api?query=upgrade/static)
* library for hybrid upgrade apps that support AoT compilation*
*
* The `upgrade/static` package contains helpers that allow AngularJS and Angular components
* to be used together inside a hybrid upgrade application, which supports AoT compilation.
*
* Specifically, the classes and functions in the `upgrade/static` module allow the following:
*
* 1. Creation of an Angular directive that wraps and exposes an AngularJS component so
* that it can be used in an Angular template. See `UpgradeComponent`.
* 2. Creation of an AngularJS directive that wraps and exposes an Angular component so
@ -39,8 +40,15 @@ import {NgAdapterInjector} from './util';
* 4. Creation of an AngularJS service that wraps and exposes an Angular injectable
* so that it can be injected into an AngularJS context. See `downgradeInjectable`.
* 3. Bootstrapping of a hybrid Angular application which contains both of the frameworks
* coexisting in a single application. See the
* {@link UpgradeModule#examples example} below.
* coexisting in a single application.
*
* @usageNotes
*
* ```ts
* import {UpgradeModule} from '@angular/upgrade/static';
* ```
*
* See also the {@link UpgradeModule#examples examples} below.
*
* ### Mental Model
*
@ -59,29 +67,34 @@ import {NgAdapterInjector} from './util';
* 5. An AngularJS component can be "upgraded"" to an Angular component. This is achieved by
* defining an Angular directive, which bootstraps the AngularJS component at its location
* in the DOM. See `UpgradeComponent`.
* 6. An Angular component can be "downgraded"" to an AngularJS component. This is achieved by
* 6. An Angular component can be "downgraded" to an AngularJS component. This is achieved by
* defining an AngularJS directive, which bootstraps the Angular component at its location
* in the DOM. See `downgradeComponent`.
* 7. Whenever an "upgraded"/"downgraded" component is instantiated the host element is owned by
* the framework doing the instantiation. The other framework then instantiates and owns the
* view for that component.
* a. This implies that the component bindings will always follow the semantics of the
* 1. This implies that the component bindings will always follow the semantics of the
* instantiation framework.
* b. The DOM attributes are parsed by the framework that owns the current template. So
* attributes in AngularJS templates must use kebab-case, while AngularJS templates must
* use camelCase.
* c. However the template binding syntax will always use the Angular style, e.g. square
* 2. The DOM attributes are parsed by the framework that owns the current template. So
* attributes in AngularJS templates must use kebab-case, while AngularJS templates must use
* camelCase.
* 3. However the template binding syntax will always use the Angular style, e.g. square
* brackets (`[...]`) for property binding.
* 8. Angular is bootstrapped first; AngularJS is bootstrapped second. AngularJS always owns the
* root component of the application.
* 9. The new application is running in an Angular zone, and therefore it no longer needs calls
* to `$apply()`.
* 9. The new application is running in an Angular zone, and therefore it no longer needs calls to
* `$apply()`.
*
* ### Core AngularJS services
* ### The `UpgradeModule` class
*
* This class is an `NgModule`, which you import to provide AngularJS core services,
* and has an instance method used to bootstrap the hybrid upgrade application.
*
* #### Core AngularJS services
* Importing this `NgModule` will add providers for the core
* [AngularJS services](https://docs.angularjs.org/api/ng/service) to the root injector.
*
* ### Bootstrap
* #### Bootstrap
* The runtime instance of this class contains a {@link UpgradeModule#bootstrap `bootstrap()`}
* method, which you use to bootstrap the top level AngularJS module onto an element in the
* DOM for the hybrid upgrade app.
@ -94,16 +107,19 @@ import {NgAdapterInjector} from './util';
*
* Import the `UpgradeModule` into your top level {@link NgModule Angular `NgModule`}.
*
* {@example upgrade/static/ts/module.ts region='ng2-module'}
* {@example upgrade/static/ts/full/module.ts region='ng2-module'}
*
* Then bootstrap the hybrid upgrade app's module, get hold of the `UpgradeModule` instance
* and use it to bootstrap the top level [AngularJS
* module](https://docs.angularjs.org/api/ng/type/angular.Module).
* Then inject `UpgradeModule` into your Angular `NgModule` and use it to bootstrap the top level
* [AngularJS module](https://docs.angularjs.org/api/ng/type/angular.Module) in the
* `ngDoBootstrap()` method.
*
* {@example upgrade/static/ts/module.ts region='bootstrap'}
* {@example upgrade/static/ts/full/module.ts region='bootstrap-ng1'}
*
* Finally, kick off the whole process, by bootstraping your top level Angular `NgModule`.
*
* {@example upgrade/static/ts/full/module.ts region='bootstrap-ng2'}
*
* {@a upgrading-an-angular-1-service}
*
* ### Upgrading an AngularJS service
*
* There is no specific API for upgrading an AngularJS service. Instead you should just follow the
@ -111,17 +127,17 @@ import {NgAdapterInjector} from './util';
*
* Let's say you have an AngularJS service:
*
* {@example upgrade/static/ts/module.ts region="ng1-title-case-service"}
* {@example upgrade/static/ts/full/module.ts region="ng1-text-formatter-service"}
*
* Then you should define an Angular provider to be included in your `NgModule` `providers`
* property.
*
* {@example upgrade/static/ts/module.ts region="upgrade-ng1-service"}
* {@example upgrade/static/ts/full/module.ts region="upgrade-ng1-service"}
*
* Then you can use the "upgraded" AngularJS service by injecting it into an Angular component
* or service.
*
* {@example upgrade/static/ts/module.ts region="use-ng1-upgraded-service"}
* {@example upgrade/static/ts/full/module.ts region="use-ng1-upgraded-service"}
*
* @experimental
*/

View File

@ -52,9 +52,9 @@
"@angular-devkit/schematics" "0.5.4"
typescript "~2.6.2"
"@types/angularjs@1.5.14-alpha":
version "1.5.14-alpha"
resolved "https://registry.yarnpkg.com/@types/angularjs/-/angularjs-1.5.14-alpha.tgz#2add80c88e1d84ade07e042918843093b6ac9808"
"@types/angular@^1.6.47":
version "1.6.47"
resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.6.47.tgz#f7a31279a02c0892ed9aa76aae2da1b17791bacd"
"@types/base64-js@1.2.5":
version "1.2.5"
@ -484,12 +484,18 @@ async@^1.3.0, async@^1.4.0, async@^1.5.2, async@~1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
async@^2.0.0, async@^2.0.1:
async@^2.0.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/async/-/async-2.5.0.tgz#843190fd6b7357a0b9e1c956edddd5ec8462b54d"
dependencies:
lodash "^4.14.0"
async@^2.0.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
dependencies:
lodash "^4.17.10"
async@~0.2.6:
version "0.2.10"
resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
@ -943,19 +949,19 @@ cldr-data-downloader@0.3.2:
request "~2.74.0"
request-progress "0.3.1"
cldr@4.8.0:
version "4.8.0"
resolved "https://registry.yarnpkg.com/cldr/-/cldr-4.8.0.tgz#ae7c9ae322c75ac0eac4d4825dcad0394e97b860"
cldr@4.10.0:
version "4.10.0"
resolved "https://registry.yarnpkg.com/cldr/-/cldr-4.10.0.tgz#5a72a693728eca491bc8ee027d60fa4dc750a24f"
dependencies:
memoizeasync "1.0.0"
passerror "1.1.1"
pegjs "0.9.0"
seq "0.3.5"
uglify-js "1.3.3"
underscore "1.3.3"
unicoderegexp "0.4.1"
xmldom "0.1.27"
xpath "0.0.24"
lodash "^4.17.10"
memoizeasync "^1.1.0"
passerror "^1.1.1"
pegjs "^0.10.0"
seq "^0.3.5"
uglify-js "^1.3.3"
unicoderegexp "^0.4.1"
xmldom "^0.1.27"
xpath "^0.0.27"
cldrjs@0.5.0:
version "0.5.0"
@ -2978,7 +2984,11 @@ inherits@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
ini@^1.2.0, ini@^1.3.2, ini@^1.3.4, ini@~1.3.0, ini@~1.3.3:
ini@^1.2.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
ini@^1.3.2, ini@^1.3.4, ini@~1.3.0, ini@~1.3.3:
version "1.3.4"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
@ -3745,6 +3755,10 @@ lodash@^4.0.0, lodash@^4.14.0, lodash@^4.2.1, lodash@^4.8.0, lodash@~4.17.2:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
lodash@^4.17.10:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
lodash@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551"
@ -3864,9 +3878,9 @@ mem@^1.1.0:
dependencies:
mimic-fn "^1.0.0"
memoizeasync@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/memoizeasync/-/memoizeasync-1.0.0.tgz#7b02a346f3885abb5dc37c0a43c1d202de8cb40a"
memoizeasync@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/memoizeasync/-/memoizeasync-1.1.0.tgz#9d7028a6f266deb733510bb7dbba5f51878c561e"
dependencies:
lru-cache "2.5.0"
passerror "1.1.1"
@ -4397,7 +4411,14 @@ os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
osenv@^0.1.0, osenv@^0.1.4:
osenv@^0.1.0:
version "0.1.5"
resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
dependencies:
os-homedir "^1.0.0"
os-tmpdir "^1.0.0"
osenv@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644"
dependencies:
@ -4483,7 +4504,7 @@ parseurl@~1.3.0, parseurl@~1.3.1, parseurl@~1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
passerror@1.1.1:
passerror@1.1.1, passerror@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/passerror/-/passerror-1.1.1.tgz#a25b88dbdd910a29603aec7dcb96e9a7a97687b4"
@ -4555,9 +4576,9 @@ pbkdf2-compat@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz#b6e0c8fa99494d94e0511575802a59a5c142f288"
pegjs@0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/pegjs/-/pegjs-0.9.0.tgz#f6aefa2e3ce56169208e52179dfe41f89141a369"
pegjs@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/pegjs/-/pegjs-0.10.0.tgz#cf8bafae6eddff4b5a7efb185269eaaf4610ddbd"
performance-now@^2.1.0:
version "2.1.0"
@ -5248,7 +5269,7 @@ send@0.13.2:
range-parser "~1.0.3"
statuses "~1.2.1"
seq@0.3.5:
seq@^0.3.5:
version "0.3.5"
resolved "https://registry.yarnpkg.com/seq/-/seq-0.3.5.tgz#ae02af3a424793d8ccbf212d69174e0c54dffe38"
dependencies:
@ -6008,9 +6029,9 @@ uglify-js@1.2.6, uglify-js@~1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-1.2.6.tgz#d354b2d3c1cf10ebc18fa78c11a28bdd9ce1580d"
uglify-js@1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-1.3.3.tgz#ddd3e98aa27f5f47e589cfb3f95cddba26096190"
uglify-js@^1.3.3:
version "1.3.5"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-1.3.5.tgz#4b5bfff9186effbaa888e4c9e94bd9fc4c94929d"
uglify-js@^2.6:
version "2.8.29"
@ -6066,15 +6087,11 @@ underscore.string@~3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.0.3.tgz#4617b8c1a250cf6e5064fbbb363d0fa96cf14552"
underscore@1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.3.3.tgz#47ac53683daf832bfa952e1774417da47817ae42"
underscore@1.x:
version "1.8.3"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"
unicoderegexp@0.4.1:
unicoderegexp@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/unicoderegexp/-/unicoderegexp-0.4.1.tgz#afb10e4ef1eeddc711417bbb652bc885da9d4171"
@ -6447,7 +6464,7 @@ xmlbuilder@^4.1.0:
dependencies:
lodash "^4.0.0"
xmldom@0.1.27:
xmldom@^0.1.27:
version "0.1.27"
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9"
@ -6455,9 +6472,9 @@ xmlhttprequest-ssl@1.5.3:
version "1.5.3"
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e4070d99f7b49de3528992d"
xpath@0.0.24:
version "0.0.24"
resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.24.tgz#1ade162e1cc523c8d39fc7d06afc16ea216f29fb"
xpath@^0.0.27:
version "0.0.27"
resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.27.tgz#dd3421fbdcc5646ac32c48531b4d7e9d0c2cfa92"
"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
version "4.0.1"