Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
2880cf9ef1 | |||
64a8584a92 | |||
97897ab738 | |||
9378f44d6d | |||
afa46af4c6 | |||
dbffdcc442 | |||
06d68a1b9f | |||
75dd3c5ca5 | |||
23f56198a5 | |||
69167e4519 | |||
10feafcf27 | |||
3f3fed95be | |||
94433f3b9e | |||
aa753878e6 | |||
3513ae40cc | |||
979e7b6086 | |||
41f008d109 | |||
c950410ee1 | |||
b0fa504d39 | |||
426f9710a0 | |||
ae01c70bba | |||
3341a97154 | |||
2056e1f05c | |||
3938a8be75 | |||
393db94b8d | |||
65744e4ae1 | |||
56bc86992c | |||
9426c02648 | |||
0b356d4163 | |||
a886659444 | |||
3649958595 | |||
8919577c54 | |||
84eff4219e | |||
c11e84ee18 | |||
1ce5a495d3 | |||
1f532aaa5a |
10
CHANGELOG.md
10
CHANGELOG.md
@ -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)
|
||||
|
||||
|
2
aio/content/examples/.gitignore
vendored
2
aio/content/examples/.gitignore
vendored
@ -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
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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 ]
|
||||
})
|
||||
|
@ -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
|
@ -0,0 +1,5 @@
|
||||
/* #docregion cross-validation-error-css */
|
||||
.cross-validation-error input {
|
||||
border-left: 5px solid red;
|
||||
}
|
||||
/* #enddocregion cross-validation-error-css */
|
@ -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">
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -0,0 +1,4 @@
|
||||
/* #docregion */
|
||||
.cross-validation-error input {
|
||||
border-left: 5px solid red;
|
||||
}
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -2,6 +2,7 @@
|
||||
"description": "Validation",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js"
|
||||
"!**/*.js",
|
||||
"!**/*.[1].*"
|
||||
]
|
||||
}
|
||||
|
@ -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);
|
||||
}));
|
||||
|
||||
});
|
||||
|
@ -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();
|
||||
}));
|
||||
|
@ -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();
|
||||
}));
|
||||
|
@ -8,6 +8,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"removeComments": false,
|
||||
"noImplicitAny": false,
|
||||
"skipLibCheck": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,8 @@
|
||||
"experimentalDecorators": true,
|
||||
"lib": [ "es2015", "dom" ],
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types/"
|
||||
]
|
||||
"skipLibCheck": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
},
|
||||
"compileOnSave": true,
|
||||
"exclude": [
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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: []}
|
||||
]);
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,8 @@
|
||||
"experimentalDecorators": true,
|
||||
"lib": [ "es2015", "dom" ],
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types/"
|
||||
]
|
||||
"skipLibCheck": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
},
|
||||
"compileOnSave": true,
|
||||
"exclude": [
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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: []}
|
||||
]);
|
||||
|
@ -8,10 +8,8 @@
|
||||
"experimentalDecorators": true,
|
||||
"lib": [ "es2015", "dom" ],
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types/"
|
||||
]
|
||||
"skipLibCheck": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
},
|
||||
"compileOnSave": true,
|
||||
"exclude": [
|
||||
|
@ -895,7 +895,7 @@ For more information on pipes, see [Pipes](guide/pipes).
|
||||
### lowercase
|
||||
|
||||
<code-example hideCopy>
|
||||
<div>{{movie.title | lowercase}}</div>
|
||||
<td>{{movie.title | lowercase}}</td>
|
||||
</code-example>
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -31,6 +31,8 @@ Here are a few essential commands for guide page authors.
|
||||
|
||||
1. http://localhost:4200/ — 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.
|
||||
|
@ -92,7 +92,7 @@ built-in validators—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.**
|
||||
|
352
aio/content/guide/upgrade-performance.md
Normal file
352
aio/content/guide/upgrade-performance.md
Normal 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—one that you can use
|
||||
as a dependency in your main AngularJS module—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`—which requires some extra steps—
|
||||
`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.
|
@ -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>`
|
||||
|
BIN
aio/content/images/bios/elanaolson.jpg
Normal file
BIN
aio/content/images/bios/elanaolson.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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/"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -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",
|
||||
|
@ -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"},
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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>
|
||||
|
||||
|
14
aio/src/assets/images/logos/github-icon.svg
Normal file
14
aio/src/assets/images/logos/github-icon.svg
Normal 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 |
13
aio/src/assets/images/logos/twitter-icon.svg
Normal file
13
aio/src/assets/images/logos/twitter-icon.svg
Normal 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 |
@ -37,5 +37,9 @@
|
||||
padding: 16px 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.short-description {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -18,7 +18,6 @@ const EXAMPLE_CONFIG_FILENAME = 'example-config.json';
|
||||
const IGNORED_EXAMPLES = [ // temporary ignores
|
||||
'quickstart',
|
||||
'setup',
|
||||
'upgrade-p'
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -8,10 +8,8 @@
|
||||
"experimentalDecorators": true,
|
||||
"lib": [ "es2015", "dom" ],
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"typeRoots": [
|
||||
"../node_modules/@types/"
|
||||
]
|
||||
"skipLibCheck": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
},
|
||||
"compileOnSave": true,
|
||||
"exclude": [
|
||||
|
@ -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",
|
||||
|
@ -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"
|
||||
|
@ -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.
|
||||
|
18
aio/tools/transforms/angular-api-package/processors/removeInjectableConstructors.js
vendored
Normal file
18
aio/tools/transforms/angular-api-package/processors/removeInjectableConstructors.js
vendored
Normal 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;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
@ -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();
|
||||
});
|
||||
});
|
@ -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>
|
||||
|
@ -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') $}
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
"skipDefaultLibCheck": true,
|
||||
"skipLibCheck": true,
|
||||
"target": "es5",
|
||||
"types": ["angularjs"]
|
||||
"types": ["angular"]
|
||||
},
|
||||
"exclude": [
|
||||
"angular1_router",
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"
|
||||
],
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
@ -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;
|
||||
|
@ -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); }
|
||||
}
|
||||
|
@ -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
@ -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) {
|
||||
|
@ -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
|
||||
)
|
||||
|
66
packages/examples/core/ts/metadata/directives.ts
Normal file
66
packages/examples/core/ts/metadata/directives.ts
Normal 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
|
@ -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
|
||||
)
|
||||
)
|
||||
|
@ -11,7 +11,7 @@
|
||||
"rxjs/*": ["../../node_modules/rxjs/*"]
|
||||
},
|
||||
"outDir": "../../dist/examples",
|
||||
"types": ["jasmine", "node", "angularjs", "systemjs"]
|
||||
"types": ["jasmine", "node", "angular", "systemjs"]
|
||||
},
|
||||
|
||||
"include": [
|
||||
|
@ -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', () => {
|
@ -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
|
@ -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);
|
||||
}
|
@ -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');
|
||||
});
|
||||
});
|
220
packages/examples/upgrade/static/ts/lite/module.ts
Normal file
220
packages/examples/upgrade/static/ts/lite/module.ts
Normal 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]);
|
17
packages/examples/upgrade/static/ts/lite/styles.css
Normal file
17
packages/examples/upgrade/static/ts/lite/styles.css
Normal 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;
|
||||
}
|
@ -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;
|
||||
});
|
||||
}));
|
||||
|
@ -22,7 +22,7 @@
|
||||
"skipDefaultLibCheck": true,
|
||||
"skipLibCheck": true,
|
||||
"target": "es5",
|
||||
"types": ["angularjs"]
|
||||
"types": ["angular"]
|
||||
},
|
||||
"bazelOptions": {
|
||||
"suppressTsconfigOverrideWarnings": true
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
*/
|
||||
|
93
yarn.lock
93
yarn.lock
@ -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"
|
||||
|
Reference in New Issue
Block a user