Compare commits
142 Commits
add-topnav
...
ngcontaine
Author | SHA1 | Date | |
---|---|---|---|
03616bcb43 | |||
3a19f70d1c | |||
dc1f1295ee | |||
49df4ef454 | |||
e1146f3d06 | |||
0d5f2d3c7e | |||
a167bca927 | |||
e3709f5d48 | |||
197387d05e | |||
1089261717 | |||
ddb792da28 | |||
89203c96ad | |||
3d20c50156 | |||
dcabb05102 | |||
68814040e3 | |||
3980640d53 | |||
52d43a99ef | |||
45feb10c46 | |||
250527ca68 | |||
94076c934c | |||
f936b8cbd2 | |||
d571a51739 | |||
86b1cc7313 | |||
787c54736c | |||
19544060d3 | |||
c0e2dba07b | |||
e01b539ee5 | |||
809e8f742e | |||
00c110b055 | |||
1e74ea9e60 | |||
f62876bbcb | |||
fddd2af4fc | |||
d5a9396017 | |||
3e6a722ddb | |||
5fe1e74dd3 | |||
f974c48885 | |||
568612349f | |||
b719905f9b | |||
56a8533cf3 | |||
b72dbc843f | |||
8fe8b8fcff | |||
b6af8700ce | |||
3d52174bf1 | |||
dbdcfed2bd | |||
ffbacdf4ac | |||
7f3242affb | |||
e3064d5432 | |||
0c3738a780 | |||
0922228024 | |||
c94a2c9e3f | |||
948e2236c0 | |||
a294e0dd79 | |||
3553977bd7 | |||
1ae3f87383 | |||
4e7a44c816 | |||
d1805d04d5 | |||
d243baf48a | |||
ff84c5c4da | |||
87ddbdf919 | |||
9803cb011e | |||
13d60eac61 | |||
d876700c26 | |||
99bdd257a6 | |||
3db9d57de3 | |||
66e50f28d2 | |||
0ede987ced | |||
71100e6d72 | |||
676ec411b9 | |||
01e7ff682c | |||
34c42836cf | |||
50d4a4fe5c | |||
69510acb20 | |||
ef1c6d8c26 | |||
2ecaa40e64 | |||
fc4dc35426 | |||
104d30507a | |||
c57b491778 | |||
1dc7d0d29e | |||
39c8baea31 | |||
abed2cd52c | |||
22758912a0 | |||
bb6b59128f | |||
4258c3d1df | |||
70156bc4ed | |||
2ac2ab7ff6 | |||
ca0a55f4ee | |||
0b3d25d67e | |||
24e0c3d43d | |||
922908818f | |||
8dec381145 | |||
32da3e1602 | |||
6d68f3e39a | |||
50fb13fb09 | |||
fe8fcc834c | |||
5c0e681bf3 | |||
7d6e833a6f | |||
49e900d6fc | |||
5feb9e1935 | |||
002a5afa98 | |||
cf0968f98e | |||
855e8ad9f6 | |||
89c442270a | |||
ae9418c7de | |||
166d90d2a9 | |||
7d318743c1 | |||
2a68ba4cbb | |||
d244523ae6 | |||
941d2cdaaf | |||
7d1f9c8a7c | |||
f841e36543 | |||
f229449c67 | |||
6e20e0aac8 | |||
1e139d4339 | |||
fba3f10938 | |||
c95437f15d | |||
39c7769c9e | |||
8c51ce6f3b | |||
71b0c3d469 | |||
b8760a0ca5 | |||
50fb58fd01 | |||
fe8fe9ba9e | |||
637805a0c9 | |||
7b2b1afe71 | |||
7d3fd4d655 | |||
10da6a45c6 | |||
84272e2227 | |||
cb31381734 | |||
3e1a3b2e32 | |||
1e6a226703 | |||
b91254fc43 | |||
8b8168262d | |||
a26965812b | |||
def354de16 | |||
9782736e00 | |||
e8354edcd2 | |||
5b76f04b7f | |||
a57825acf3 | |||
efc7639352 | |||
3e26cabe02 | |||
9d114c052a | |||
43e61c25e1 | |||
4e1493a1d6 |
42
CHANGELOG.md
42
CHANGELOG.md
@ -1,3 +1,43 @@
|
||||
<a name="6.1.0-beta.3"></a>
|
||||
# [6.1.0-beta.3](https://github.com/angular/angular/compare/6.1.0-beta.2...6.1.0-beta.3) (2018-06-28)
|
||||
|
||||
### 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.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.1.0-beta.2"></a>
|
||||
# [6.1.0-beta.2](https://github.com/angular/angular/compare/6.1.0-beta.1...6.1.0-beta.2) (2018-06-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** support `.` in import statements. ([#20634](https://github.com/angular/angular/issues/20634)) ([d8f7b29](https://github.com/angular/angular/commit/d8f7b29)), closes [#20363](https://github.com/angular/angular/issues/20363)
|
||||
* **core:** Injector correctly honors the @Self flag ([#24520](https://github.com/angular/angular/issues/24520)) ([ccbda9d](https://github.com/angular/angular/commit/ccbda9d))
|
||||
|
||||
|
||||
<a name="6.0.6"></a>
|
||||
## [6.0.6](https://github.com/angular/angular/compare/6.0.5...6.0.6) (2018-06-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** support `.` in import statements. ([#20634](https://github.com/angular/angular/issues/20634)) ([e543c73](https://github.com/angular/angular/commit/e543c73)), closes [#20363](https://github.com/angular/angular/issues/20363)
|
||||
* **core:** Injector correctly honors the @Self flag ([#24520](https://github.com/angular/angular/issues/24520)) ([f5b3661](https://github.com/angular/angular/commit/f5b3661))
|
||||
|
||||
|
||||
|
||||
<a name="6.1.0-beta.1"></a>
|
||||
# [6.1.0-beta.1](https://github.com/angular/angular/compare/6.1.0-beta.0...6.1.0-beta.1) (2018-06-13)
|
||||
|
||||
@ -41,7 +81,7 @@
|
||||
|
||||
|
||||
<a name="6.1.0-beta.0"></a>
|
||||
## [6.1.0-beta.0](https://github.com/angular/angular/compare/6.0.0-rc.5...6.1.0-beta.0) (2018-06-06)
|
||||
# [6.1.0-beta.0](https://github.com/angular/angular/compare/6.0.0-rc.5...6.1.0-beta.0) (2018-06-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
18
WORKSPACE
18
WORKSPACE
@ -6,9 +6,9 @@ workspace(name = "angular")
|
||||
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.9.1.zip",
|
||||
strip_prefix = "rules_nodejs-0.9.1",
|
||||
sha256 = "6139762b62b37c1fd171d7f22aa39566cb7dc2916f0f801d505a9aaf118c117f",
|
||||
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.10.0.zip",
|
||||
strip_prefix = "rules_nodejs-0.10.0",
|
||||
sha256 = "2f77623311da8b5009b1c7eade12de8e15fa3cd2adf9dfcc9f87cb2082b2211f",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@ -20,9 +20,9 @@ http_archive(
|
||||
|
||||
http_archive(
|
||||
name = "build_bazel_rules_typescript",
|
||||
url = "https://github.com/bazelbuild/rules_typescript/archive/0.15.0.zip",
|
||||
strip_prefix = "rules_typescript-0.15.0",
|
||||
sha256 = "1aa75917330b820cb239b3c10a936a10f0a46fe215063d4492dd76dc6e1616f4",
|
||||
url = "https://github.com/rkirov/rules_typescript/archive/v0.16.0.zip",
|
||||
strip_prefix = "rules_typescript-0.16.0",
|
||||
sha256 = "f5aedd3a792e5af19cd0c0f0318cb692e2989e816e896e794152d07808fccacd",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@ -59,9 +59,9 @@ http_archive(
|
||||
# ts_library rules in the devkit repository.
|
||||
http_archive(
|
||||
name = "angular_devkit",
|
||||
url = "https://github.com/angular/devkit/archive/v0.3.1.zip",
|
||||
strip_prefix = "devkit-0.3.1",
|
||||
sha256 = "31d4b597fe9336650acf13df053c1c84dcbe9c29c6a833bcac3819cd3fd8cad3",
|
||||
url = "https://github.com/angular/angular-cli/archive/v6.1.0-rc.0.zip",
|
||||
strip_prefix = "angular-cli-6.1.0-rc.0",
|
||||
sha256 = "8cf320ea58c321e103f39087376feea502f20eaf79c61a4fdb05c7286c8684fd",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
|
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
|
||||
|
@ -40,5 +40,7 @@ export class HighlightDirective {
|
||||
// #docregion color-2
|
||||
@Input() appHighlight: string;
|
||||
// #enddocregion color-2
|
||||
}
|
||||
|
||||
// #docregion
|
||||
}
|
||||
// #enddocregion
|
||||
|
@ -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].*"
|
||||
]
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ button {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
code, .code {
|
||||
code,
|
||||
.code {
|
||||
background-color: #eee;
|
||||
color: black;
|
||||
font-family: Courier, sans-serif;
|
||||
@ -21,14 +22,18 @@ div.code {
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 40px 0
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
td, th {
|
||||
td,
|
||||
th {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* #docregion p-span */
|
||||
p span { color: red; font-size: 70%; }
|
||||
p span {
|
||||
color: red;
|
||||
font-size: 70%;
|
||||
}
|
||||
/* #enddocregion p-span */
|
||||
|
@ -132,7 +132,7 @@
|
||||
<!-- #docregion select-span -->
|
||||
<select [(ngModel)]="hero">
|
||||
<span *ngFor="let h of heroes">
|
||||
<span *ngIf="showSad || h?.emotion != 'sad'">
|
||||
<span *ngIf="showSad || h?.emotion !== 'sad'">
|
||||
<option [ngValue]="h">{{h.name}} ({{h?.emotion}})</option>
|
||||
</span>
|
||||
</span>
|
||||
@ -147,7 +147,7 @@
|
||||
<!-- #docregion select-ngcontainer -->
|
||||
<select [(ngModel)]="hero">
|
||||
<ng-container *ngFor="let h of heroes">
|
||||
<ng-container *ngIf="showSad || h?.emotion != 'sad'">
|
||||
<ng-container *ngIf="showSad || h?.emotion !== 'sad'">
|
||||
<option [ngValue]="h">{{h.name}} ({{h?.emotion}})</option>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
@ -6,14 +6,15 @@ import { heroes } from './hero';
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: [ './app.component.css' ]
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
heroes = heroes;
|
||||
hero = this.heroes[0];
|
||||
heroTraits = [ 'honest', 'brave', 'considerate' ];
|
||||
heroTraits = ['honest', 'brave', 'considerate'];
|
||||
|
||||
// flags for the table
|
||||
|
||||
attrDirs = true;
|
||||
strucDirs = true;
|
||||
divNgIf = false;
|
||||
|
@ -1,9 +1,9 @@
|
||||
// #docregion
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppComponent } from './app.component';
|
||||
import { ContentComponent } from './content.component';
|
||||
import { heroComponents } from './hero.components';
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
// #docregion
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
import { Hero } from './hero';
|
||||
|
||||
@Component({
|
||||
@ -33,11 +34,15 @@ export class ConfusedHeroComponent {
|
||||
export class UnknownHeroComponent {
|
||||
@Input() hero: Hero;
|
||||
get message() {
|
||||
return this.hero && this.hero.name ?
|
||||
`${this.hero.name} is strange and mysterious.` :
|
||||
'Are you feeling indecisive?';
|
||||
return this.hero && this.hero.name
|
||||
? `${this.hero.name} is strange and mysterious.`
|
||||
: 'Are you feeling indecisive?';
|
||||
}
|
||||
}
|
||||
|
||||
export const heroComponents =
|
||||
[ HappyHeroComponent, SadHeroComponent, ConfusedHeroComponent, UnknownHeroComponent ];
|
||||
export const heroComponents = [
|
||||
HappyHeroComponent,
|
||||
SadHeroComponent,
|
||||
ConfusedHeroComponent,
|
||||
UnknownHeroComponent
|
||||
];
|
||||
|
@ -6,8 +6,8 @@ export class Hero {
|
||||
}
|
||||
|
||||
export const heroes: Hero[] = [
|
||||
{ id: 1, name: 'Mr. Nice', emotion: 'happy'},
|
||||
{ id: 2, name: 'Narco', emotion: 'sad' },
|
||||
{ id: 1, name: 'Mr. Nice', emotion: 'happy' },
|
||||
{ id: 2, name: 'Narco', emotion: 'sad' },
|
||||
{ id: 3, name: 'Windstorm', emotion: 'confused' },
|
||||
{ id: 4, name: 'Magneta'}
|
||||
{ id: 4, name: 'Magneta' }
|
||||
];
|
||||
|
@ -3,15 +3,15 @@
|
||||
<!-- #enddocregion show-hero-1 -->
|
||||
|
||||
<!-- #docregion show-hero-2 -->
|
||||
<h2>{{ hero.name }} Details</h2>
|
||||
<h2>{{hero.name}} Details</h2>
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div><span>name: </span>{{hero.name}}</div>
|
||||
<!-- #enddocregion show-hero-2 -->
|
||||
|
||||
<!-- #docregion name-input -->
|
||||
<div>
|
||||
<label>name:
|
||||
<input [(ngModel)]="hero.name" placeholder="name">
|
||||
</label>
|
||||
<label>name:
|
||||
<input [(ngModel)]="hero.name" placeholder="name">
|
||||
</label>
|
||||
</div>
|
||||
<!-- #enddocregion name-input -->
|
||||
|
@ -1,10 +1,10 @@
|
||||
<!-- #docregion -->
|
||||
<!-- #docregion pipe -->
|
||||
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||
<h2>{{hero.name | uppercase}} Details</h2>
|
||||
<!-- #enddocregion pipe -->
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
<input [(ngModel)]="hero.name" placeholder="name">
|
||||
</label>
|
||||
<label>name:
|
||||
<input [(ngModel)]="hero.name" placeholder="name">
|
||||
</label>
|
||||
</div>
|
||||
|
@ -14,7 +14,7 @@
|
||||
<div *ngIf="selectedHero">
|
||||
|
||||
<!-- #docregion selectedHero-details -->
|
||||
<h2>{{ selectedHero.name | uppercase }} Details</h2>
|
||||
<h2>{{selectedHero.name | uppercase}} Details</h2>
|
||||
<div><span>id: </span>{{selectedHero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div *ngIf="hero">
|
||||
|
||||
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||
<h2>{{hero.name | uppercase}} Details</h2>
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div *ngIf="hero">
|
||||
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||
<h2>{{hero.name | uppercase}} Details</h2>
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div *ngIf="hero">
|
||||
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||
<h2>{{hero.name | uppercase}} Details</h2>
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
|
@ -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,5 +1,5 @@
|
||||
<div *ngIf="hero">
|
||||
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||
<h2>{{hero.name | uppercase}} Details</h2>
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
|
@ -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();
|
||||
}));
|
||||
|
@ -40,7 +40,7 @@ export class HeroService {
|
||||
// #enddocregion getHeroes-1
|
||||
.pipe(
|
||||
// #enddocregion getHeroes-2
|
||||
tap(heroes => this.log(`fetched heroes`)),
|
||||
tap(heroes => this.log('fetched heroes')),
|
||||
// #docregion getHeroes-2
|
||||
catchError(this.handleError('getHeroes', []))
|
||||
);
|
||||
|
@ -20,7 +20,7 @@
|
||||
</a>
|
||||
<!-- #docregion delete -->
|
||||
<button class="delete" title="delete hero"
|
||||
(click)="delete(hero)">x</button>
|
||||
(click)="delete(hero)">x</button>
|
||||
<!-- #enddocregion delete -->
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -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();
|
||||
}));
|
||||
|
@ -30,7 +30,7 @@ export class HeroService {
|
||||
getHeroes (): Observable<Hero[]> {
|
||||
return this.http.get<Hero[]>(this.heroesUrl)
|
||||
.pipe(
|
||||
tap(heroes => this.log(`fetched heroes`)),
|
||||
tap(heroes => this.log('fetched heroes')),
|
||||
catchError(this.handleError('getHeroes', []))
|
||||
);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -8,9 +8,10 @@ A _component_ controls a patch of screen called a *view*. For example, individua
|
||||
* The list of heroes.
|
||||
* The hero editor.
|
||||
|
||||
You define a component's application logic—what it does to support the view—inside a class. The class interacts with the view through an API of properties and methods.
|
||||
You define a component's application logic—what it does to support the view—inside a class.
|
||||
The class interacts with the view through an API of properties and methods.
|
||||
|
||||
For example, the `HeroListComponent` has a `heroes` property that returns an array of heroes that it acquires from a service. `HeroListComponent` also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list.
|
||||
For example, the `HeroListComponent` has a `heroes` property that holds an array of heroes. It also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list. The component acquires the heroes from a service, which is a TypeScript [parameter property[(http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor. The service is provided to the component through the dependency injection system.
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (class)" region="class"></code-example>
|
||||
|
||||
@ -39,8 +40,7 @@ Angular inserts an instance of the `HeroListComponent` view between those tags.
|
||||
|
||||
* `templateUrl`: The module-relative address of this component's HTML template. Alternatively, you can provide the HTML template inline, as the value of the `template` property. This template defines the component's _host view_.
|
||||
|
||||
* `providers`: An array of **dependency injection providers** for services that the component requires. In the example, this tells Angular that the component's constructor requires a `HeroService` instance
|
||||
in order to get the list of heroes to display.
|
||||
* `providers`: An array of **dependency injection providers** for services that the component requires. In the example, this tells Angular how provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
|
||||
|
||||
<hr/>
|
||||
|
||||
|
@ -88,7 +88,8 @@ For example, import Angular's `Component` decorator from the `@angular/core` lib
|
||||
|
||||
<code-example path="architecture/src/app/app.component.ts" region="import" linenums="false"></code-example>
|
||||
|
||||
You also import NgModules from Angular _libraries_ using JavaScript import statements:
|
||||
You also import NgModules from Angular _libraries_ using JavaScript import statements.
|
||||
For example, the following code imports the `BrowserModule` NgModule from the `platform-browser` library:
|
||||
|
||||
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false"></code-example>
|
||||
|
||||
|
@ -60,11 +60,29 @@ The process of `HeroService` injection looks something like this:
|
||||
|
||||
### Providing services
|
||||
|
||||
You must register at least one *provider* of any service you are going to use. You can register providers in modules or in components.
|
||||
You must register at least one *provider* of any service you are going to use. A service can register providers itself, making it available everywhere, or you can register providers with specific modules or components. You register providers in the metadata of the service (in the `@Injectable` decorator), or in the `@NgModule` or `@Component` metadata
|
||||
|
||||
* When you add providers to the [root module](guide/architecture-modules), the same instance of a service is available to all components in your app.
|
||||
* By default, the Angular CLI command `ng generate service` registers a provider with the root injector for your service by including provider metadata in the `@Injectable` decorator. The tutorial uses this method to register the provider of HeroService class definition:
|
||||
|
||||
<code-example path="architecture/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (module providers)" region="providers"></code-example>
|
||||
```
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
```
|
||||
|
||||
When you provide the service at the root level, Angular creates a single, shared instance of HeroService and injects into any class that asks for it. Registering the provider in the `@Injectable` metadata also allows Angular to optimize an app by removing the service if it turns out not to be used after all.
|
||||
|
||||
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule` decorator:
|
||||
|
||||
```
|
||||
@NgModule({
|
||||
providers: [
|
||||
BackendService,
|
||||
Logger
|
||||
],
|
||||
...
|
||||
})
|
||||
```
|
||||
|
||||
* When you register a provider at the component level, you get a new instance of the
|
||||
service with each new instance of that component. At the component level, register a service provider in the `providers` property of the `@Component` metadata:
|
||||
|
@ -96,7 +96,7 @@ Now edit the generated `src/app/highlight.directive.ts` to look as follows:
|
||||
|
||||
The `import` statement specifies an additional `ElementRef` symbol from the Angular `core` library:
|
||||
|
||||
You use the `ElementRef`in the directive's constructor
|
||||
You use the `ElementRef` in the directive's constructor
|
||||
to [inject](guide/dependency-injection) a reference to the host DOM element,
|
||||
the element to which you applied `appHighlight`.
|
||||
|
||||
|
@ -280,12 +280,14 @@ To control how this encapsulation happens on a *per
|
||||
component* basis, you can set the *view encapsulation mode* in the component metadata.
|
||||
Choose from the following modes:
|
||||
|
||||
* `Native` view encapsulation uses the browser's native shadow DOM implementation (see
|
||||
* `ShadowDom` view encapsulation uses the browser's native shadow DOM implementation (see
|
||||
[Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM)
|
||||
on the [MDN](https://developer.mozilla.org) site)
|
||||
to attach a shadow DOM to the component's host element, and then puts the component
|
||||
view inside that shadow DOM. The component's styles are included within the shadow DOM.
|
||||
|
||||
* `Native` view encapsulation uses a now deprecated version of the browser's native shadow DOM implementation - [learn about the changes](https://hayato.io/2016/shadowdomv1/).
|
||||
|
||||
* `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing
|
||||
(and renaming) the CSS code to effectively scope the CSS to the component's view.
|
||||
For details, see [Appendix 1](guide/component-styles#inspect-generated-css).
|
||||
@ -300,8 +302,8 @@ To set the components encapsulation mode, use the `encapsulation` property in th
|
||||
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" title="src/app/quest-summary.component.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
`Native` view encapsulation only works on browsers that have native support
|
||||
for shadow DOM (see [Shadow DOM v0](http://caniuse.com/#feat=shadowdom) on the
|
||||
`ShadowDom` view encapsulation only works on browsers that have native support
|
||||
for shadow DOM (see [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) on the
|
||||
[Can I use](http://caniuse.com) site). The support is still limited,
|
||||
which is why `Emulated` view encapsulation is the default mode and recommended
|
||||
in most cases.
|
||||
|
@ -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.**
|
||||
|
@ -114,19 +114,23 @@ The following class types can be declared:
|
||||
|
||||
A [decorator](guide/glossary#decorator) statement immediately before a field in a class definition that declares the type of that field. Some examples are `@Input` and `@Output`.
|
||||
|
||||
{@a cli}
|
||||
|
||||
## CLI
|
||||
|
||||
The [Angular CLI](https://cli.angular.io/) is a command-line tool that can create a project, add files, and perform a variety of ongoing development tasks such as testing, bundling, and deployment.
|
||||
The [Angular CLI](https://cli.angular.io/) is a command-line tool for managing the Angular development cycle. Use it to create the initial filesystem scaffolding for a [workspace](guide/glossary#workspace) or [project](guide/glossary#project), and to run [schematics](guide/glossary#schematic) that add and modify code for initial generic versions of various elements. The tool supports all stages of the development cycle, including building, testing, bundling, and deployment.
|
||||
|
||||
Learn more in the [Getting Started](guide/quickstart) guide.
|
||||
* To begin using the CLI for a new project, see [Getting Started](guide/quickstart) guide.
|
||||
* To learn more about the full capabilities of the CLI, see the [Angular CLI documentation].(https://github.com/angular/angular-cli/wiki).
|
||||
|
||||
{@a component}
|
||||
|
||||
## Component
|
||||
|
||||
A class with the `@Component` [decorator](guide/glossary#decorator) that associates it with a companion [template](guide/glossary#template).
|
||||
A class with the `@Component` [decorator](guide/glossary#decorator) that associates it with a companion [template](guide/glossary#template). Together, the component and template define a [view](guide/glossary#view).
|
||||
|
||||
A component is a special type of [directive](guide/glossary#directive) that represents a [view](guide/glossary#view).The `@Component` decorator extends the `@Directive` decorator with template-oriented features.
|
||||
A component is a special type of [directive](guide/glossary#directive).
|
||||
The `@Component` decorator extends the `@Directive` decorator with template-oriented features.
|
||||
|
||||
An Angular component class is responsible for exposing data and handling most of the view's display and user-interaction logic through [data binding](guide/glossary#data-binding).
|
||||
|
||||
@ -208,7 +212,8 @@ See [Class decorator](guide/glossary#class-decorator), [Class field decorator](g
|
||||
|
||||
A design pattern and mechanism for creating and delivering parts of an application (dependencies) to other parts of an application that require them.
|
||||
|
||||
In Angular, dependencies are typically services, but can also be values, such as strings or functions. An [injector](guide/glossary#injector) for an app (created automatically during bootstrap) creates dependencies when needed, using a registered [provider](guide/glossary#provider) of the service or value. Different providers can provide different implementations of the same service.
|
||||
In Angular, dependencies are typically services, but can also be values, such as strings or functions.
|
||||
An [injector](guide/glossary#injector) for an app (created automatically during bootstrap) instantiates dependencies when needed, using a configured [provider](guide/glossary#provider) of the service or value.
|
||||
|
||||
Learn more in the [Dependency Injection](guide/dependency-injection) guide.
|
||||
|
||||
@ -280,7 +285,7 @@ Compare [Custom element](guide/glossary#custom-element).
|
||||
|
||||
## Entry point
|
||||
|
||||
A JavaScript ID that makes parts of an NPM package available for import by other code.
|
||||
A JavaScript symbol that makes parts of an npm package available for import by other code.
|
||||
The Angular [scoped packages](guide/glossary#scoped-package) each have an entry point named `index`.
|
||||
|
||||
Within Angular, use [NgModules](guide/glossary#ngmodule) to achieve the same result.
|
||||
@ -310,7 +315,17 @@ Both a [service](guide/glossary#service) and a [component](guide/glossary#compon
|
||||
|
||||
An object in the Angular [dependency-injection system](guide/glossary#dependency-injection)
|
||||
that can find a named dependency in its cache or create a dependency
|
||||
with a registered [provider](guide/glossary#provider). Injectors are created for NgModules automatically as part of the bootstrap process, and inherited through the component hierarchy.
|
||||
using a configured [provider](guide/glossary#provider).
|
||||
Injectors are created for NgModules automatically as part of the bootstrap process
|
||||
and are inherited through the component hierarchy.
|
||||
|
||||
* An injector provides a singleton instance of a dependency, and can inject this same instance in multiple components.
|
||||
|
||||
* A hierarchy of injectors at the NgModule and component level can provide different instances of a dependency to their own components and child components.
|
||||
|
||||
* You can configure injectors with different providers that can provide different implementations of the same dependency.
|
||||
|
||||
Learn more about the injector hierarchy in the [Dependency Injection guide](guide/hierarchical-dependency-injection).
|
||||
|
||||
|
||||
## Input
|
||||
@ -373,6 +388,17 @@ Lazy loading speeds up application load time by splitting the application into m
|
||||
For example, dependencies can be lazy-loaded as needed&emdash;as opposed to "eager-loaded" modules that are required by the root module, and are thus loaded on launch.
|
||||
Similarly, the [router](guide/glossary#router) can load child views only when the parent view is activated, and you can build custom elements that can be loaded into an Angular app when needed.
|
||||
|
||||
{@a library}
|
||||
|
||||
## Library
|
||||
|
||||
In Angular, a [project](guide/glossary#project) that provides functionality that can be included in other Angular apps. A library is not a complete Angular app, and it cannot run independently.
|
||||
|
||||
* Library developers can use the [CLI](guide/glossary#cli) to `generate` scaffolding for a new library in an existing [workspace](guide/glossary#workspace), and can publish a library as an `npm` package.
|
||||
|
||||
* App developers can use the [CLI](guide/glossary#cli) to `add` a published library for use with an app in the same [workspace](guide/glossary#workspace).
|
||||
|
||||
|
||||
## Lifecycle hook
|
||||
|
||||
An interface that allows you to tap into the lifecycle of [directives](guide/glossary#directive) and [components](guide/glossary#component) as they are created, updated, and destroyed.
|
||||
@ -402,7 +428,7 @@ In general, a module collects a block of code dedicated to a single purpose. Ang
|
||||
|
||||
In JavaScript (ECMAScript), each file is a module and all objects defined in the file belong to that module. Objects can exported, making them public, and public objects can be imported for use by other modules.
|
||||
|
||||
Angular ships as a collection of JavaScript modules, or libraries. Each Angular library name begins with the `@angular` prefix. Install them with the NPM package manager and import parts of them with JavaScript `import` declarations.
|
||||
Angular ships as a collection of JavaScript modules, or libraries. Each Angular library name begins with the `@angular` prefix. Install them with the npm package manager and import parts of them with JavaScript `import` declarations.
|
||||
|
||||
Compare the Angular [NgModule](guide/glossary#ngmodule).
|
||||
|
||||
@ -470,8 +496,13 @@ To learn more, see the [pipes](guide/pipes) page.
|
||||
|
||||
## Polyfill
|
||||
|
||||
An [NPM package](guide/npm-packages) that plugs gaps in a browser's JavaScript implementation. See the [Browser Support](guide/browser-support) guide for polyfills that support particular functionality for particular platforms.
|
||||
An [npm package](guide/npm-packages) that plugs gaps in a browser's JavaScript implementation. See the [Browser Support](guide/browser-support) guide for polyfills that support particular functionality for particular platforms.
|
||||
|
||||
{@a project}
|
||||
|
||||
## Project
|
||||
|
||||
In Angular, a folder within a [workspace](guide/glossary#workspace) that contains one or more Angular apps or [libraries](guide/glossary#library).
|
||||
|
||||
## Provider
|
||||
|
||||
@ -531,9 +562,24 @@ For more information, see the [Routing & Navigation](guide/router) page.
|
||||
|
||||
{@a S}
|
||||
|
||||
{@a schematic}
|
||||
|
||||
## Schematic
|
||||
|
||||
A scaffolding library that defines how to generate or transform a programming project by creating, modifying, refactoring, or moving files and code.
|
||||
|
||||
The Angular [CLI](guide/glossary#cli) uses schematics to generate and modify [Angular projects](guide/glossary#project) and parts of projects.
|
||||
|
||||
* Angular provides a set of schematics for use with the CLI.
|
||||
For details, see [Angular CLI documentation].(https://github.com/angular/angular-cli/wiki).
|
||||
|
||||
* Library developers can create schematics that enable the CLI to generate their published libraries.
|
||||
For more information, see https://www.npmjs.com/package/@angular-devkit/schematics.
|
||||
|
||||
|
||||
## Scoped package
|
||||
|
||||
A way to group related NPM packages.
|
||||
A way to group related npm packages.
|
||||
NgModules are delivered within *scoped packages* whose names begin with the Angular *scope name* `@angular`. For example, `@angular/core`, `@angular/common`, `@angular/http`, and `@angular/router`.
|
||||
|
||||
Import a scoped package in the same way that you import a normal package.
|
||||
@ -677,6 +723,12 @@ The view hierarchy does not imply a component hierarchy. Views that are embedded
|
||||
|
||||
See [Custom element](guide/glossary#custom-element)
|
||||
|
||||
{@a workspace}
|
||||
|
||||
## Workspace
|
||||
|
||||
In Angular, a folder that contains a [project](guide/glossary#project).
|
||||
The [CLI](guide/glossary#cli) `new` command creates a workspace to contain apps and libraries, and other commands must be executed from within a workspace folder.
|
||||
|
||||
{@a X}
|
||||
|
||||
|
@ -1034,7 +1034,7 @@ Call `request.flush()` with an error message, as seen in the following example.
|
||||
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="network-error"
|
||||
region="404"
|
||||
linenums="false">
|
||||
</code-example>
|
||||
|
||||
|
@ -219,7 +219,7 @@ configure services in root and feature modules respectively.
|
||||
|
||||
Angular doesn't recognize these names but Angular developers do.
|
||||
Follow this convention when you write similar modules with configurable service providers.
|
||||
<!--KW--I don't understand how Angular doesn't understand these methods...-->
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -233,9 +233,8 @@ When you import an NgModule,
|
||||
Angular adds the module's service providers (the contents of its `providers` list)
|
||||
to the application root injector.
|
||||
|
||||
This makes the provider visible to every class in the application that knows the provider's lookup token, or knows its name.
|
||||
This makes the provider visible to every class in the application that knows the provider's lookup token, or name.
|
||||
|
||||
This is by design.
|
||||
Extensibility through NgModule imports is a primary goal of the NgModule system.
|
||||
Merging NgModule providers into the application injector
|
||||
makes it easy for a module library to enrich the entire application with new services.
|
||||
@ -247,6 +246,8 @@ If the `HeroModule` provides the `HeroService` and the root `AppModule` imports
|
||||
any class that knows the `HeroService` _type_ can inject that service,
|
||||
not just the classes declared in the `HeroModule`.
|
||||
|
||||
To limit access to a service, consider lazy loading the NgModule that provides that service. See [How do I restrict service scope to a module?](guide/ngmodule-faq#service-scope) for more information.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-lazy-loaded-module-provider-visibility}
|
||||
@ -288,6 +289,7 @@ The `AppModule` always wins.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a service-scope}
|
||||
|
||||
## How do I restrict service scope to a module?
|
||||
|
||||
@ -335,6 +337,8 @@ You can embed the child components in the top component's template.
|
||||
Alternatively, make the top component a routing host by giving it a `<router-outlet>`.
|
||||
Define child routes and let the router load module components into that outlet.
|
||||
|
||||
Though you can limit access to a service by providing it in a lazy loaded module or providing it in a component, providing services in a component can lead to multiple instances of those services. Thus, the lazy loading is preferable.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-root-component-or-module}
|
||||
|
@ -3,7 +3,7 @@
|
||||
Every application starts out with what seems like a simple task: get data, transform them, and show them to users.
|
||||
Getting data could be as simple as creating a local variable or as complex as streaming data over a WebSocket.
|
||||
|
||||
Once data arrive, you could push their raw `toString` values directly to the view,
|
||||
Once data arrives, you could push their raw `toString` values directly to the view,
|
||||
but that rarely makes for a good user experience.
|
||||
For example, in most use cases, users prefer to see a date in a simple format like
|
||||
<samp>April 15, 1988</samp> rather than the raw string format
|
||||
|
@ -23,6 +23,7 @@ Unless otherwise noted, patterns use a limited glob format:
|
||||
|
||||
* `**` matches 0 or more path segments.
|
||||
* `*` matches 0 or more characters excluding `/`.
|
||||
* `?` matches exactly one character excluding `/`.
|
||||
* The `!` prefix marks the pattern as being negative, meaning that only files that don't match the pattern will be included.
|
||||
|
||||
Example patterns:
|
||||
@ -106,7 +107,7 @@ This section describes the resources to cache, broken up into three groups.
|
||||
* `versionedFiles` has been deprecated. As of v6 `versionedFiles` and `files` options have the same behavior. Use `files` instead.
|
||||
|
||||
* `urls` includes both URLs and URL patterns that will be matched at runtime. These resources are not fetched directly and do not have content hashes, but they will be cached according to their HTTP headers. This is most useful for CDNs such as the Google Fonts service.<br>
|
||||
_(Negative glob patterns are not supported.)_
|
||||
_(Negative glob patterns are not supported and `?` will be matched literally; i.e. it will not match any character other than `?`.)_
|
||||
|
||||
## `dataGroups`
|
||||
|
||||
@ -133,7 +134,7 @@ Similar to `assetGroups`, every data group has a `name` which uniquely identifie
|
||||
|
||||
### `urls`
|
||||
A list of URL patterns. URLs that match these patterns will be cached according to this data group's policy.<br>
|
||||
_(Negative glob patterns are not supported.)_
|
||||
_(Negative glob patterns are not supported and `?` will be matched literally; i.e. it will not match any character other than `?`.)_
|
||||
|
||||
### `version`
|
||||
Occasionally APIs change formats in a way that is not backward-compatible. A new version of the app may not be compatible with the old API format and thus may not be compatible with existing cached resources from that API.
|
||||
|
@ -212,7 +212,7 @@ You can get runtime information about the current platform and the `appId` by in
|
||||
|
||||
### Build Destination
|
||||
|
||||
A Universal app is distributed in two parts: the server-side code that serves up the initial application, and the client-side code that's loaded in dynamically.
|
||||
A Universal app is distributed in two parts: the server-side code that serves up the initial application, and the client-side code that's loaded in dynamically.
|
||||
|
||||
The Angular CLI outputs the client-side code in the `dist` directory by default, so you modify the `outputPath` for the __build__ target in the `angular.json` to keep the client-side build outputs separate from the server-side code. The client-side build output will be served by the Express server.
|
||||
|
||||
@ -223,7 +223,7 @@ The Angular CLI outputs the client-side code in the `dist` directory by default,
|
||||
"options": {
|
||||
"outputPath": "dist/browser",
|
||||
...
|
||||
}
|
||||
}
|
||||
}
|
||||
...
|
||||
```
|
||||
@ -235,13 +235,13 @@ The Angular CLI outputs the client-side code in the `dist` directory by default,
|
||||
The tutorial's `HeroService` and `HeroSearchService` delegate to the Angular `HttpClient` module to fetch application data.
|
||||
These services send requests to _relative_ URLs such as `api/heroes`.
|
||||
|
||||
In a Universal app, HTTP URLs must be _absolute_, for example, `https://my-server.com/api/heroes`
|
||||
In a Universal app, HTTP URLs must be _absolute_, for example, `https://my-server.com/api/heroes`
|
||||
even when the Universal web server is capable of handling those requests.
|
||||
|
||||
You'll have to change the services to make requests with absolute URLs when running on the server
|
||||
and with relative URLs when running in the browser.
|
||||
|
||||
One solution is to provide the server's runtime origin under the Angular [`APP_BASE_REF` token](api/common/APP_BASE_HREF),
|
||||
One solution is to provide the server's runtime origin under the Angular [`APP_BASE_HREF` token](api/common/APP_BASE_HREF),
|
||||
inject it into the service, and prepend the origin to the request URL.
|
||||
|
||||
Start by changing the `HeroService` constructor to take a second `origin` parameter that is optionally injected via the `APP_BASE_HREF` token.
|
||||
@ -288,7 +288,7 @@ This is also the place to register providers that are specific to running your a
|
||||
|
||||
### App server entry point
|
||||
|
||||
The `Angular CLI` uses the `AppServerModule` to build the server-side bundle.
|
||||
The `Angular CLI` uses the `AppServerModule` to build the server-side bundle.
|
||||
|
||||
Create a `main.server.ts` file in the `src/` directory that exports the `AppServerModule`:
|
||||
|
||||
|
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 |
@ -360,7 +360,7 @@
|
||||
"picture": "jorgeucano.jpg",
|
||||
"twitter": "jorgeucano",
|
||||
"website": "https://medium.com/@jorgeucano",
|
||||
"bio": "Jorge is a Fulll Stack Developer in ByteDefault ... Professor in several courses related to javascript , speaker, and writer of technical articles and a book ‘Entendiendo Angular’, Google Developer Expert in web technologies nominate by Google, Nativescript Developer Expert nominated by Telerik.",
|
||||
"bio": "Jorge is a Full Stack Developer in ByteDefault, a professor for several courses related to JavaScript, a speaker, and an author of technical articles and the book \"Entendiendo Angular.\" He is a Google Developer Expert in web technologies (nominated by Google) and a NativeScript Developer Expert (nominated by Telerik).",
|
||||
"group": "GDE"
|
||||
},
|
||||
|
||||
@ -378,8 +378,8 @@
|
||||
"picture": "michaelprentice.jpg",
|
||||
"twitter": "splaktar",
|
||||
"website": "https://www.DevIntent.com",
|
||||
"bio": "Owner and consultant at DevIntent. Active open-source contributor and leader. Passionate advocate, coach, and consultant for LEAN and Agile teams. Google Developer Expert (GDE) in Angular. Founder and organizer for the Google Developers Group (GDG) community on the Space Coast of Florida, USA.",
|
||||
"group": "GDE"
|
||||
"bio": "Lead for AngularJS Material. Owner and consultant at DevIntent. Ex-Angular GDE. Founder of the Google Developers Group (GDG) community on the Space Coast of Florida, USA.",
|
||||
"group": "Angular"
|
||||
},
|
||||
|
||||
"mikebrocchi": {
|
||||
@ -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/"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -605,6 +612,12 @@
|
||||
"title": "Ultimate Angular",
|
||||
"url": "https://ultimateangular.com/"
|
||||
},
|
||||
"willh-angular-zero": {
|
||||
"desc": "Online video course in Chinese for newbies who need to learning from the scratch in Chinese. It's covering Angular, Angular CLI, TypeScript, VSCode, and some must known knowledge of Angular development.",
|
||||
"rev": true,
|
||||
"title": "Angular in Action: Start From Scratch (正體中文)",
|
||||
"url": "https://www.udemy.com/angular-zero/?couponCode=ANGULAR.IO"
|
||||
},
|
||||
"angular-firebase": {
|
||||
"desc": "Video lessons covering progressive web apps with Angular, Firebase, RxJS, and related APIs.",
|
||||
"rev": true,
|
||||
@ -659,8 +672,8 @@
|
||||
"url": "http://ninja-squad.com/formations/formation-angular2"
|
||||
},
|
||||
"a2b": {
|
||||
"desc": "Angular Boot Camp covers introductory and intermediate content. It includes extensive workshop session, with hands-on help from our experienced developer-trainers. At the end of this class, student are usually able to use AngularJS to make an end-to-end, working application.",
|
||||
"logo": "",
|
||||
"desc": "Angular Boot Camp covers introductory through advanced Angular topics. It includes extensive workshop sessions, with hands-on help from our experienced developer-trainers. We take developers or teams from the beginnings of Angular understanding through a working knowledge of all essential Angular features.",
|
||||
"logo": "https://angularbootcamp.com/images/angular-boot-camp-logo.svg",
|
||||
"rev": true,
|
||||
"title": "Angular Boot Camp",
|
||||
"url": "https://angularbootcamp.com"
|
||||
|
@ -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",
|
||||
|
@ -142,6 +142,9 @@ Here are the code files discussed on this page and your app should look like thi
|
||||
<code-pane title="src/app/heroes/heroes.component.html" path="toh-pt3/src/app/heroes/heroes.component.html">
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="src/app/app.module.ts" path="toh-pt3/src/app/app.module.ts">
|
||||
</code-pane>
|
||||
|
||||
</code-tabs>
|
||||
|
||||
## Summary
|
||||
|
@ -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>
|
||||
|
||||
|
@ -595,7 +595,7 @@ describe('DocViewerComponent', () => {
|
||||
describe(`(.${NO_ANIMATIONS}: ${noAnimations})`, () => {
|
||||
beforeEach(() => docViewerEl.classList[noAnimations ? 'add' : 'remove'](NO_ANIMATIONS));
|
||||
|
||||
it('should return an observable', (done: DoneFn) => {
|
||||
it('should return an observable', done => {
|
||||
docViewer.swapViews().subscribe(done, done.fail);
|
||||
});
|
||||
|
||||
|
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
|
||||
|
@ -9,8 +9,8 @@
|
||||
],
|
||||
"dependencies": [],
|
||||
"devDependencies": [
|
||||
"@angular/cli",
|
||||
"@angular-devkit/build-angular",
|
||||
"@angular/cli",
|
||||
"@types/jasminewd2",
|
||||
"jasmine-spec-reporter",
|
||||
"karma-coverage-istanbul-reporter",
|
||||
|
@ -11,8 +11,8 @@
|
||||
],
|
||||
"dependencies": [],
|
||||
"devDependencies": [
|
||||
"@angular/cli",
|
||||
"@angular-devkit/build-angular",
|
||||
"@angular/cli",
|
||||
"@types/jasminewd2",
|
||||
"jasmine-spec-reporter",
|
||||
"karma-coverage-istanbul-reporter",
|
||||
|
@ -1,19 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('canonical-path');
|
||||
const fs = require('fs');
|
||||
|
||||
const examplesPath = path.resolve(__dirname, '../../../examples');
|
||||
const packageFolder = path.resolve(__dirname);
|
||||
|
||||
class PackageJsonCustomizer {
|
||||
constructor() {
|
||||
this.dependenciesPackageJson = require(path.join(examplesPath, '/shared/package.json'));
|
||||
this.scriptsPackageJson = require(path.join(examplesPath, '/shared/boilerplate/systemjs/package.json'));
|
||||
this.basePackageJson = require(`${packageFolder}/base.json`);
|
||||
this.dependenciesPackageJson = this.readJson(path.join(examplesPath, '/shared/package.json'));
|
||||
this.scriptsPackageJson = this.readJson(path.join(examplesPath, '/shared/boilerplate/systemjs/package.json'));
|
||||
this.basePackageJson = this.readJson(`${packageFolder}/base.json`);
|
||||
this.templatePackageJson = this.readJson(`${packageFolder}/package.json`, false);
|
||||
}
|
||||
|
||||
generate(type = 'systemjs') {
|
||||
let packageJson = require(`${packageFolder}/package.json`);
|
||||
let packageJson = JSON.parse(this.templatePackageJson);
|
||||
let rules = require(`${packageFolder}/${type}.json`);
|
||||
|
||||
this._mergeJSON(rules, this.basePackageJson);
|
||||
@ -39,8 +41,8 @@ class PackageJsonCustomizer {
|
||||
return JSON.stringify(packageJson, null, 2);
|
||||
}
|
||||
|
||||
_mergeJSON(json1,json2) {
|
||||
var result = json1 ;
|
||||
_mergeJSON(json1, json2) {
|
||||
var result = json1;
|
||||
for (var prop in json2)
|
||||
{
|
||||
if (json2.hasOwnProperty(prop))
|
||||
@ -50,6 +52,12 @@ class PackageJsonCustomizer {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
readJson(jsonFile, parse = true) {
|
||||
const contents = fs.readFileSync(jsonFile, 'utf8');
|
||||
|
||||
return parse ? JSON.parse(contents) : contents;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PackageJsonCustomizer;
|
||||
|
@ -9,6 +9,7 @@
|
||||
],
|
||||
"dependencies": [],
|
||||
"devDependencies": [
|
||||
"@angular-devkit/build-angular",
|
||||
"@angular/cli",
|
||||
"@types/jasminewd2",
|
||||
"jasmine-marbles",
|
||||
|
@ -15,6 +15,7 @@
|
||||
"@nguniversal/module-map-ngfactory-loader"
|
||||
],
|
||||
"devDependencies": [
|
||||
"@angular-devkit/build-angular",
|
||||
"@angular/cli",
|
||||
"@types/jasminewd2",
|
||||
"jasmine-spec-reporter",
|
||||
|
@ -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",
|
||||
@ -78,6 +77,7 @@
|
||||
"rollup-plugin-node-resolve": "2.0.0",
|
||||
"rollup-plugin-uglify": "^1.0.1",
|
||||
"source-map-explorer": "^1.3.2",
|
||||
"ts-loader": "^4.2.0",
|
||||
"ts-node": "^5.0.1",
|
||||
"tslint": "^5.9.1",
|
||||
"typescript": "2.7.2",
|
||||
|
@ -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"
|
||||
@ -7369,7 +7361,7 @@ semver-intersect@^1.1.2:
|
||||
dependencies:
|
||||
semver "^5.0.0"
|
||||
|
||||
"semver@2 >=2.2.1 || 3.x || 4 || 5", semver@^5.0.0, semver@^5.5.0:
|
||||
"semver@2 >=2.2.1 || 3.x || 4 || 5", semver@^5.0.0, semver@^5.0.1, semver@^5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
|
||||
|
||||
@ -8265,6 +8257,16 @@ trim-right@^1.0.1:
|
||||
dependencies:
|
||||
glob "^6.0.4"
|
||||
|
||||
ts-loader@^4.2.0:
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-4.4.1.tgz#c93a46eea430ebce1f790dfe438caefb8670d365"
|
||||
dependencies:
|
||||
chalk "^2.3.0"
|
||||
enhanced-resolve "^4.0.0"
|
||||
loader-utils "^1.0.2"
|
||||
micromatch "^3.1.4"
|
||||
semver "^5.0.1"
|
||||
|
||||
ts-node@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-5.0.1.tgz#78e5d1cb3f704de1b641e43b76be2d4094f06f81"
|
||||
|
@ -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') $}
|
||||
|
||||
|
@ -21,8 +21,7 @@
|
||||
{% if showType %}<td class="param-type"><code>{$ parameter.type $}</code></td>{% endif %}
|
||||
<td class="param-description">
|
||||
{% marked %}
|
||||
{% if parameter.description | trim %}{$ parameter.description $}
|
||||
|
||||
{% if (parameter.shortDescription | trim) or (parameter.description | trim) %}{$ parameter.shortDescription + '\n\n' + parameter.description $}
|
||||
{% elseif not showType and parameter.type %}<p>Type: <code>{$ parameter.type $}</code>.</p>
|
||||
{% endif %}
|
||||
|
||||
|
4
build.sh
4
build.sh
@ -409,6 +409,10 @@ if [[ ${BUILD_ALL} == true && ${TYPECHECK_ALL} == true ]]; then
|
||||
travisFoldStart "tsc -p ${TSCONFIG}" "no-xtrace"
|
||||
$TSC -p ${TSCONFIG}
|
||||
travisFoldEnd "tsc -p ${TSCONFIG}"
|
||||
TSCONFIG="packages/examples/tsconfig.json"
|
||||
travisFoldStart "tsc -p ${TSCONFIG}" "no-xtrace"
|
||||
$TSC -p ${TSCONFIG}
|
||||
travisFoldEnd "tsc -p ${TSCONFIG}"
|
||||
TSCONFIG="modules/tsconfig.json"
|
||||
travisFoldStart "tsc -p ${TSCONFIG}" "no-xtrace"
|
||||
$TSC -p ${TSCONFIG}
|
||||
|
155
docs/PR_REVIEW.md
Normal file
155
docs/PR_REVIEW.md
Normal file
@ -0,0 +1,155 @@
|
||||
# PR Review
|
||||
|
||||
## Tools
|
||||
|
||||
A better way to do a code-review of a PR is to do it in your IDE.
|
||||
Here are two scripts which allow you to perform the review and create local changes which can be appended to the PR.
|
||||
|
||||
### 1. Loading PR
|
||||
|
||||
Run this command to load the changes into your local repository where your IDE is running.
|
||||
|
||||
```
|
||||
$ ./scripts/github/review-pr 24623
|
||||
```
|
||||
|
||||
This will result in output:
|
||||
|
||||
```
|
||||
Already on 'master'
|
||||
Your branch is up to date with 'origin/master'.
|
||||
Fetching pull request #24623 with 1 SHA(s) into branch range: pr/24623_base..pr/24623_top
|
||||
======================================================================================
|
||||
cef93a51b (pr/24623_top) ci: scripts to review PRs locally
|
||||
======================================================================================
|
||||
Switched to a new branch 'pr/24623'
|
||||
On branch pr/24623
|
||||
Untracked files:
|
||||
(use "git add <file>..." to include in what will be committed)
|
||||
|
||||
docs/PR_REVIEW.md
|
||||
scripts/github/push-pr
|
||||
scripts/github/review-pr
|
||||
|
||||
nothing added to commit but untracked files present (use "git add" to track)
|
||||
```
|
||||
|
||||
Note that the script created `pr/24623_top` and `pr/24623_base` branches which denote SHAs where the PR start and end.
|
||||
|
||||
```
|
||||
cef93a51b (pr/24623_top) ci: scripts to review PRs locally
|
||||
637805a0c (pr/24623_base) docs: update `lowercase` pipe example in "AngularJS to Angular" guide (#24588)
|
||||
```
|
||||
|
||||
Knowing `pr/24623_top` and `pr/24623_base` makes it convenient to refer to different SHAs in PR when rebasing or reseting.
|
||||
|
||||
### 2. Review PR
|
||||
|
||||
Because the script has reset the `HEAD` of the PR the changes show up as unstaged files.
|
||||
|
||||
```
|
||||
$ git status
|
||||
On branch pr/24623
|
||||
Untracked files:
|
||||
(use "git add <file>..." to include in what will be committed)
|
||||
|
||||
docs/PR_REVIEW.md
|
||||
scripts/github/push-pr
|
||||
scripts/github/review-pr
|
||||
|
||||
nothing added to commit but untracked files present (use "git add" to track)
|
||||
```
|
||||
|
||||
Use your IDE to review the untracked files as needed.
|
||||
A good trick is to use your IDE to stage the files which were already reviewed.
|
||||
When all files are staged the review is done.
|
||||
|
||||
### 3. Creating Edits
|
||||
|
||||
At any point you can edit any line in the repository.
|
||||
The idea is to create edits locally and push them to the PR later.
|
||||
This is useful because it is often times easier to make minor changes locally than to request the PR author to change and repush through a comment (often times the comment is larger than the change.)
|
||||
|
||||
Example of a local edit.
|
||||
```
|
||||
echo "# here is a change" >> docs/PR_REVIEW.md
|
||||
```
|
||||
|
||||
### 4. Creating a Commit From Local Edits
|
||||
|
||||
Since the HEAD has been reset to `pr/24623_base` so that changes show up in `git status` we have to reverse the reset to only see our local changes.
|
||||
To do that reset the `HEAD` to `pr/24623_top`.
|
||||
|
||||
```
|
||||
$ git reset pr/24623_top
|
||||
```
|
||||
|
||||
Doing so will remove all PR changes and only leave your local modifications which you have done.
|
||||
You can verify by running `git status` and `git diff` to see only your changes (PR changes have been removed.)
|
||||
|
||||
```
|
||||
$ git status
|
||||
On branch pr/24623
|
||||
Changes not staged for commit:
|
||||
(use "git add <file>..." to update what will be committed)
|
||||
(use "git checkout -- <file>..." to discard changes in working directory)
|
||||
|
||||
modified: docs/PR_REVIEW.md
|
||||
|
||||
no changes added to commit (use "git add" and/or "git commit -a")
|
||||
```
|
||||
```
|
||||
$ git diff
|
||||
diff --git a/docs/PR_REVIEW.md b/docs/PR_REVIEW.md
|
||||
index 184b5aeca..83517fbe0 100644
|
||||
--- a/docs/PR_REVIEW.md
|
||||
+++ b/docs/PR_REVIEW.md
|
||||
@@ -8,4 +8,4 @@ A better way to do code review of the PR is to do it in your IDE. Here are two s
|
||||
existing text
|
||||
-
|
||||
\ No newline at end of file
|
||||
+# here is a change
|
||||
```
|
||||
|
||||
Next step is to turn your local changes into a `fixup!` commit.
|
||||
Run `git commit --all --fixup HEAD` to create a `fixup!` commit.
|
||||
|
||||
NOTE: If you added new files they must be added using `git add .` or they will not be picked up by the `git commit --all` flag.
|
||||
|
||||
```
|
||||
$ git commit --all --fixup HEAD
|
||||
[pr/24623 45ae87ce4] fixup! ci: scripts to review PRs locally
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
```
|
||||
|
||||
You can verify that the `fixup!` commit with your local modifications was created.
|
||||
```
|
||||
$ git log --oneline
|
||||
45ae87ce4 (HEAD -> pr/24623) fixup! ci: scripts to review PRs locally
|
||||
cef93a51b (pr/24623_top) ci: scripts to review PRs locally
|
||||
```
|
||||
|
||||
### 5. Pushing local edits back to the PR
|
||||
|
||||
The last step is to push your local changes back into the PR.
|
||||
Use `./scripts/github/push-pr` script for that.
|
||||
|
||||
```
|
||||
$ ./scripts/github/push-pr
|
||||
Assuming PR #24623
|
||||
>>> git push git@github.com:mhevery/angular.git HEAD:review_pr_script
|
||||
Counting objects: 4, done.
|
||||
Delta compression using up to 8 threads.
|
||||
Compressing objects: 100% (4/4), done.
|
||||
Writing objects: 100% (4/4), 392 bytes | 392.00 KiB/s, done.
|
||||
Total 4 (delta 3), reused 0 (delta 0)
|
||||
remote: Resolving deltas: 100% (3/3), completed with 3 local objects.
|
||||
To github.com:mhevery/angular.git
|
||||
cef93a51b..45ae87ce4 HEAD -> review_pr_script
|
||||
```
|
||||
|
||||
NOTE: Notice that we did not have to specify the PR number since the script can guess it from the branch name.
|
||||
|
||||
If you visit https://github.com/angular/angular/pull/24623/commits you will see that your `fixup!` commit has been added to the PR.
|
||||
This greatly simplifies the work for many minor changes to the PR.
|
||||
|
@ -21,6 +21,7 @@ The components have a clear piece of source code associated with it within the `
|
||||
* `comp: core & compiler` - because core, compiler, compiler-cli and
|
||||
browser-platforms are very intertwined, we will be treating them as one
|
||||
* `comp: ivy` - a subset of core representing the new Ivy renderer.
|
||||
* `comp: elements`
|
||||
* `comp: forms`
|
||||
* `comp: http`
|
||||
* `comp: i18n`
|
||||
|
@ -1,7 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "../dist/e2e",
|
||||
"types": ["jasmine"],
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2"
|
||||
],
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,9 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/jasmine": "file:../../node_modules/@types/jasmine",
|
||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||
"@types/jasmine": "file:../../node_modules/@types/jasmine",
|
||||
"@types/jasminewd2": "file:../../node_modules/@types/jasminewd2",
|
||||
"concurrently": "3.4.0",
|
||||
"lite-server": "2.2.2",
|
||||
"protractor": "file:../../node_modules/protractor",
|
||||
|
@ -10,6 +10,7 @@ import * as compiler from '@angular/compiler';
|
||||
import * as compilerTesting from '@angular/compiler/testing';
|
||||
import * as core from '@angular/core';
|
||||
import * as coreTesting from '@angular/core/testing';
|
||||
import * as elements from '@angular/elements';
|
||||
import * as forms from '@angular/forms';
|
||||
import * as http from '@angular/http';
|
||||
import * as httpTesting from '@angular/http/testing';
|
||||
@ -30,6 +31,7 @@ export default {
|
||||
compilerTesting,
|
||||
core,
|
||||
coreTesting,
|
||||
elements,
|
||||
forms,
|
||||
http,
|
||||
httpTesting,
|
||||
|
@ -9,6 +9,7 @@
|
||||
"@angular/compiler": "file:../../dist/packages-dist/compiler",
|
||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||
"@angular/core": "file:../../dist/packages-dist/core",
|
||||
"@angular/elements": "file:../../dist/packages-dist/elements",
|
||||
"@angular/forms": "file:../../dist/packages-dist/forms",
|
||||
"@angular/http": "file:../../dist/packages-dist/http",
|
||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user