Compare commits
62 Commits
Author | SHA1 | Date | |
---|---|---|---|
9dc310eb50 | |||
b26bc9096e | |||
56c98f7c23 | |||
6bf07b4e60 | |||
a2ff4abddc | |||
ee37d4b26d | |||
f99335bc47 | |||
445d833b5d | |||
cbd93fe0d0 | |||
e1dfd9b17a | |||
e95b5d77bc | |||
9e69d97b77 | |||
b450c83091 | |||
38be44df9f | |||
e4ce695b66 | |||
4da184c29b | |||
647ca64216 | |||
53aff1fb83 | |||
71a6f1768e | |||
108b6e77dd | |||
ead759670b | |||
bdaee508cf | |||
e099911dd0 | |||
66fd1f8ce6 | |||
0e8f0288cb | |||
c7b211ca3c | |||
22bbd6e045 | |||
2a5b6194bb | |||
264260f997 | |||
c70cd258d5 | |||
2b0c896d78 | |||
c0bf1b0545 | |||
65a40e659b | |||
215832d8c6 | |||
686c4efe6d | |||
efce39605b | |||
b567f9cc21 | |||
4ff60c3562 | |||
bc904b19a4 | |||
135cf226bf | |||
0550383fc9 | |||
07699cbaec | |||
e4bb077d27 | |||
27e7439c3b | |||
66d160215e | |||
0feba49c4b | |||
9ca6ee9eed | |||
a5a82296b7 | |||
f9f2c20a57 | |||
662422a67e | |||
b53ead4c45 | |||
d0abfa3acc | |||
3bf36e9492 | |||
82aace63ea | |||
15795d09cf | |||
8ddbed8f7b | |||
81f1d42328 | |||
3df1542c87 | |||
8e1e7faef4 | |||
27f8c69d8a | |||
5d0dd97402 | |||
814f06289b |
@ -22,7 +22,6 @@
|
||||
# petebacondarwin - Pete Bacon Darwin
|
||||
# pkozlowski-opensource - Pawel Kozlowski
|
||||
# robwormald - Rob Wormald
|
||||
# tbosch - Tobias Bosch
|
||||
# tinayuangao - Tina Gao
|
||||
# vicb - Victor Berchet
|
||||
# vikerman - Vikram Subramanian
|
||||
@ -100,7 +99,6 @@ groups:
|
||||
users:
|
||||
- alexeagle
|
||||
- mhevery
|
||||
- tbosch
|
||||
- vicb
|
||||
- IgorMinar #fallback
|
||||
|
||||
@ -109,8 +107,7 @@ groups:
|
||||
files:
|
||||
- "packages/core/*"
|
||||
users:
|
||||
- tbosch #primary
|
||||
- chuckjaz
|
||||
- chuckjaz #primary
|
||||
- mhevery
|
||||
- vicb
|
||||
- IgorMinar #fallback
|
||||
@ -132,7 +129,7 @@ groups:
|
||||
- "packages/compiler/src/i18n/*"
|
||||
users:
|
||||
- vicb #primary
|
||||
- tbosch
|
||||
- chuckjaz
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
@ -141,9 +138,8 @@ groups:
|
||||
files:
|
||||
- "packages/compiler/*"
|
||||
users:
|
||||
- tbosch #primary
|
||||
- chuckjaz #primary
|
||||
- vicb
|
||||
- chuckjaz
|
||||
- mhevery
|
||||
- IgorMinar #fallback
|
||||
|
||||
@ -163,12 +159,11 @@ groups:
|
||||
- "packages/compiler-cli/*"
|
||||
- "packages/bazel/*"
|
||||
exclude:
|
||||
- "packages/compiler-cli/src/ngtools*"
|
||||
- "packages/compiler-cli/src/ngtools*"
|
||||
users:
|
||||
- alexeagle
|
||||
- chuckjaz
|
||||
- vicb
|
||||
- tbosch
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
@ -212,7 +207,7 @@ groups:
|
||||
- "packages/language-service/*"
|
||||
users:
|
||||
- chuckjaz #primary
|
||||
- tbosch #secondary
|
||||
# needs secondary
|
||||
- vicb
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
@ -242,8 +237,8 @@ groups:
|
||||
files:
|
||||
- "packages/platform-browser/*"
|
||||
users:
|
||||
- tbosch #primary
|
||||
- vicb #secondary
|
||||
- vicb #primary
|
||||
# needs secondary
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
@ -253,9 +248,9 @@ groups:
|
||||
- "packages/platform-server/*"
|
||||
users:
|
||||
- vikerman #primary
|
||||
# needs secondary
|
||||
- alxhub
|
||||
- vicb
|
||||
- tbosch
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
@ -265,7 +260,7 @@ groups:
|
||||
- "packages/platform-webworker/*"
|
||||
users:
|
||||
- vicb #primary
|
||||
- tbosch #secondary
|
||||
# needs secondary
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
@ -284,7 +279,7 @@ groups:
|
||||
files:
|
||||
- "packages/benchpress/*"
|
||||
users:
|
||||
- tbosch #primary
|
||||
- alxhub #primary
|
||||
# needs secondary
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
49
CHANGELOG.md
49
CHANGELOG.md
@ -1,3 +1,52 @@
|
||||
<a name="5.0.5"></a>
|
||||
## [5.0.5](https://github.com/angular/angular/compare/5.0.4...5.0.5) (2017-12-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** propagate ts.SourceFile moduleName into metadata ([a2ff4ab](https://github.com/angular/angular/commit/a2ff4ab))
|
||||
* **service-worker:** allow disabling SW while still using services ([f99335b](https://github.com/angular/angular/commit/f99335b))
|
||||
* **service-worker:** don't crash if SW not supported ([ee37d4b](https://github.com/angular/angular/commit/ee37d4b))
|
||||
* **service-worker:** send initialization signal from the application ([6bf07b4](https://github.com/angular/angular/commit/6bf07b4))
|
||||
* **service-worker:** use relative path for ngsw.json ([56c98f7](https://github.com/angular/angular/commit/56c98f7))
|
||||
|
||||
|
||||
|
||||
<a name="5.0.4"></a>
|
||||
## [5.0.4](https://github.com/angular/angular/compare/5.0.3...5.0.4) (2017-12-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** ensure multi-level enter animations work ([#19455](https://github.com/angular/angular/issues/19455)) ([22bbd6e](https://github.com/angular/angular/commit/22bbd6e))
|
||||
* **animations:** ensure multi-level leave animations work ([#19455](https://github.com/angular/angular/issues/19455)) ([c7b211c](https://github.com/angular/angular/commit/c7b211c))
|
||||
* **common:** accept falsy values as HTTP bodies ([#19958](https://github.com/angular/angular/issues/19958)) ([66fd1f8](https://github.com/angular/angular/commit/66fd1f8)), closes [#19825](https://github.com/angular/angular/issues/19825) [#19195](https://github.com/angular/angular/issues/19195)
|
||||
* **common:** don't strip XSSI prefix for if error isn't JSON ([#19958](https://github.com/angular/angular/issues/19958)) ([ead7596](https://github.com/angular/angular/commit/ead7596))
|
||||
* **common:** remove useless guard in HttpClient ([#19958](https://github.com/angular/angular/issues/19958)) ([e099911](https://github.com/angular/angular/commit/e099911)), closes [#19223](https://github.com/angular/angular/issues/19223)
|
||||
* **common:** treat an empty body as null when parsing JSON in HttpClient ([#19958](https://github.com/angular/angular/issues/19958)) ([bdaee50](https://github.com/angular/angular/commit/bdaee50)), closes [#18680](https://github.com/angular/angular/issues/18680) [#19413](https://github.com/angular/angular/issues/19413) [#19502](https://github.com/angular/angular/issues/19502) [#19555](https://github.com/angular/angular/issues/19555)
|
||||
* **compiler-cli:** fix memory leak in program creation ([#20692](https://github.com/angular/angular/issues/20692)) ([38be44d](https://github.com/angular/angular/commit/38be44d)), closes [#20691](https://github.com/angular/angular/issues/20691)
|
||||
* **compiler-cli:** normalize sourcepaths for i18n extracted files ([#20417](https://github.com/angular/angular/issues/20417)) ([2b0c896](https://github.com/angular/angular/commit/2b0c896)), closes [#20416](https://github.com/angular/angular/issues/20416)
|
||||
|
||||
|
||||
|
||||
<a name="5.0.3"></a>
|
||||
## [5.0.3](https://github.com/angular/angular/compare/5.0.2...5.0.3) (2017-11-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** always fire inner trigger callbacks even if blocked by parent animations ([#19753](https://github.com/angular/angular/issues/19753)) ([814f062](https://github.com/angular/angular/commit/814f062)), closes [#19100](https://github.com/angular/angular/issues/19100)
|
||||
* **animations:** validate against trigger() names that use @ symbols ([#20326](https://github.com/angular/angular/issues/20326)) ([15795d0](https://github.com/angular/angular/commit/15795d0))
|
||||
* **benchpress:** Allow ignoring navigationStart events in perflog metric. ([#20312](https://github.com/angular/angular/issues/20312)) ([9ca6ee9](https://github.com/angular/angular/commit/9ca6ee9))
|
||||
* **common:** return ISubscription from Location.subscribe() ([#20429](https://github.com/angular/angular/issues/20429)) ([bc904b1](https://github.com/angular/angular/commit/bc904b1)), closes [#20406](https://github.com/angular/angular/issues/20406)
|
||||
* **compiler:** emit correct type-check-blocks with TemplateRef's ([#20463](https://github.com/angular/angular/issues/20463)) ([81f1d42](https://github.com/angular/angular/commit/81f1d42))
|
||||
* **compiler:** support event bindings in `fullTemplateTypeCheck` ([#20490](https://github.com/angular/angular/issues/20490)) ([b53ead4](https://github.com/angular/angular/commit/b53ead4))
|
||||
* **core:** fix [#20532](https://github.com/angular/angular/issues/20532), should be able to cancel listener from mixed zone ([#20538](https://github.com/angular/angular/issues/20538)) ([0feba49](https://github.com/angular/angular/commit/0feba49))
|
||||
* **core:** should support event.stopImmediatePropagation ([#20469](https://github.com/angular/angular/issues/20469)) ([82aace6](https://github.com/angular/angular/commit/82aace6))
|
||||
* **forms:** updateOn should check if change occurred ([#20358](https://github.com/angular/angular/issues/20358)) ([f9f2c20](https://github.com/angular/angular/commit/f9f2c20)), closes [#20259](https://github.com/angular/angular/issues/20259)
|
||||
|
||||
|
||||
|
||||
<a name="5.0.2"></a>
|
||||
## [5.0.2](https://github.com/angular/angular/compare/5.0.1...5.0.2) (2017-11-16)
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
<!-- #docregion -->
|
||||
<h1>My First Attribute Directive</h1>
|
||||
<!-- #docregion applied -->
|
||||
<p appHightlight>Highlight me!</p>
|
||||
<p appHighlight>Highlight me!</p>
|
||||
<!-- #enddocregion applied, -->
|
||||
|
||||
<!-- #docregion color-1 -->
|
||||
<p appHightlight highlightColor="yellow">Highlighted in yellow</p>
|
||||
<p appHightlight [highlightColor]="'orange'">Highlighted in orange</p>
|
||||
<p appHighlight highlightColor="yellow">Highlighted in yellow</p>
|
||||
<p appHighlight [highlightColor]="'orange'">Highlighted in orange</p>
|
||||
<!-- #enddocregion color-1 -->
|
||||
|
||||
<!-- #docregion color-2 -->
|
||||
<p appHightlight [highlightColor]="color">Highlighted with parent component's color</p>
|
||||
<p appHighlight [highlightColor]="color">Highlighted with parent component's color</p>
|
||||
<!-- #enddocregion color-2 -->
|
||||
|
@ -93,22 +93,20 @@ export class AfterContentComponent implements AfterContentChecked, AfterContentI
|
||||
|
||||
<h4>-- AfterContent Logs --</h4>
|
||||
<p><button (click)="reset()">Reset</button></p>
|
||||
<div *ngFor="let msg of logs">{{msg}}</div>
|
||||
<div *ngFor="let msg of logger.logs">{{msg}}</div>
|
||||
</div>
|
||||
`,
|
||||
styles: ['.parent {background: burlywood}'],
|
||||
providers: [LoggerService]
|
||||
})
|
||||
export class AfterContentParentComponent {
|
||||
logs: string[];
|
||||
show = true;
|
||||
|
||||
constructor(private logger: LoggerService) {
|
||||
this.logs = logger.logs;
|
||||
constructor(public logger: LoggerService) {
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.logs.length = 0;
|
||||
this.logger.clear();
|
||||
// quickly remove and reload AfterContentComponent which recreates it
|
||||
this.show = false;
|
||||
this.logger.tick_then(() => this.show = true);
|
||||
|
@ -95,22 +95,20 @@ export class AfterViewComponent implements AfterViewChecked, AfterViewInit {
|
||||
|
||||
<h4>-- AfterView Logs --</h4>
|
||||
<p><button (click)="reset()">Reset</button></p>
|
||||
<div *ngFor="let msg of logs">{{msg}}</div>
|
||||
<div *ngFor="let msg of logger.logs">{{msg}}</div>
|
||||
</div>
|
||||
`,
|
||||
styles: ['.parent {background: burlywood}'],
|
||||
providers: [LoggerService]
|
||||
})
|
||||
export class AfterViewParentComponent {
|
||||
logs: string[];
|
||||
show = true;
|
||||
|
||||
constructor(private logger: LoggerService) {
|
||||
this.logs = logger.logs;
|
||||
constructor(public logger: LoggerService) {
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.logs.length = 0;
|
||||
this.logger.clear();
|
||||
// quickly remove and reload AfterViewComponent which recreates it
|
||||
this.show = false;
|
||||
this.logger.tick_then(() => this.show = true);
|
||||
|
@ -27,7 +27,7 @@ export class MyCounterComponent implements OnChanges {
|
||||
// Empty the changeLog whenever counter goes to zero
|
||||
// hint: this is a way to respond programmatically to external value changes.
|
||||
if (this.counter === 0) {
|
||||
this.changeLog.length = 0;
|
||||
this.changeLog = [];
|
||||
}
|
||||
|
||||
// A change to `counter` is the only change we care about
|
||||
|
@ -68,7 +68,7 @@ export class DoCheckComponent implements DoCheck {
|
||||
|
||||
reset() {
|
||||
this.changeDetected = true;
|
||||
this.changeLog.length = 0;
|
||||
this.changeLog = [];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ export class LoggerService {
|
||||
}
|
||||
}
|
||||
|
||||
clear() { this.logs.length = 0; }
|
||||
clear() { this.logs = []; }
|
||||
|
||||
// schedules a view refresh to ensure display catches up
|
||||
tick() { this.tick_then(() => { }); }
|
||||
|
@ -43,7 +43,7 @@ export class OnChangesComponent implements OnChanges {
|
||||
}
|
||||
// #enddocregion ng-on-changes
|
||||
|
||||
reset() { this.changeLog.length = 0; }
|
||||
reset() { this.changeLog = []; }
|
||||
}
|
||||
|
||||
/***************************************/
|
||||
|
@ -12,5 +12,5 @@
|
||||
</div>
|
||||
<!-- #enddocregion template -->
|
||||
<h4>-- Spy Lifecycle Hook Log --</h4>
|
||||
<div *ngFor="let msg of spyLog">{{msg}}</div>
|
||||
<div *ngFor="let msg of logger.logs">{{msg}}</div>
|
||||
</div>
|
||||
|
@ -15,10 +15,8 @@ import { LoggerService } from './logger.service';
|
||||
export class SpyParentComponent {
|
||||
newName = 'Herbie';
|
||||
heroes: string[] = ['Windstorm', 'Magneta'];
|
||||
spyLog: string[];
|
||||
|
||||
constructor(private logger: LoggerService) {
|
||||
this.spyLog = logger.logs;
|
||||
constructor(public logger: LoggerService) {
|
||||
}
|
||||
|
||||
addHero() {
|
||||
@ -34,7 +32,7 @@ export class SpyParentComponent {
|
||||
}
|
||||
reset() {
|
||||
this.logger.log('-- reset --');
|
||||
this.heroes.length = 0;
|
||||
this.heroes = [];
|
||||
this.logger.tick();
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
"app/contact/contact.component.html",
|
||||
"app/contact/contact.component.3.ts",
|
||||
"app/contact/contact.service.ts",
|
||||
"app/contact/highlight.directive.ts",
|
||||
"app/contact/contact-highlight.directive.ts",
|
||||
|
||||
"main.1b.ts",
|
||||
"styles.css",
|
||||
|
@ -16,7 +16,7 @@
|
||||
"app/contact/awesome.pipe.ts",
|
||||
"app/contact/contact.component.3.ts",
|
||||
"app/contact/contact.module.2.ts",
|
||||
"app/contact/highlight.directive.ts",
|
||||
"app/contact/contact-highlight.directive.ts",
|
||||
|
||||
"main.2.ts",
|
||||
"styles.css",
|
||||
|
@ -15,7 +15,7 @@ describe('NgModule', function () {
|
||||
|
||||
return {
|
||||
title: element.all(by.tagName('h1')).get(0),
|
||||
subtitle: element.all(by.css('app-title p i')).get(0),
|
||||
welcome: element.all(by.css('app-title p i')).get(0),
|
||||
contactButton: buttons.get(0),
|
||||
crisisButton: buttons.get(1),
|
||||
heroesButton: buttons.get(2)
|
||||
@ -67,7 +67,7 @@ describe('NgModule', function () {
|
||||
|
||||
it('should welcome us', function () {
|
||||
const commons = getCommonsSectionStruct();
|
||||
expect(commons.subtitle.getText()).toBe('Welcome, ' + (name || 'Sherlock Holmes'));
|
||||
expect(commons.welcome.getText()).toBe('Welcome, ' + (name || 'Sherlock Holmes'));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
"app/contact/contact.component.3.ts",
|
||||
"app/contact/contact.module.3.ts",
|
||||
"app/contact/contact-routing.module.3.ts",
|
||||
"app/contact/highlight.directive.ts",
|
||||
"app/contact/contact-highlight.directive.ts",
|
||||
|
||||
"app/crisis/*.ts",
|
||||
|
||||
|
@ -1,14 +1,19 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
export const routes: Routes = [
|
||||
import { ContactModule } from './contact/contact.module.3';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', redirectTo: 'contact', pathMatch: 'full'},
|
||||
{ path: 'crisis', loadChildren: 'app/crisis/crisis.module#CrisisModule' },
|
||||
{ path: 'heroes', loadChildren: 'app/hero/hero.module.3#HeroModule' }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
imports: [
|
||||
ContactModule,
|
||||
RouterModule.forRoot(routes)
|
||||
],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule {}
|
||||
|
@ -2,18 +2,29 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
export const routes: Routes = [
|
||||
import { ContactModule } from './contact/contact.module';
|
||||
|
||||
// #docregion routes
|
||||
const routes: Routes = [
|
||||
{ path: '', redirectTo: 'contact', pathMatch: 'full'},
|
||||
// #docregion lazy-routes
|
||||
// #docregion lazy-routes
|
||||
{ path: 'crisis', loadChildren: 'app/crisis/crisis.module#CrisisModule' },
|
||||
{ path: 'heroes', loadChildren: 'app/hero/hero.module#HeroModule' }
|
||||
// #enddocregion lazy-routes
|
||||
// #enddocregion lazy-routes
|
||||
];
|
||||
// #enddocregion routes
|
||||
|
||||
// #docregion forRoot
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
// #docregion imports
|
||||
imports: [
|
||||
ContactModule,
|
||||
// #docregion forRoot
|
||||
RouterModule.forRoot(routes),
|
||||
// #enddocregion forRoot
|
||||
],
|
||||
// #enddocregion imports
|
||||
// #docregion exports
|
||||
exports: [RouterModule]
|
||||
// #enddocregion exports
|
||||
})
|
||||
export class AppRoutingModule {}
|
||||
// #enddocregion forRoot
|
||||
|
@ -6,5 +6,5 @@ import { Component } from '@angular/core';
|
||||
template: '<h1>{{title}}</h1>',
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'Minimal NgModule';
|
||||
title = 'Angular Modules';
|
||||
}
|
||||
|
@ -11,9 +11,7 @@ import { Component } from '@angular/core';
|
||||
// #enddocregion template
|
||||
*/
|
||||
// #docregion
|
||||
template: '<app-title [subtitle]="subtitle"></app-title>'
|
||||
template: '<app-title></app-title>'
|
||||
})
|
||||
export class AppComponent {
|
||||
subtitle = '(v1)';
|
||||
}
|
||||
export class AppComponent {}
|
||||
// #enddocregion
|
||||
|
@ -5,11 +5,9 @@ import { Component } from '@angular/core';
|
||||
selector: 'app-root',
|
||||
// #docregion template
|
||||
template: `
|
||||
<app-title [subtitle]="subtitle"></app-title>
|
||||
<app-title></app-title>
|
||||
<app-contact></app-contact>
|
||||
`
|
||||
// #enddocregion template
|
||||
})
|
||||
export class AppComponent {
|
||||
subtitle = '(v1)';
|
||||
}
|
||||
export class AppComponent {}
|
||||
|
@ -3,10 +3,8 @@ import { Component } from '@angular/core';
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
template: `
|
||||
<app-title [subtitle]="subtitle"></app-title>
|
||||
<app-title></app-title>
|
||||
<app-contact></app-contact>
|
||||
`
|
||||
})
|
||||
export class AppComponent {
|
||||
subtitle = '(v2)';
|
||||
}
|
||||
export class AppComponent {}
|
||||
|
@ -4,7 +4,7 @@ import { Component } from '@angular/core';
|
||||
selector: 'app-root',
|
||||
// #docregion template
|
||||
template: `
|
||||
<app-title [subtitle]="subtitle"></app-title>
|
||||
<app-title></app-title>
|
||||
<nav>
|
||||
<a routerLink="contact" routerLinkActive="active">Contact</a>
|
||||
<a routerLink="crisis" routerLinkActive="active">Crisis Center</a>
|
||||
@ -14,6 +14,4 @@ import { Component } from '@angular/core';
|
||||
`
|
||||
// #enddocregion template
|
||||
})
|
||||
export class AppComponent {
|
||||
subtitle = '(v3)';
|
||||
}
|
||||
export class AppComponent {}
|
||||
|
@ -5,7 +5,7 @@ import { Component } from '@angular/core';
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
template: `
|
||||
<app-title [subtitle]="subtitle"></app-title>
|
||||
<app-title></app-title>
|
||||
<nav>
|
||||
<a routerLink="contact" routerLinkActive="active">Contact</a>
|
||||
<a routerLink="crisis" routerLinkActive="active">Crisis Center</a>
|
||||
@ -14,6 +14,4 @@ import { Component } from '@angular/core';
|
||||
<router-outlet></router-outlet>
|
||||
`
|
||||
})
|
||||
export class AppComponent {
|
||||
subtitle = '(Final)';
|
||||
}
|
||||
export class AppComponent {}
|
||||
|
@ -1,17 +1,7 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import
|
||||
// #enddocregion
|
||||
{ AppComponent } from './app.component.0';
|
||||
/*
|
||||
// #docregion
|
||||
{ AppComponent } from './app.component';
|
||||
// #enddocregion
|
||||
*/
|
||||
// #docregion
|
||||
import { AppComponent } from './app.component.0';
|
||||
|
||||
@NgModule({
|
||||
// #docregion imports
|
||||
|
@ -1,14 +1,15 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
/* Angular Imports */
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import
|
||||
/* App Imports */
|
||||
// #enddocregion
|
||||
{ AppComponent } from './app.component.1';
|
||||
import { AppComponent } from './app.component.1';
|
||||
/*
|
||||
// #docregion
|
||||
{ AppComponent } from './app.component';
|
||||
import { AppComponent } from './app.component';
|
||||
// #enddocregion
|
||||
*/
|
||||
// #docregion
|
||||
@ -21,12 +22,9 @@ import { FormsModule } from '@angular/forms';
|
||||
|
||||
import { AwesomePipe } from './contact/awesome.pipe';
|
||||
import { ContactComponent } from './contact/contact.component.3';
|
||||
|
||||
// #docregion import-contact-directive
|
||||
import {
|
||||
HighlightDirective as ContactHighlightDirective
|
||||
} from './contact/highlight.directive';
|
||||
// #enddocregion import-contact-directive
|
||||
ContactHighlightDirective as ContactHighlightDirective
|
||||
} from './contact/contact-highlight.directive';
|
||||
|
||||
@NgModule({
|
||||
// #docregion imports
|
||||
|
@ -1,15 +1,16 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
/* Angular Imports */
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
/* App Root */
|
||||
import
|
||||
/* App Imports */
|
||||
// #enddocregion
|
||||
{ AppComponent } from './app.component.1b';
|
||||
import { AppComponent } from './app.component.1b';
|
||||
/*
|
||||
// #docregion
|
||||
{ AppComponent } from './app.component';
|
||||
import { AppComponent } from './app.component';
|
||||
// #enddocregion
|
||||
*/
|
||||
// #docregion
|
||||
@ -18,25 +19,17 @@ import { TitleComponent } from './title.component';
|
||||
import { UserService } from './user.service';
|
||||
|
||||
/* Contact Imports */
|
||||
import
|
||||
// #enddocregion
|
||||
{ ContactComponent } from './contact/contact.component.3';
|
||||
import { ContactComponent } from './contact/contact.component.3';
|
||||
/*
|
||||
// #docregion
|
||||
{ ContactComponent } from './contact/contact.component';
|
||||
import { ContactComponent } from './contact/contact.component';
|
||||
// #enddocregion
|
||||
*/
|
||||
// #docregion
|
||||
import { ContactService } from './contact/contact.service';
|
||||
import { AwesomePipe } from './contact/awesome.pipe';
|
||||
|
||||
// #docregion import-alias
|
||||
import {
|
||||
HighlightDirective as ContactHighlightDirective
|
||||
} from './contact/highlight.directive';
|
||||
// #enddocregion import-alias
|
||||
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { AwesomePipe } from './contact/awesome.pipe';
|
||||
import { ContactService } from './contact/contact.service';
|
||||
import { ContactHighlightDirective } from './contact/contact-highlight.directive';
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule, FormsModule ],
|
||||
|
@ -1,15 +1,15 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
/* Angular Imports */
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
/* App Root */
|
||||
import
|
||||
/* App Imports */
|
||||
// #enddocregion
|
||||
{ AppComponent } from './app.component.2';
|
||||
import { AppComponent } from './app.component.2';
|
||||
/*
|
||||
// #docregion
|
||||
{ AppComponent } from './app.component';
|
||||
import { AppComponent } from './app.component';
|
||||
// #enddocregion
|
||||
*/
|
||||
// #docregion
|
||||
@ -18,12 +18,11 @@ import { TitleComponent } from './title.component';
|
||||
import { UserService } from './user.service';
|
||||
|
||||
/* Contact Imports */
|
||||
import
|
||||
// #enddocregion
|
||||
{ ContactModule } from './contact/contact.module.2';
|
||||
import { ContactModule } from './contact/contact.module.2';
|
||||
/*
|
||||
// #docregion
|
||||
{ ContactModule } from './contact/contact.module';
|
||||
import { ContactModule } from './contact/contact.module';
|
||||
// #enddocregion
|
||||
*/
|
||||
// #docregion
|
||||
|
@ -1,25 +1,36 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
/* Angular Imports */
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
/* App Root */
|
||||
/* App Imports */
|
||||
// #enddocregion
|
||||
import { AppComponent } from './app.component.3';
|
||||
/*
|
||||
// #docregion
|
||||
import { AppComponent } from './app.component';
|
||||
// #enddocregion
|
||||
*/
|
||||
// #docregion
|
||||
import { HighlightDirective } from './highlight.directive';
|
||||
import { TitleComponent } from './title.component';
|
||||
import { UserService } from './user.service';
|
||||
|
||||
/* Feature Modules */
|
||||
import { ContactModule } from './contact/contact.module.3';
|
||||
|
||||
/* Routing Module */
|
||||
// #enddocregion
|
||||
import { AppRoutingModule } from './app-routing.module.3';
|
||||
/*
|
||||
// #docregion
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
// #enddocregion
|
||||
*/
|
||||
// #docregion
|
||||
|
||||
@NgModule({
|
||||
// #docregion imports
|
||||
imports: [
|
||||
BrowserModule,
|
||||
ContactModule,
|
||||
AppRoutingModule
|
||||
],
|
||||
// #enddocregion imports
|
||||
|
@ -1,14 +1,14 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
// #docregion v4
|
||||
/* Angular Imports */
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
/* App Root */
|
||||
/* App Imports */
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
/* Feature Modules */
|
||||
import { ContactModule } from './contact/contact.module';
|
||||
/* Core Modules */
|
||||
import { CoreModule } from './core/core.module';
|
||||
|
||||
/* Routing Module */
|
||||
@ -18,7 +18,6 @@ import { AppRoutingModule } from './app-routing.module';
|
||||
// #docregion import-for-root
|
||||
imports: [
|
||||
BrowserModule,
|
||||
ContactModule,
|
||||
// #enddocregion v4
|
||||
// #enddocregion import-for-root
|
||||
/*
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* tslint:disable */
|
||||
// #docplaster
|
||||
// Same directive name and selector as
|
||||
// HighlightDirective in parent AppModule
|
||||
// It selects for both input boxes and 'highlight' attr
|
||||
@ -7,12 +7,14 @@
|
||||
// #docregion
|
||||
import { Directive, ElementRef } from '@angular/core';
|
||||
|
||||
// Highlight the host element or any InputElement in blue
|
||||
@Directive({ selector: '[highlight], input' })
|
||||
/** Highlight the attached element or an InputElement in blue */
|
||||
export class HighlightDirective {
|
||||
export class ContactHighlightDirective {
|
||||
constructor(el: ElementRef) {
|
||||
el.nativeElement.style.backgroundColor = 'powderblue';
|
||||
console.log(
|
||||
`* Contact highlight called for ${el.nativeElement.tagName}`);
|
||||
// #enddocregion
|
||||
console.log(`* Contact highlight called for ${el.nativeElement.tagName}`);
|
||||
// #docregion
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
@ -3,10 +3,12 @@ import { RouterModule } from '@angular/router';
|
||||
|
||||
import { ContactComponent } from './contact.component.3';
|
||||
|
||||
const routes = [
|
||||
{ path: 'contact', component: ContactComponent}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild([
|
||||
{ path: 'contact', component: ContactComponent}
|
||||
])],
|
||||
exports: [RouterModule]
|
||||
imports: [ RouterModule.forChild(routes) ],
|
||||
exports: [ RouterModule ]
|
||||
})
|
||||
export class ContactRoutingModule {}
|
||||
|
@ -4,11 +4,13 @@ import { RouterModule } from '@angular/router';
|
||||
import { ContactComponent } from './contact.component';
|
||||
|
||||
// #docregion routing
|
||||
const routes = [
|
||||
{ path: 'contact', component: ContactComponent}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild([
|
||||
{ path: 'contact', component: ContactComponent }
|
||||
])],
|
||||
exports: [RouterModule]
|
||||
imports: [ RouterModule.forChild(routes) ],
|
||||
exports: [ RouterModule ]
|
||||
})
|
||||
export class ContactRoutingModule {}
|
||||
// #enddocregion
|
||||
|
@ -21,7 +21,7 @@ export class ContactComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.contactService.getContacts().then(contacts => {
|
||||
this.contactService.getContacts().subscribe(contacts => {
|
||||
this.msg = '';
|
||||
this.contacts = contacts;
|
||||
this.contact = contacts[0];
|
||||
|
@ -27,3 +27,6 @@
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
@ -6,18 +6,32 @@
|
||||
<!-- #docregion awesome -->
|
||||
<h3 highlight>{{ contact.name | awesome }}</h3>
|
||||
<!-- #enddocregion awesome -->
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
|
||||
<!-- #docregion ngModel -->
|
||||
<input type="text" class="form-control" required
|
||||
[(ngModel)]="contact.name"
|
||||
name="name" #name="ngModel" >
|
||||
name="name" #name="ngModel" >
|
||||
<!-- #enddocregion ngModel -->
|
||||
|
||||
<div [hidden]="name.valid" class="alert alert-danger">
|
||||
Name is required
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<button type="submit" class="btn btn-default" [disabled]="!contactForm.form.valid">Save</button>
|
||||
<button type="button" class="btn" (click)="next()" [disabled]="!contactForm.form.valid">Next Contact</button>
|
||||
<button type="button" class="btn" (click)="newContact()">New Contact</button>
|
||||
|
||||
<div class="button-group">
|
||||
<button type="submit" class="btn btn-default"
|
||||
[disabled]="!contactForm.form.valid">
|
||||
Save</button>
|
||||
|
||||
<button type="button" class="btn" (click)="next()"
|
||||
[disabled]="!contactForm.form.valid">
|
||||
Next Contact</button>
|
||||
|
||||
<button type="button" class="btn" (click)="newContact()">
|
||||
New Contact</button>
|
||||
</div>
|
||||
</form>
|
||||
<!-- #enddocregion -->
|
||||
|
@ -22,7 +22,7 @@ export class ContactComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.contactService.getContacts().then(contacts => {
|
||||
this.contactService.getContacts().subscribe(contacts => {
|
||||
this.msg = '';
|
||||
this.contacts = contacts;
|
||||
this.contact = contacts[0];
|
||||
|
@ -0,0 +1,11 @@
|
||||
// #docregion
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule
|
||||
],
|
||||
declarations: []
|
||||
})
|
||||
export class ContactModule { }
|
@ -5,25 +5,32 @@ import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
import { AwesomePipe } from './awesome.pipe';
|
||||
|
||||
import
|
||||
// #enddocregion
|
||||
{ ContactComponent } from './contact.component.3';
|
||||
import { ContactComponent } from './contact.component.3';
|
||||
/*
|
||||
// #docregion
|
||||
{ ContactComponent } from './contact.component';
|
||||
import { ContactComponent } from './contact.component';
|
||||
// #enddocregion
|
||||
*/
|
||||
// #docregion
|
||||
import { ContactHighlightDirective } from './contact-highlight.directive';
|
||||
import { ContactService } from './contact.service';
|
||||
import { HighlightDirective } from './highlight.directive';
|
||||
|
||||
// #docregion class
|
||||
@NgModule({
|
||||
imports: [ CommonModule, FormsModule ],
|
||||
declarations: [ ContactComponent, HighlightDirective, AwesomePipe ],
|
||||
exports: [ ContactComponent ],
|
||||
providers: [ ContactService ]
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule
|
||||
],
|
||||
declarations: [
|
||||
AwesomePipe,
|
||||
ContactComponent,
|
||||
ContactHighlightDirective
|
||||
],
|
||||
// #docregion exports
|
||||
exports: [ ContactComponent ],
|
||||
// #enddocregion exports
|
||||
providers: [ ContactService ]
|
||||
})
|
||||
export class ContactModule { }
|
||||
// #enddocregion class
|
||||
|
@ -1,21 +1,43 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
import { AwesomePipe } from './awesome.pipe';
|
||||
|
||||
// #enddocregion
|
||||
import { ContactComponent } from './contact.component.3';
|
||||
/*
|
||||
// #docregion
|
||||
import { ContactComponent } from './contact.component';
|
||||
// #enddocregion
|
||||
*/
|
||||
// #docregion
|
||||
import { ContactHighlightDirective } from './contact-highlight.directive';
|
||||
import { ContactService } from './contact.service';
|
||||
import { HighlightDirective } from './highlight.directive';
|
||||
|
||||
// #enddocregion
|
||||
import { ContactRoutingModule } from './contact-routing.module.3';
|
||||
/*
|
||||
// #docregion
|
||||
import { ContactRoutingModule } from './contact-routing.module';
|
||||
// #enddocregion
|
||||
*/
|
||||
// #docregion
|
||||
|
||||
// #docregion class
|
||||
@NgModule({
|
||||
imports: [ CommonModule, FormsModule, ContactRoutingModule ],
|
||||
declarations: [ ContactComponent, HighlightDirective, AwesomePipe ],
|
||||
providers: [ ContactService ]
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ContactRoutingModule
|
||||
],
|
||||
declarations: [
|
||||
AwesomePipe,
|
||||
ContactComponent,
|
||||
ContactHighlightDirective
|
||||
],
|
||||
providers: [ ContactService ]
|
||||
})
|
||||
export class ContactModule { }
|
||||
// #enddocregion class
|
||||
|
@ -8,7 +8,10 @@ import { ContactRoutingModule } from './contact-routing.module';
|
||||
|
||||
// #docregion class
|
||||
@NgModule({
|
||||
imports: [ SharedModule, ContactRoutingModule ],
|
||||
imports: [
|
||||
SharedModule,
|
||||
ContactRoutingModule
|
||||
],
|
||||
declarations: [ ContactComponent ],
|
||||
providers: [ ContactService ]
|
||||
})
|
||||
|
@ -1,5 +1,10 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
import { delay } from 'rxjs/operators';
|
||||
|
||||
export class Contact {
|
||||
constructor(public id: number, public name: string) { }
|
||||
@ -13,17 +18,21 @@ const CONTACTS: Contact[] = [
|
||||
|
||||
const FETCH_LATENCY = 500;
|
||||
|
||||
/** Simulate a data service that retrieves contacts from a server */
|
||||
@Injectable()
|
||||
export class ContactService {
|
||||
export class ContactService implements OnDestroy {
|
||||
// #enddocregion
|
||||
constructor() { console.log('ContactService instance created.'); }
|
||||
ngOnDestroy() { console.log('ContactService instance destroyed.'); }
|
||||
|
||||
getContacts() {
|
||||
return new Promise<Contact[]>(resolve => {
|
||||
setTimeout(() => { resolve(CONTACTS); }, FETCH_LATENCY);
|
||||
});
|
||||
// #docregion
|
||||
getContacts(): Observable<Contact[]> {
|
||||
return of(CONTACTS).pipe(delay(FETCH_LATENCY));
|
||||
}
|
||||
|
||||
getContact(id: number | string) {
|
||||
return this.getContacts()
|
||||
.then(heroes => heroes.find(hero => hero.id === +id));
|
||||
getContact(id: number | string): Observable<Contact> {
|
||||
return of(CONTACTS.find(contact => contact.id === +id))
|
||||
.pipe(delay(FETCH_LATENCY));
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!-- Exact copy from earlier app.component.html -->
|
||||
<h1 highlight>{{title}} {{subtitle}}</h1>
|
||||
<h1 highlight>{{title}}</h1>
|
||||
<p *ngIf="user">
|
||||
<i>Welcome, {{user}}</i>
|
||||
<p>
|
||||
|
@ -7,7 +7,6 @@ import { UserService } from '../core/user.service';
|
||||
templateUrl: './title.component.html',
|
||||
})
|
||||
export class TitleComponent {
|
||||
@Input() subtitle = '';
|
||||
title = 'Angular Modules';
|
||||
user = '';
|
||||
|
||||
|
@ -1,22 +1,21 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { Crisis,
|
||||
CrisisService } from './crisis.service';
|
||||
CrisisService } from './crisis.service';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<h3 highlight>Crisis List</h3>
|
||||
<div *ngFor='let crisis of crisises | async'>
|
||||
<div *ngFor='let crisis of crises | async'>
|
||||
<a routerLink="{{'../' + crisis.id}}">{{crisis.id}} - {{crisis.name}}</a>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class CrisisListComponent implements OnInit {
|
||||
crisises: Promise<Crisis[]>;
|
||||
export class CrisisListComponent {
|
||||
crises: Observable<Crisis[]>;
|
||||
|
||||
constructor(private crisisService: CrisisService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.crisises = this.crisisService.getCrises();
|
||||
constructor(private crisisService: CrisisService) {
|
||||
this.crises = this.crisisService.getCrises();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
import { delay } from 'rxjs/operators';
|
||||
|
||||
export class Crisis {
|
||||
constructor(public id: number, public name: string) { }
|
||||
@ -13,18 +17,18 @@ const CRISES: Crisis[] = [
|
||||
|
||||
const FETCH_LATENCY = 500;
|
||||
|
||||
/** Simulate a data service that retrieves crises from a server */
|
||||
@Injectable()
|
||||
export class CrisisService {
|
||||
export class CrisisService implements OnDestroy {
|
||||
constructor() { console.log('CrisisService instance created.'); }
|
||||
ngOnDestroy() { console.log('CrisisService instance destroyed.'); }
|
||||
|
||||
getCrises() {
|
||||
return new Promise<Crisis[]>(resolve => {
|
||||
setTimeout(() => { resolve(CRISES); }, FETCH_LATENCY);
|
||||
});
|
||||
getCrises(): Observable<Crisis[]> {
|
||||
return of(CRISES).pipe(delay(FETCH_LATENCY));
|
||||
}
|
||||
|
||||
getCrisis(id: number | string) {
|
||||
return this.getCrises()
|
||||
.then(heroes => heroes.find(hero => hero.id === +id));
|
||||
getCrisis(id: number | string): Observable<Crisis> {
|
||||
return of(CRISES.find(crisis => crisis.id === +id))
|
||||
.pipe(delay(FETCH_LATENCY));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,6 +26,6 @@ export class HeroDetailComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
let id = parseInt(this.route.snapshot.paramMap.get('id'), 10);
|
||||
this.heroService.getHero(id).then(hero => this.hero = hero);
|
||||
this.heroService.getHero(id).subscribe(hero => this.hero = hero);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
|
||||
import { Hero,
|
||||
HeroService } from './hero.service';
|
||||
@ -11,11 +12,9 @@ import { Hero,
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class HeroListComponent implements OnInit {
|
||||
heroes: Promise<Hero[]>;
|
||||
constructor(private heroService: HeroService) { }
|
||||
|
||||
ngOnInit() {
|
||||
export class HeroListComponent {
|
||||
heroes: Observable<Hero[]>;
|
||||
constructor(private heroService: HeroService) {
|
||||
this.heroes = this.heroService.getHeroes();
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,10 @@ import { FormsModule } from '@angular/forms';
|
||||
import { HeroComponent } from './hero.component.3';
|
||||
import { HeroDetailComponent } from './hero-detail.component';
|
||||
import { HeroListComponent } from './hero-list.component';
|
||||
import { HighlightDirective } from './highlight.directive';
|
||||
import { HeroRoutingModule } from './hero-routing.module.3';
|
||||
|
||||
import { HighlightDirective } from './highlight.directive';
|
||||
|
||||
// #docregion class
|
||||
@NgModule({
|
||||
imports: [ CommonModule, FormsModule, HeroRoutingModule ],
|
||||
|
@ -1,4 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
import { delay } from 'rxjs/operators';
|
||||
|
||||
export class Hero {
|
||||
constructor(public id: number, public name: string) { }
|
||||
@ -15,18 +19,19 @@ const HEROES: Hero[] = [
|
||||
|
||||
const FETCH_LATENCY = 500;
|
||||
|
||||
/** Simulate a data service that retrieves heroes from a server */
|
||||
@Injectable()
|
||||
export class HeroService {
|
||||
export class HeroService implements OnDestroy {
|
||||
|
||||
getHeroes() {
|
||||
return new Promise<Hero[]>(resolve => {
|
||||
setTimeout(() => { resolve(HEROES); }, FETCH_LATENCY);
|
||||
});
|
||||
constructor() { console.log('HeroService instance created.'); }
|
||||
ngOnDestroy() { console.log('HeroService instance destroyed.'); }
|
||||
|
||||
getHeroes(): Observable<Hero[]> {
|
||||
return of(HEROES).pipe(delay(FETCH_LATENCY));
|
||||
}
|
||||
|
||||
getHero(id: number | string) {
|
||||
return this.getHeroes()
|
||||
.then(heroes => heroes.find(hero => hero.id === +id));
|
||||
getHero(id: number | string): Observable<Hero> {
|
||||
return of(HEROES.find(hero => hero.id === +id))
|
||||
.pipe(delay(FETCH_LATENCY));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Directive, ElementRef } from '@angular/core';
|
||||
|
||||
// Highlight the host element in gold
|
||||
@Directive({ selector: '[highlight]' })
|
||||
/** Highlight the attached element in gold */
|
||||
export class HighlightDirective {
|
||||
constructor(el: ElementRef) {
|
||||
el.nativeElement.style.backgroundColor = 'gold';
|
||||
console.log(
|
||||
`* AppRoot highlight called for ${el.nativeElement.tagName}`);
|
||||
// #enddocregion
|
||||
console.log(`* AppRoot highlight called for ${el.nativeElement.tagName}`);
|
||||
// #docregion
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
||||
|
@ -1,9 +1,8 @@
|
||||
/* tslint:disable */
|
||||
// Exact copy of contact/highlight.directive except for color and message
|
||||
import { Directive, ElementRef } from '@angular/core';
|
||||
|
||||
@Directive({ selector: '[highlight], input' })
|
||||
/** Highlight the attached element or an InputElement in gray */
|
||||
// Highlight the host element or any InputElement in gray
|
||||
export class HighlightDirective {
|
||||
constructor(el: ElementRef) {
|
||||
el.nativeElement.style.backgroundColor = 'lightgray';
|
||||
|
@ -1,6 +1,6 @@
|
||||
<!-- #docregion -->
|
||||
<!-- #docregion v1 -->
|
||||
<h1 highlight>{{title}} {{subtitle}}</h1>
|
||||
<h1 highlight>{{title}}</h1>
|
||||
<!-- #enddocregion v1 -->
|
||||
<!-- #docregion ngIf -->
|
||||
<p *ngIf="user">
|
||||
|
@ -1,18 +1,17 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
// #docregion v1
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
// #enddocregion v1
|
||||
import { UserService } from './user.service';
|
||||
// #docregion v1
|
||||
|
||||
@Component({
|
||||
selector: 'app-title',
|
||||
templateUrl: './title.component.html',
|
||||
templateUrl: './title.component.html'
|
||||
})
|
||||
export class TitleComponent {
|
||||
@Input() subtitle = '';
|
||||
title = 'NgModules';
|
||||
title = 'Angular Modules';
|
||||
// #enddocregion v1
|
||||
user = '';
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
<blockquote>
|
||||
<!-- #docregion built-in, asterisk, ngif -->
|
||||
<div *ngIf="hero" >{{hero.name}}</div>
|
||||
<div *ngIf="hero" class="name">{{hero.name}}</div>
|
||||
<!-- #enddocregion built-in, asterisk, ngif -->
|
||||
</blockquote>
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
<p><ng-template> element</p>
|
||||
<!-- #docregion ngif-template -->
|
||||
<ng-template [ngIf]="hero">
|
||||
<div>{{hero.name}}</div>
|
||||
<div class="name">{{hero.name}}</div>
|
||||
</ng-template>
|
||||
<!-- #enddocregion ngif-template -->
|
||||
|
||||
|
@ -9,6 +9,6 @@ export class MessageService {
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.messages.length = 0;
|
||||
this.messages = [];
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,6 @@ export class MessageService {
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.messages.length = 0;
|
||||
this.messages = [];
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,6 @@ export class MessageService {
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.messages.length = 0;
|
||||
this.messages = [];
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,6 @@ export class MessageService {
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.messages.length = 0;
|
||||
this.messages = [];
|
||||
}
|
||||
}
|
||||
|
@ -409,7 +409,7 @@ function; it can only contain a single `return` statement.
|
||||
|
||||
The Angular [`RouterModule`](api/router/RouterModule) exports two macro static methods, `forRoot` and `forChild`, to help declare root and child routes.
|
||||
Review the [source code](https://github.com/angular/angular/blob/master/packages/router/src/router_module.ts#L139 "RouterModule.forRoot source code")
|
||||
for these methods to see how macros can simplify configuration of complex Angular modules.
|
||||
for these methods to see how macros can simplify configuration of complex [NgModules](guide/ngmodule).
|
||||
|
||||
### Metadata rewriting
|
||||
|
||||
|
@ -160,7 +160,7 @@ Providing the `UserService` with an Angular module is a good choice.
|
||||
<div class="l-sub-section">
|
||||
|
||||
To be precise, Angular module providers are registered with the root injector
|
||||
_unless the module is_ [lazy loaded](guide/ngmodule#lazy-load).
|
||||
_unless the module is_ [lazy loaded](guide/ngmodule#lazy-load-DI).
|
||||
In this sample, all modules are _eagerly loaded_ when the application starts,
|
||||
so all module providers are registered with the app's root injector.
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -74,8 +74,8 @@ Generate a new project and skeleton application by running the following command
|
||||
|
||||
|
||||
|
||||
Patience please.
|
||||
It takes time to set up a new project, most of it spent installing npm packages.
|
||||
Patience, please.
|
||||
It takes time to set up a new project; most of it is spent installing npm packages.
|
||||
|
||||
|
||||
</div>
|
||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 79 KiB |
BIN
aio/content/images/marketing/home/ng-atl.png
Normal file
BIN
aio/content/images/marketing/home/ng-atl.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
@ -32,9 +32,9 @@
|
||||
<!-- Announcement Bar -->
|
||||
<div class="homepage-container">
|
||||
<div class="announcement-bar">
|
||||
<img src="generated/images/marketing/home/angular-connect.png">
|
||||
<p>Join us in London for AngularConnect<br>November 7-8, 2017</p>
|
||||
<a class="button" href="https://angularconnect.com/">Learn More</a>
|
||||
<img src="generated/images/marketing/home/ng-atl.png">
|
||||
<p>Join us in Atlanta for ngATL<br>Jan 30 - Feb 2, 2018</p>
|
||||
<a class="button" href="http://ng-atl.org/">Learn More</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -241,6 +241,12 @@
|
||||
"UI Components": {
|
||||
"order": 4,
|
||||
"resources": {
|
||||
"IgniteUIforAngular": {
|
||||
"desc": "Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps.",
|
||||
"rev": true,
|
||||
"title": "Ignite UI for Angular",
|
||||
"url": "https://www.infragistics.com/products/ignite-ui-angular"
|
||||
},
|
||||
"DevExtreme": {
|
||||
"desc": "50+ UI components including data grid, pivot grid, scheduler, charts, editors, maps and other multi-purpose controls for creating highly responsive web applications for touch devices and traditional desktops.",
|
||||
"rev": true,
|
||||
@ -534,6 +540,12 @@
|
||||
"rev": true,
|
||||
"title": "Ultimate Angular",
|
||||
"url": "https://ultimateangular.com/"
|
||||
},
|
||||
"angular-firebase": {
|
||||
"desc": "Video lessons covering progressive web apps with Angular, Firebase, RxJS, and related APIs.",
|
||||
"rev": true,
|
||||
"title": "AngularFirebase.com",
|
||||
"url": "https://angularfirebase.com/"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -621,6 +633,12 @@
|
||||
"rev": true,
|
||||
"title": "SFEIR School (French)",
|
||||
"url": "https://school.sfeir.com/project/sa200/"
|
||||
},
|
||||
"formationjs": {
|
||||
"desc": "Angular onsite training in Paris (France). Monthly Angular workshops and custom onsite classes. We are focused on Angular, so we are always up to date.",
|
||||
"rev": true,
|
||||
"title": "Formation JavaScript (French)",
|
||||
"url": "https://formationjavascript.com/formation-angular/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -225,17 +225,17 @@
|
||||
|
||||
{
|
||||
"title": "NgModules",
|
||||
"tooltip": "Learn how to use NgModules to make your apps efficient.",
|
||||
"tooltip": "Modularize your app with NgModules.",
|
||||
"children": [
|
||||
{
|
||||
"url": "guide/ngmodule",
|
||||
"title": "NgModules",
|
||||
"tooltip": "Define application modules with @NgModule."
|
||||
"tooltip": "Define application modules with the NgModule."
|
||||
},
|
||||
{
|
||||
"url": "guide/ngmodule-faq",
|
||||
"title": "NgModule FAQs",
|
||||
"tooltip": "Answers to frequently asked questions about @NgModule."
|
||||
"tooltip": "Answers to frequently asked questions about NgModules."
|
||||
}
|
||||
]},
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
The Tour of Heroes `HeroesComponent` is currently getting and displaying fake data.
|
||||
|
||||
After the refactoring in this tutorial, `HeroesComponent` will be lean and focused on supporting the view.
|
||||
It will also be easier to unit-test with a mock services.
|
||||
It will also be easier to unit-test with a mock service.
|
||||
|
||||
## Why services
|
||||
|
||||
|
@ -77,7 +77,7 @@ Then define an array of routes with a single `route` to that component.
|
||||
</code-example>
|
||||
|
||||
Once you've finished setting up, the router will match that URL to `path: 'heroes'`
|
||||
and display the `HeroesComponent`, .
|
||||
and display the `HeroesComponent`.
|
||||
|
||||
### _RouterModule.forRoot()_
|
||||
|
||||
|
@ -28,7 +28,7 @@ describe('Api pages', function() {
|
||||
|
||||
it('should show readonly properties as getters', () => {
|
||||
const page = new ApiPage('api/common/http/HttpRequest');
|
||||
expect(page.getOverview('class').getText()).toContain('get body: T|null');
|
||||
expect(page.getOverview('class').getText()).toContain('get body: T | null');
|
||||
});
|
||||
|
||||
it('should not show parenthesis for getters', () => {
|
||||
|
@ -80,8 +80,6 @@ describe('site App', function() {
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(https://github.com/angular/angular/issues/19785): Activate this again
|
||||
// once it is no more flaky.
|
||||
describe('google analytics', () => {
|
||||
|
||||
it('should call ga with initial URL', done => {
|
||||
|
@ -29,10 +29,7 @@ export class SitePage {
|
||||
locationPath() { return browser.executeScript('return document.location.pathname') as promise.Promise<string>; }
|
||||
|
||||
navigateTo(pageUrl = '') {
|
||||
return browser.get('/' + pageUrl)
|
||||
// We need to tell the index.html not to load the real analytics library
|
||||
// See the GA snippet in index.html
|
||||
.then(() => browser.executeScript('sessionStorage.setItem("__e2e__", true);'));
|
||||
return browser.get('/' + pageUrl);
|
||||
}
|
||||
|
||||
getDocViewerText() {
|
||||
|
@ -98,8 +98,9 @@
|
||||
"codelyzer": "~2.0.0",
|
||||
"concurrently": "^3.4.0",
|
||||
"cross-spawn": "^5.1.0",
|
||||
"css-selector-parser": "^1.3.0",
|
||||
"dgeni": "^0.4.7",
|
||||
"dgeni-packages": "0.22.0",
|
||||
"dgeni-packages": "0.22.1",
|
||||
"entities": "^1.1.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-plugin-jasmine": "^2.2.0",
|
||||
|
3
aio/scripts/_payload-limits.json
Executable file
3
aio/scripts/_payload-limits.json
Executable file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"aio":{"master":{"change":"application","gzip7":{"inline":925,"main":119519,"polyfills":11863},"gzip9":{"inline":925,"main":119301,"polyfills":11861},"uncompressed":{"inline":1533,"main":486493,"polyfills":37068}}}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -u -e -o pipefail
|
||||
|
||||
declare -A payloadLimits
|
||||
payloadLimits["aio", "uncompressed", "inline"]=1600
|
||||
payloadLimits["aio", "uncompressed", "main"]=487000
|
||||
payloadLimits["aio", "uncompressed", "polyfills"]=38000
|
||||
payloadLimits["aio", "gzip7", "inline"]=1000
|
||||
payloadLimits["aio", "gzip7", "main"]=120000
|
||||
payloadLimits["aio", "gzip7", "polyfills"]=11900
|
||||
payloadLimits["aio", "gzip9", "inline"]=1000
|
||||
payloadLimits["aio", "gzip9", "main"]=120000
|
||||
payloadLimits["aio", "gzip9", "polyfills"]=11900
|
@ -7,7 +7,6 @@ readonly parentDir=$(dirname $thisDir)
|
||||
|
||||
# Track payload size functions
|
||||
source ../scripts/ci/payload-size.sh
|
||||
source ${thisDir}/_payload-limits.sh
|
||||
|
||||
trackPayloadSize "aio" "dist/*.bundle.js" true true
|
||||
trackPayloadSize "aio" "dist/*.bundle.js" true true "${thisDir}/_payload-limits.json"
|
||||
|
||||
|
@ -34,11 +34,13 @@
|
||||
|
||||
<!-- Google Analytics -->
|
||||
<script>
|
||||
// Note this is a customised version of the GA tracking snippet to aid e2e testing
|
||||
// See the bit between /**/.../**/
|
||||
// Note this is a customised version of the GA tracking snippet
|
||||
// See the comments below for more info
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;/**/i.sessionStorage.__e2e__||/**/m.parentNode.insertBefore(a,m)
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;
|
||||
~i.name.indexOf('NG_DEFER_BOOTSTRAP')|| // only load library if not running e2e tests
|
||||
m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
</script>
|
||||
<!-- End Google Analytics -->
|
||||
|
@ -62,7 +62,7 @@ class ExampleZipper {
|
||||
let exampleZipName;
|
||||
const exampleType = this._getExampleType(path.join(sourceDirName, relativeDirName));
|
||||
if (relativeDirName.indexOf('/') !== -1) { // Special example
|
||||
exampleZipName = relativeDirName.split('/')[0];
|
||||
exampleZipName = relativeDirName.split('/').join('-');
|
||||
} else {
|
||||
exampleZipName = jsonFileName.replace(/(plnkr|zipper).json/, relativeDirName);
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ This tool expects all the examples to be build with `npm run build`. If an examp
|
||||
with that script, the author would need to specify the new build command in the `example-config.json`
|
||||
as shown earlier.
|
||||
|
||||
### add-example-boilerplate.js
|
||||
### example-boilerplate.js
|
||||
|
||||
This script installs all the dependencies that are shared among all the examples, creates the
|
||||
`node_modules` symlinks and copy all the boilerplate files where needed. It won't do anything
|
||||
@ -102,7 +102,7 @@ This script will find all the `e2e-spec.ts` files and run them.
|
||||
To not run all tests, you can use the `--filter=name` flag to run the example's e2e that contains
|
||||
that name.
|
||||
|
||||
It also has an optional `--setup` flag to run the `add-example-boilerplate.js` script and install
|
||||
It also has an optional `--setup` flag to run the `example-boilerplate.js` script and install
|
||||
the latest `webdriver`.
|
||||
|
||||
It will create a `/aio/protractor-results-txt` file when it finishes running tests.
|
||||
|
@ -26,13 +26,13 @@
|
||||
"zone.js": "^0.8.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cli": "1.5.0",
|
||||
"@angular/cli": "1.5.4",
|
||||
"@angular/compiler-cli": "^5.0.0",
|
||||
"@angular/language-service": "^5.0.0",
|
||||
"@types/jasmine": "~2.5.53",
|
||||
"@types/jasminewd2": "~2.0.2",
|
||||
"@types/node": "~6.0.60",
|
||||
"codelyzer": "~3.2.0",
|
||||
"codelyzer": "^4.0.1",
|
||||
"jasmine-core": "~2.6.2",
|
||||
"jasmine-spec-reporter": "~4.1.0",
|
||||
"karma": "~1.7.0",
|
||||
|
@ -1,3 +1,5 @@
|
||||
const CssSelectorParser = require('css-selector-parser').CssSelectorParser;
|
||||
const cssParser = new CssSelectorParser();
|
||||
/**
|
||||
* @dgProcessor addMetadataAliases
|
||||
*
|
||||
@ -28,11 +30,18 @@ module.exports = function addMetadataAliasesProcessor() {
|
||||
};
|
||||
|
||||
function extractSelectors(selectors) {
|
||||
if (selectors) {
|
||||
return stripQuotes(selectors).split(',').map(selector => selector.replace(/^\W*([\w-]+)\W*$/, '$1'));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
const selectorAST = cssParser.parse(stripQuotes(selectors));
|
||||
const rules = selectorAST.selectors ? selectorAST.selectors.map(ruleSet => ruleSet.rule) : [selectorAST.rule];
|
||||
const aliases = {};
|
||||
rules.forEach(rule => {
|
||||
if (rule.tagName) {
|
||||
aliases[rule.tagName] = true;
|
||||
}
|
||||
if (rule.attrs) {
|
||||
rule.attrs.forEach(attr => aliases[attr.name] = true);
|
||||
}
|
||||
});
|
||||
return Object.keys(aliases);
|
||||
}
|
||||
|
||||
function stripQuotes(value) {
|
||||
|
@ -45,8 +45,8 @@ describe('addSelectorsAsAliases processor', () => {
|
||||
expect(docs[2].aliases).toEqual([docs[2].name]);
|
||||
expect(docs[3].aliases).toEqual([docs[3].name]);
|
||||
expect(docs[4].aliases).toEqual([docs[4].name, 'myPipe']);
|
||||
expect(docs[5].aliases).toEqual([docs[5].name, 'my-directive', 'myDirective', 'my-directive']);
|
||||
expect(docs[6].aliases).toEqual([docs[6].name, '[ngModel]:not([formControlName]):not([formControl])']);
|
||||
expect(docs[5].aliases).toEqual([docs[5].name, 'my-directive', 'myDirective']);
|
||||
expect(docs[6].aliases).toEqual([docs[6].name, 'ngModel']);
|
||||
expect(docs[7].aliases).toEqual([docs[7].name, 'my-component']);
|
||||
expect(docs[8].aliases).toEqual([docs[8].name]);
|
||||
expect(docs[9].aliases).toEqual([docs[9].name]);
|
||||
|
@ -35,6 +35,8 @@ module.exports = new Package('angular-base', [
|
||||
.factory('packageInfo', function() { return require(path.resolve(PROJECT_ROOT, 'package.json')); })
|
||||
.factory(require('./readers/json'))
|
||||
.factory(require('./services/copyFolder'))
|
||||
.factory(require('./services/filterPipes'))
|
||||
.factory(require('./services/filterAmbiguousDirectiveAliases'))
|
||||
.factory(require('./services/getImageDimensions'))
|
||||
|
||||
.factory(require('./post-processors/add-image-dimensions'))
|
||||
@ -126,8 +128,9 @@ module.exports = new Package('angular-base', [
|
||||
})
|
||||
|
||||
|
||||
.config(function(postProcessHtml, addImageDimensions, autoLinkCode) {
|
||||
.config(function(postProcessHtml, addImageDimensions, autoLinkCode, filterPipes, filterAmbiguousDirectiveAliases) {
|
||||
addImageDimensions.basePath = path.resolve(AIO_PATH, 'src');
|
||||
autoLinkCode.customFilters = [filterPipes, filterAmbiguousDirectiveAliases];
|
||||
postProcessHtml.plugins = [
|
||||
require('./post-processors/autolink-headings'),
|
||||
addImageDimensions,
|
||||
|
@ -10,12 +10,19 @@ const textContent = require('hast-util-to-string');
|
||||
* Only docs that have one of these docTypes will be linked to.
|
||||
* Usually set to the API exported docTypes, e.g. "class", "function", "directive", etc.
|
||||
*
|
||||
* @property customFilters array of functions `(docs, words, wordIndex) => docs` that will filter
|
||||
* out docs where a word should not link to a doc.
|
||||
* - `docs` is the array of docs that match the link `word`
|
||||
* - `words` is the collection of words parsed from the code text
|
||||
* - `wordIndex` is the index of the current `word` for which we are finding a link
|
||||
*
|
||||
* @property codeElements an array of strings.
|
||||
* Only text contained in these elements will be linked to.
|
||||
* Usually set to "code" but also "code-example" for angular.io.
|
||||
*/
|
||||
module.exports = function autoLinkCode(getDocFromAlias) {
|
||||
autoLinkCodeImpl.docTypes = [];
|
||||
autoLinkCodeImpl.customFilters = [];
|
||||
autoLinkCodeImpl.codeElements = ['code'];
|
||||
return autoLinkCodeImpl;
|
||||
|
||||
@ -38,12 +45,13 @@ module.exports = function autoLinkCode(getDocFromAlias) {
|
||||
parent.children.splice(index, 1, createLinkNode(docs[0], node.value));
|
||||
} else {
|
||||
// Parse the text for words that we can convert to links
|
||||
const nodes = textContent(node).split(/([A-Za-z0-9_]+)/)
|
||||
const nodes = textContent(node).split(/([A-Za-z0-9_-]+)/)
|
||||
.filter(word => word.length)
|
||||
.map(word => {
|
||||
const docs = getDocFromAlias(word);
|
||||
return foundValidDoc(docs) ?
|
||||
createLinkNode(docs[0], word) : // Create a link wrapping the text node.
|
||||
.map((word, index, words) => {
|
||||
// remove docs that fail the custom filter tests
|
||||
const filteredDocs = autoLinkCodeImpl.customFilters.reduce((docs, filter) => filter(docs, words, index), getDocFromAlias(word));
|
||||
return foundValidDoc(filteredDocs) ?
|
||||
createLinkNode(filteredDocs[0], word) : // Create a link wrapping the text node.
|
||||
{ type: 'text', value: word }; // this is just text so push a new text node
|
||||
});
|
||||
|
||||
|
@ -2,7 +2,7 @@ var createTestPackage = require('../../helpers/test-package');
|
||||
var Dgeni = require('dgeni');
|
||||
|
||||
describe('autoLinkCode post-processor', () => {
|
||||
let processor, autoLinkCode, aliasMap;
|
||||
let processor, autoLinkCode, aliasMap, filterPipes;
|
||||
|
||||
beforeEach(() => {
|
||||
const testPackage = createTestPackage('angular-base-package');
|
||||
@ -14,6 +14,7 @@ describe('autoLinkCode post-processor', () => {
|
||||
processor = injector.get('postProcessHtml');
|
||||
processor.docTypes = ['test-doc'];
|
||||
processor.plugins = [autoLinkCode];
|
||||
filterPipes = injector.get('filterPipes');
|
||||
});
|
||||
|
||||
it('should insert an anchor into every code item that matches the id of an API doc', () => {
|
||||
@ -51,6 +52,26 @@ describe('autoLinkCode post-processor', () => {
|
||||
expect(doc.renderedContent).toEqual('<code>MyClass</code>');
|
||||
});
|
||||
|
||||
it('should ignore code items that match an API doc but are attached to other text via a dash', () => {
|
||||
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
|
||||
const doc = { docType: 'test-doc', renderedContent: '<code>xyz-MyClass</code>' };
|
||||
processor.$process([doc]);
|
||||
expect(doc.renderedContent).toEqual('<code>xyz-MyClass</code>');
|
||||
});
|
||||
|
||||
it('should ignore code items that are filtered out by custom filters', () => {
|
||||
autoLinkCode.customFilters = [filterPipes];
|
||||
aliasMap.addDoc({ docType: 'pipe', id: 'MyClass', aliases: ['MyClass', 'myClass'], path: 'a/b/myclass', pipeOptions: { name: '\'myClass\'' } });
|
||||
const doc = { docType: 'test-doc', renderedContent: '<code>{ xyz | myClass } { xyz|myClass } MyClass myClass OtherClass|MyClass</code>' };
|
||||
processor.$process([doc]);
|
||||
expect(doc.renderedContent).toEqual('<code>' +
|
||||
'{ xyz | <a href="a/b/myclass" class="code-anchor">myClass</a> } ' +
|
||||
'{ xyz|<a href="a/b/myclass" class="code-anchor">myClass</a> } ' +
|
||||
'<a href="a/b/myclass" class="code-anchor">MyClass</a> ' +
|
||||
'myClass OtherClass|<a href="a/b/myclass" class="code-anchor">MyClass</a>' +
|
||||
'</code>');
|
||||
});
|
||||
|
||||
it('should insert anchors for individual text nodes within a code block', () => {
|
||||
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
|
||||
const doc = { docType: 'test-doc', renderedContent: '<code><span>MyClass</span><span>MyClass</span></code>' };
|
||||
|
26
aio/tools/transforms/angular-base-package/services/filterAmbiguousDirectiveAliases.js
vendored
Normal file
26
aio/tools/transforms/angular-base-package/services/filterAmbiguousDirectiveAliases.js
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
/**
|
||||
* This service is used by the autoLinkCode post-processor to filter out ambiguous directive
|
||||
* docs where the matching word is a directive selector.
|
||||
* E.g. `ngModel`, which is a selector for a number of directives, where we are only really
|
||||
* interested in the `NgModel` class.
|
||||
*/
|
||||
module.exports = function filterAmbiguousDirectiveAliases() {
|
||||
return (docs, words, index) => {
|
||||
const word = words[index];
|
||||
|
||||
// we are only interested if there are multiple matching docs
|
||||
if (docs.length > 1) {
|
||||
if (docs.every(doc =>
|
||||
// We are only interested if they are all either directives or components
|
||||
(doc.docType === 'directive' || doc.docType === 'component') &&
|
||||
// and the matching word is in the selector for all of them
|
||||
doc[doc.docType + 'Options'].selector.indexOf(word) != -1
|
||||
)) {
|
||||
// find the directive whose class name matches the word (case-insensitive)
|
||||
return docs.filter(doc => doc.name.toLowerCase() === word.toLowerCase());
|
||||
}
|
||||
}
|
||||
return docs;
|
||||
};
|
||||
};
|
@ -0,0 +1,50 @@
|
||||
const filterAmbiguousDirectiveAliases = require('./filterAmbiguousDirectiveAliases')();
|
||||
|
||||
const words = ['Http', 'ngModel', 'NgModel', 'NgControlStatus'];
|
||||
|
||||
describe('filterAmbiguousDirectiveAliases(docs, words, index)', () => {
|
||||
it('should not try to filter the docs, if the docs are not all directives or components', () => {
|
||||
const docs = [
|
||||
{ docType: 'class', name: 'Http' },
|
||||
{ docType: 'directive', name: 'NgModel', directiveOptions: { selector: '[ngModel]' } },
|
||||
{ docType: 'component', name: 'NgModel', componentOptions: { selector: '[ngModel]' } }
|
||||
];
|
||||
// take a copy to prove `docs` was not modified
|
||||
const filteredDocs = docs.slice(0);
|
||||
expect(filterAmbiguousDirectiveAliases(docs, words, 1)).toEqual(filteredDocs);
|
||||
expect(filterAmbiguousDirectiveAliases(docs, words, 2)).toEqual(filteredDocs);
|
||||
});
|
||||
|
||||
describe('(where all the docs are components or directives', () => {
|
||||
describe('and do not all contain the matching word in their selector)', () => {
|
||||
it('should not try to filter the docs', () => {
|
||||
const docs = [
|
||||
{ docType: 'directive', name: 'NgModel', ['directiveOptions']: { selector: '[ngModel]' } },
|
||||
{ docType: 'component', name: 'NgControlStatus', ['componentOptions']: { selector: '[ngControlStatus]' } }
|
||||
];
|
||||
// take a copy to prove `docs` was not modified
|
||||
const filteredDocs = docs.slice(0);
|
||||
expect(filterAmbiguousDirectiveAliases(docs, words, 1)).toEqual(filteredDocs);
|
||||
expect(filterAmbiguousDirectiveAliases(docs, words, 2)).toEqual(filteredDocs);
|
||||
|
||||
// Also test that the check is case-sensitive
|
||||
docs[1].componentOptions.selector = '[ngModel]';
|
||||
filteredDocs[1].componentOptions.selector = '[ngModel]';
|
||||
expect(filterAmbiguousDirectiveAliases(docs, words, 2)).toEqual(filteredDocs);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and do all contain the matching word in there selector)', () => {
|
||||
it('should filter out docs whose class name is not (case-insensitively) equal to the matching word', () => {
|
||||
const docs = [
|
||||
{ docType: 'directive', name: 'NgModel', ['directiveOptions']: { selector: '[ngModel],[ngControlStatus]' } },
|
||||
{ docType: 'component', name: 'NgControlStatus', ['componentOptions']: { selector: '[ngModel],[ngControlStatus]' } }
|
||||
];
|
||||
const filteredDocs = [
|
||||
{ docType: 'directive', name: 'NgModel', ['directiveOptions']: { selector: '[ngModel],[ngControlStatus]' } }
|
||||
];
|
||||
expect(filterAmbiguousDirectiveAliases(docs, words, 1)).toEqual(filteredDocs);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
12
aio/tools/transforms/angular-base-package/services/filterPipes.js
vendored
Normal file
12
aio/tools/transforms/angular-base-package/services/filterPipes.js
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
/**
|
||||
* This service is used by the autoLinkCode post-processors to filter out pipe docs
|
||||
* where the matching word is the pipe name and is not preceded by a pipe
|
||||
*/
|
||||
module.exports = function filterPipes() {
|
||||
return (docs, words, index) =>
|
||||
docs.filter(doc =>
|
||||
doc.docType !== 'pipe' ||
|
||||
doc.pipeOptions.name !== '\'' + words[index] + '\'' ||
|
||||
index > 0 && words[index - 1].trim() === '|');
|
||||
};
|
@ -0,0 +1,36 @@
|
||||
const filterPipes = require('./filterPipes')();
|
||||
|
||||
describe('filterPipes', () => {
|
||||
it('should ignore docs that are not pipes', () => {
|
||||
const docs = [{ docType: 'class', name: 'B', pipeOptions: { name: '\'b\'' } }];
|
||||
const words = ['A', 'b', 'B', 'C'];
|
||||
const filteredDocs = [{ docType: 'class', name: 'B', pipeOptions: { name: '\'b\'' } }];
|
||||
expect(filterPipes(docs, words, 1)).toEqual(filteredDocs);
|
||||
expect(filterPipes(docs, words, 2)).toEqual(filteredDocs);
|
||||
});
|
||||
|
||||
it('should ignore docs that are pipes but do not match the pipe name', () => {
|
||||
const docs = [{ docType: 'pipe', name: 'B', pipeOptions: { name: '\'b\'' } }];
|
||||
const words = ['A', 'B', 'C'];
|
||||
const filteredDocs = [{ docType: 'pipe', name: 'B', pipeOptions: { name: '\'b\'' } }];
|
||||
expect(filterPipes(docs, words, 1)).toEqual(filteredDocs);
|
||||
});
|
||||
|
||||
it('should ignore docs that are pipes, match the pipe name and are preceded by a pipe character', () => {
|
||||
const docs = [{ docType: 'pipe', name: 'B', pipeOptions: { name: '\'b\'' } }];
|
||||
const words = ['A', '|', 'b', 'C'];
|
||||
const filteredDocs = [{ docType: 'pipe', name: 'B', pipeOptions: { name: '\'b\'' } }];
|
||||
expect(filterPipes(docs, words, 2)).toEqual(filteredDocs);
|
||||
});
|
||||
|
||||
it('should filter out docs that are pipes, match the pipe name but are not preceded by a pipe character', () => {
|
||||
const docs = [
|
||||
{ docType: 'pipe', name: 'B', pipeOptions: { name: '\'b\'' } },
|
||||
{ docType: 'class', name: 'B' }
|
||||
];
|
||||
const words = ['A', 'b', 'C'];
|
||||
const index = 1;
|
||||
const filteredDocs = [{ docType: 'class', name: 'B' }];
|
||||
expect(filterPipes(docs, words, index)).toEqual(filteredDocs);
|
||||
});
|
||||
});
|
@ -2007,6 +2007,10 @@ css-select@^1.1.0:
|
||||
domutils "1.5.1"
|
||||
nth-check "~1.0.1"
|
||||
|
||||
css-selector-parser@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.3.0.tgz#5f1ad43e2d8eefbfdc304fcd39a521664943e3eb"
|
||||
|
||||
css-selector-tokenizer@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86"
|
||||
@ -2283,9 +2287,9 @@ devtools-timeline-model@1.1.6:
|
||||
chrome-devtools-frontend "1.0.401423"
|
||||
resolve "1.1.7"
|
||||
|
||||
dgeni-packages@0.22.0:
|
||||
version "0.22.0"
|
||||
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.22.0.tgz#7ed07af9074f6547847256c1a65b488a5a17ad03"
|
||||
dgeni-packages@0.22.1:
|
||||
version "0.22.1"
|
||||
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.22.1.tgz#c4587a765689c4c9d48ed661517ed2249403bfb2"
|
||||
dependencies:
|
||||
canonical-path "0.0.2"
|
||||
catharsis "^0.8.1"
|
||||
|
@ -1,49 +1,52 @@
|
||||
# Triage Process and GitHub Labels for Angular
|
||||
|
||||
This document describes how the Angular team uses labels and milestones
|
||||
to triage issues on github. The basic idea of the process is that
|
||||
caretaker only assigns a component and type (bug, feature) label. The
|
||||
owner of the component than is in full control of how the issues should
|
||||
be triaged further.
|
||||
This document describes how the Angular team uses labels and milestones to triage issues on github.
|
||||
The basic idea of the process is that caretaker only assigns a component (`comp: *`) label.
|
||||
The owner of the component is then responsible for the secondary / component-level triage.
|
||||
|
||||
Once this process is implemented and in use, we will revisit it to see
|
||||
if further labeling is needed.
|
||||
|
||||
## Label Types
|
||||
|
||||
### Components
|
||||
|
||||
A caretaker should be able to determine which component the issue
|
||||
belongs to. The components have a clear piece of source code associated
|
||||
with it within the `/packages/` folder of this repo.
|
||||
The caretaker should be able to determine which component the issue belongs to.
|
||||
The components have a clear piece of source code associated with it within the `/packages/` folder of this repo.
|
||||
|
||||
* `comp: aio` - the angular.io application
|
||||
* `comp: animations`
|
||||
* `comp: bazel`
|
||||
* `comp: bazel` - @angular/bazel rules
|
||||
* `comp: benchpress`
|
||||
* `comp: common` - this includes core components / pipes
|
||||
* `comp: core, compiler` - because core, compiler, compiler-cli and
|
||||
* `comp: common/http` - this includes core components / pipes
|
||||
* `comp: core & compiler` - because core, compiler, compiler-cli and
|
||||
browser-platforms are very intertwined, we will be treating them as one
|
||||
* `comp: forms`
|
||||
* `comp: http`
|
||||
* `comp: i18n`
|
||||
* `comp: language service`
|
||||
* `comp: language-service`
|
||||
* `comp: metadata-extractor`
|
||||
* `comp: router`
|
||||
* `comp: server`
|
||||
* `comp: service-worker`
|
||||
* `comp: testing`
|
||||
* `comp: upgrade`
|
||||
* `comp: upgrade/dynamic`
|
||||
* `comp: upgrade/static`
|
||||
* `comp: web-worker`
|
||||
* `comp: zones`
|
||||
|
||||
There are few components which are cross-cutting. They don't have
|
||||
a clear location in the source tree. We will treat them as a component
|
||||
even thought no specific source tree is associated with them.
|
||||
There are few components which are cross-cutting.
|
||||
They don't have a clear location in the source tree.
|
||||
We will treat them as a component even thought no specific source tree is associated with them.
|
||||
|
||||
* `comp: build & ci` - all build and CI scripts
|
||||
* `comp: build & ci` - build and CI infrastructure for the angular/angular repo
|
||||
* `comp: docs` - documentation, including API docs, guides, tutorial
|
||||
* `comp: packaging`
|
||||
* `comp: packaging` - packaging format of @angular/* npm packages
|
||||
* `comp: performance`
|
||||
* `comp: security`
|
||||
|
||||
Sometimes, especially in the case of cross-cutting issues or PRs, these PRs or issues belong to multiple components.
|
||||
In these cases, all applicable component labels should be used to triage the issue or PR.
|
||||
|
||||
|
||||
### Type
|
||||
|
||||
@ -56,27 +59,27 @@ What kind of problem is this?
|
||||
* `type: performance`
|
||||
* `type: refactor`
|
||||
|
||||
## Caretaker Triage Process
|
||||
|
||||
It is the caretaker's responsibility to assign `comp: *` to each new
|
||||
issue as they come in. The reason why we limit the responsibility of the
|
||||
caretaker to this one label is that it is likely that without domain
|
||||
knowledge the caretaker could mislabel issues or lack knowledge of
|
||||
duplicate issues.
|
||||
## Caretaker Triage Process (Primary Triage)
|
||||
|
||||
It is the caretaker's responsibility to assign `comp: *` to each new issue as they come in.
|
||||
|
||||
If it's obvious that the issue or PR is related to a release regression, the caretaker is also responsible for assigning the `severity(5): regression` label to make the issue or PR highly visible.
|
||||
|
||||
The primary triage should be done on a daily basis so that the issues become available for secondary triage without much of delay.
|
||||
|
||||
The reason why we limit the responsibility of the caretaker to this one label is that it is likely that without domain knowledge the caretaker could mislabel issues or lack knowledge of duplicate issues.
|
||||
|
||||
|
||||
## Component's owner Triage Process
|
||||
|
||||
At this point we are leaving each component owner to determine their own
|
||||
process for their component.
|
||||
The component owner is responsible for assigning one of the labels from each of these categories:
|
||||
|
||||
It will be up to the component owner to determine the order in which the
|
||||
issues within the component will be resolved.
|
||||
- `type: *`
|
||||
- `frequency: *`
|
||||
- `severity: *`
|
||||
|
||||
Several owners have adopted the issue categorization based on
|
||||
[user pain](http://www.lostgarden.com/2008/05/improving-bug-triage-with-user-pain.html)
|
||||
used by AngularJS. In this system every issue is assigned frequency and
|
||||
severity based on which the total user pain score is calculated.
|
||||
We've adopted the issue categorization based on [user pain](http://www.lostgarden.com/2008/05/improving-bug-triage-with-user-pain.html) used by AngularJS. In this system every issue is assigned frequency and severity based on which the total user pain score is calculated.
|
||||
|
||||
Following is the definition of various frequency and severity levels:
|
||||
|
||||
@ -98,42 +101,42 @@ These criteria are then used to calculate a "user pain" score as follows:
|
||||
|
||||
`pain = severity × frequency`
|
||||
|
||||
This score can then be used to estimate the impact of the issue which helps with prioritization.
|
||||
|
||||
## Triaged vs Untrained PRs
|
||||
|
||||
PRs should also be label with a `comp: *` so that it is clear which
|
||||
primary area the PR effects.
|
||||
## Triaging PRs
|
||||
|
||||
Because of the cumulative pain associated with rebasing PRs, we triage PRs daily, and
|
||||
closing or reviewing PRs is a top priority ahead of other ongoing work.
|
||||
Triaging PRs is the same as triaging issues, except that PRs have additional label categories that should be used to signal their state.
|
||||
|
||||
Every triaged PR must have a `pr_action` label assigned to it and an assignee:
|
||||
Every triaged PR must have a `pr_action` label assigned to it:
|
||||
|
||||
* `PR action: review` - work is complete and comment is needed from the assignee.
|
||||
* `PR action: cleanup` - more work is needed from the current assignee.
|
||||
* `PR action: discuss` - discussion is needed, to be led by the current assignee.
|
||||
* `PR action: review` - work is complete and comment is needed from the reviewers.
|
||||
* `PR action: cleanup` - more work is needed from the author.
|
||||
* `PR action: discuss` - discussion is needed, to be led by the author.
|
||||
* `PR action: merge` - the PR is ready to be merged by the caretaker.
|
||||
|
||||
In addition, PRs can have the following states:
|
||||
|
||||
* `PR state: WIP` - PR is experimental or rapidly changing. Not ready for review or triage.
|
||||
* `PR state: blocked` - PR is blocked on an issue or other PR. Not ready for review or triage.
|
||||
* `PR state: blocked` - PR is blocked on an issue or other PR. Not ready for review or triage or merge.
|
||||
|
||||
|
||||
## PR Target
|
||||
|
||||
In our git workflow, we merge changes either to the `master` branch, the most recent patch branch (e.g. `4.3.x`), or to both.
|
||||
In our git workflow, we merge changes either to the `master` branch, the active patch branch (e.g. `5.0.x`), or to both.
|
||||
|
||||
The decision about the target must be done by the PR author and/or reviewer. This decision is then honored when the PR is being merged.
|
||||
The decision about the target must be done by the PR author and/or reviewer.
|
||||
This decision is then honored when the PR is being merged by the caretaker.
|
||||
|
||||
To communicate the target we use the following labels:
|
||||
|
||||
* `PR target: master-only`
|
||||
* `PR target: patch-only`
|
||||
* `PR target: master & patch`
|
||||
* `PR target: TBD` - the target is yet to be determined
|
||||
* `PR target: master & patch`: the PR should me merged into the master branch and cherry-picked into the most recent patch branch. All PRs with fixes, docs and refactorings should use this target.
|
||||
* `PR target: master-only`: the PR should be merged only into the `master` branch. All PRs with new features, API changes or high-risk changes should use this target.
|
||||
* `PR target: patch-only`: the PR should be merged only into the most recent patch branch (e.g. 5.0.x). This target is useful if a `master & patch` PR can't be cleanly cherry-picked into the stable branch and a new PR is needed.
|
||||
* `PR target: LTS-only`: the PR should be merged only into the active LTS branch(es). Only security and critical fixes are allowed in these branches. Always send a new PR targeting just the LTS branch and request review approval from @IgorMinar.
|
||||
* `PR target: TBD`: the target is yet to be determined.
|
||||
|
||||
If a PR is missing the "PR target" label, or if the label is set to "TBD" when the PR is sent to the caretaker, the caretaker should reject the PR and request the appropriate target label to be applied before the PR is merged.
|
||||
If a PR is missing the "PR target: *" label, or if the label is set to "TBD" when the PR is sent to the caretaker, the caretaker should reject the PR and request the appropriate target label to be applied before the PR is merged.
|
||||
|
||||
|
||||
## PR Approvals
|
||||
@ -142,23 +145,17 @@ Before a PR can be merged it must be approved by the appropriate reviewer(s).
|
||||
|
||||
To ensure that there right people review each change, we configured [PullApprove bot](https://about.pullapprove.com/) via (`.pullapprove.yaml`) to provide aggregate approval state via the GitHub PR Status API.
|
||||
|
||||
Note that approved state does not mean a PR is ready to be merged. For example, a reviewer might
|
||||
approve the PR but request a minor tweak that doesn't need further review, e.g., a rebase or small
|
||||
uncontroversial change.
|
||||
Note that approved state does not mean a PR is ready to be merged.
|
||||
For example, a reviewer might approve the PR but request a minor tweak that doesn't need further review, e.g., a rebase or small uncontroversial change.
|
||||
Only the `PR action: merge` label means that the PR is ready for merging.
|
||||
|
||||
|
||||
## Special Labels
|
||||
|
||||
### action:design
|
||||
More active discussion is needed before the issue can be worked on further. Typically used for
|
||||
`type: feature` or `type: RFC/discussion/question`
|
||||
### `cla: yes`, `cla: no`
|
||||
Managed by googlebot.
|
||||
Indicates whether a PR has a CLA on file for its author(s).
|
||||
Only issues with `cla:yes` should be merged into master.
|
||||
|
||||
[See all issues that need discussion](https://github.com/angular/angular/labels/action:%20Design)
|
||||
|
||||
### cla: yes, cla: no
|
||||
Managed by googlebot. Indicates whether a PR has a CLA on file for its author(s). Only issues with
|
||||
`cla:yes` should be merged into master.
|
||||
|
||||
### WORKS_AS_INTENDED
|
||||
|
||||
Only used on closed issues, to indicate to the reporter why we closed it.
|
||||
### `aio: preview`
|
||||
Applying this label to a PR makes the angular.io preview available regardless of the author. [More info](../aio/aio-builds-setup/docs/overview--security-model.md)
|
||||
|
4
integration/_payload-limits.json
Normal file
4
integration/_payload-limits.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"cli-hello-world":{"master":{"gzip7":{"inline":847,"main":42533,"polyfills":20207},"gzip9":{"inline":847,"main":42483,"polyfills":20204},"uncompressed":{"inline":1447,"main":154295,"polyfills":61254}}},
|
||||
"hello_world__closure":{"master":{"gzip7":{"bundle":32793},"gzip9":{"bundle":32758},"uncompressed":{"bundle":100661}}}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -u -e -o pipefail
|
||||
|
||||
declare -A payloadLimits
|
||||
payloadLimits["hello_world__closure", "uncompressed", "bundle"]=106000
|
||||
payloadLimits["hello_world__closure", "gzip7", "bundle"]=35000
|
||||
payloadLimits["hello_world__closure", "gzip9", "bundle"]=35000
|
||||
|
||||
payloadLimits["cli-hello-world", "uncompressed", "inline"]=1500
|
||||
payloadLimits["cli-hello-world", "uncompressed", "main"]=160000
|
||||
payloadLimits["cli-hello-world", "uncompressed", "polyfills"]=66000
|
||||
payloadLimits["cli-hello-world", "gzip7", "inline"]=900
|
||||
payloadLimits["cli-hello-world", "gzip7", "main"]=45000
|
||||
payloadLimits["cli-hello-world", "gzip7", "polyfills"]=22000
|
||||
payloadLimits["cli-hello-world", "gzip9", "inline"]=900
|
||||
payloadLimits["cli-hello-world", "gzip9", "main"]=45000
|
||||
payloadLimits["cli-hello-world", "gzip9", "polyfills"]=22000
|
||||
|
@ -4,9 +4,10 @@ set -e -o pipefail
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
readonly thisDir=$(cd $(dirname $0); pwd)
|
||||
|
||||
# Track payload size functions
|
||||
source ../scripts/ci/payload-size.sh
|
||||
source ./_payload-limits.sh
|
||||
|
||||
# Workaround https://github.com/yarnpkg/yarn/issues/2165
|
||||
# Yarn will cache file://dist URIs and not update Angular code
|
||||
@ -48,7 +49,7 @@ for testDir in $(ls | grep -v node_modules) ; do
|
||||
if [[ $testDir == cli-hello-world ]]; then
|
||||
yarn build
|
||||
fi
|
||||
trackPayloadSize "$testDir" "dist/*.js" true false
|
||||
trackPayloadSize "$testDir" "dist/*.js" true false "${thisDir}/_payload-limits.json"
|
||||
fi
|
||||
)
|
||||
done
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "5.0.2",
|
||||
"version": "5.0.5",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
|
@ -8,7 +8,7 @@
|
||||
import {AnimationMetadata, AnimationMetadataType, AnimationOptions, ɵStyleData} from '@angular/animations';
|
||||
|
||||
import {AnimationDriver} from '../render/animation_driver';
|
||||
import {normalizeStyles} from '../util';
|
||||
import {ENTER_CLASSNAME, LEAVE_CLASSNAME, normalizeStyles} from '../util';
|
||||
|
||||
import {Ast} from './animation_ast';
|
||||
import {buildAnimationAst} from './animation_ast_builder';
|
||||
@ -39,7 +39,8 @@ export class Animation {
|
||||
const errors: any = [];
|
||||
subInstructions = subInstructions || new ElementInstructionMap();
|
||||
const result = buildAnimationTimelines(
|
||||
this._driver, element, this._animationAst, start, dest, options, subInstructions, errors);
|
||||
this._driver, element, this._animationAst, ENTER_CLASSNAME, LEAVE_CLASSNAME, start, dest,
|
||||
options, subInstructions, errors);
|
||||
if (errors.length) {
|
||||
const errorMessage = `animation building failed:\n${errors.join("\n")}`;
|
||||
throw new Error(errorMessage);
|
||||
|
@ -60,10 +60,6 @@ export function buildAnimationAst(
|
||||
return new AnimationAstBuilderVisitor(driver).build(metadata, errors);
|
||||
}
|
||||
|
||||
const LEAVE_TOKEN = ':leave';
|
||||
const LEAVE_TOKEN_REGEX = new RegExp(LEAVE_TOKEN, 'g');
|
||||
const ENTER_TOKEN = ':enter';
|
||||
const ENTER_TOKEN_REGEX = new RegExp(ENTER_TOKEN, 'g');
|
||||
const ROOT_SELECTOR = '';
|
||||
|
||||
export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
|
||||
@ -90,6 +86,11 @@ export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
|
||||
let depCount = context.depCount = 0;
|
||||
const states: StateAst[] = [];
|
||||
const transitions: TransitionAst[] = [];
|
||||
if (metadata.name.charAt(0) == '@') {
|
||||
context.errors.push(
|
||||
'animation triggers cannot be prefixed with an `@` sign (e.g. trigger(\'@foo\', [...]))');
|
||||
}
|
||||
|
||||
metadata.definitions.forEach(def => {
|
||||
this._resetContextStyleTimingState(context);
|
||||
if (def.type == AnimationMetadataType.State) {
|
||||
@ -473,9 +474,8 @@ function normalizeSelector(selector: string): [string, boolean] {
|
||||
selector = selector.replace(SELF_TOKEN_REGEX, '');
|
||||
}
|
||||
|
||||
selector = selector.replace(ENTER_TOKEN_REGEX, ENTER_SELECTOR)
|
||||
.replace(LEAVE_TOKEN_REGEX, LEAVE_SELECTOR)
|
||||
.replace(/@\*/g, NG_TRIGGER_SELECTOR)
|
||||
// the :enter and :leave selectors are filled in at runtime during timeline building
|
||||
selector = selector.replace(/@\*/g, NG_TRIGGER_SELECTOR)
|
||||
.replace(/@\w+/g, match => NG_TRIGGER_SELECTOR + '-' + match.substr(1))
|
||||
.replace(/:animating/g, NG_ANIMATING_SELECTOR);
|
||||
|
||||
|
@ -15,6 +15,10 @@ import {AnimationTimelineInstruction, createTimelineInstruction} from './animati
|
||||
import {ElementInstructionMap} from './element_instruction_map';
|
||||
|
||||
const ONE_FRAME_IN_MILLISECONDS = 1;
|
||||
const ENTER_TOKEN = ':enter';
|
||||
const ENTER_TOKEN_REGEX = new RegExp(ENTER_TOKEN, 'g');
|
||||
const LEAVE_TOKEN = ':leave';
|
||||
const LEAVE_TOKEN_REGEX = new RegExp(LEAVE_TOKEN, 'g');
|
||||
|
||||
/*
|
||||
* The code within this file aims to generate web-animations-compatible keyframes from Angular's
|
||||
@ -102,19 +106,23 @@ const ONE_FRAME_IN_MILLISECONDS = 1;
|
||||
*/
|
||||
export function buildAnimationTimelines(
|
||||
driver: AnimationDriver, rootElement: any, ast: Ast<AnimationMetadataType>,
|
||||
startingStyles: ɵStyleData = {}, finalStyles: ɵStyleData = {}, options: AnimationOptions,
|
||||
enterClassName: string, leaveClassName: string, startingStyles: ɵStyleData = {},
|
||||
finalStyles: ɵStyleData = {}, options: AnimationOptions,
|
||||
subInstructions?: ElementInstructionMap, errors: any[] = []): AnimationTimelineInstruction[] {
|
||||
return new AnimationTimelineBuilderVisitor().buildKeyframes(
|
||||
driver, rootElement, ast, startingStyles, finalStyles, options, subInstructions, errors);
|
||||
driver, rootElement, ast, enterClassName, leaveClassName, startingStyles, finalStyles,
|
||||
options, subInstructions, errors);
|
||||
}
|
||||
|
||||
export class AnimationTimelineBuilderVisitor implements AstVisitor {
|
||||
buildKeyframes(
|
||||
driver: AnimationDriver, rootElement: any, ast: Ast<AnimationMetadataType>,
|
||||
startingStyles: ɵStyleData, finalStyles: ɵStyleData, options: AnimationOptions,
|
||||
subInstructions?: ElementInstructionMap, errors: any[] = []): AnimationTimelineInstruction[] {
|
||||
enterClassName: string, leaveClassName: string, startingStyles: ɵStyleData,
|
||||
finalStyles: ɵStyleData, options: AnimationOptions, subInstructions?: ElementInstructionMap,
|
||||
errors: any[] = []): AnimationTimelineInstruction[] {
|
||||
subInstructions = subInstructions || new ElementInstructionMap();
|
||||
const context = new AnimationTimelineContext(driver, rootElement, subInstructions, errors, []);
|
||||
const context = new AnimationTimelineContext(
|
||||
driver, rootElement, subInstructions, enterClassName, leaveClassName, errors, []);
|
||||
context.options = options;
|
||||
context.currentTimeline.setStyles([startingStyles], null, context.errors, options);
|
||||
|
||||
@ -445,8 +453,9 @@ export class AnimationTimelineContext {
|
||||
|
||||
constructor(
|
||||
private _driver: AnimationDriver, public element: any,
|
||||
public subInstructions: ElementInstructionMap, public errors: any[],
|
||||
public timelines: TimelineBuilder[], initialTimeline?: TimelineBuilder) {
|
||||
public subInstructions: ElementInstructionMap, private _enterClassName: string,
|
||||
private _leaveClassName: string, public errors: any[], public timelines: TimelineBuilder[],
|
||||
initialTimeline?: TimelineBuilder) {
|
||||
this.currentTimeline = initialTimeline || new TimelineBuilder(this._driver, element, 0);
|
||||
timelines.push(this.currentTimeline);
|
||||
}
|
||||
@ -499,8 +508,8 @@ export class AnimationTimelineContext {
|
||||
AnimationTimelineContext {
|
||||
const target = element || this.element;
|
||||
const context = new AnimationTimelineContext(
|
||||
this._driver, target, this.subInstructions, this.errors, this.timelines,
|
||||
this.currentTimeline.fork(target, newTime || 0));
|
||||
this._driver, target, this.subInstructions, this._enterClassName, this._leaveClassName,
|
||||
this.errors, this.timelines, this.currentTimeline.fork(target, newTime || 0));
|
||||
context.previousNode = this.previousNode;
|
||||
context.currentAnimateTimings = this.currentAnimateTimings;
|
||||
|
||||
@ -555,6 +564,8 @@ export class AnimationTimelineContext {
|
||||
results.push(this.element);
|
||||
}
|
||||
if (selector.length > 0) { // if :self is only used then the selector is empty
|
||||
selector = selector.replace(ENTER_TOKEN_REGEX, '.' + this._enterClassName);
|
||||
selector = selector.replace(LEAVE_TOKEN_REGEX, '.' + this._leaveClassName);
|
||||
const multi = limit != 1;
|
||||
let elements = this._driver.query(this.element, selector, multi);
|
||||
if (limit !== 0) {
|
||||
|
@ -37,7 +37,8 @@ export class AnimationTransitionFactory {
|
||||
|
||||
build(
|
||||
driver: AnimationDriver, element: any, currentState: any, nextState: any,
|
||||
currentOptions?: AnimationOptions, nextOptions?: AnimationOptions,
|
||||
enterClassName: string, leaveClassName: string, currentOptions?: AnimationOptions,
|
||||
nextOptions?: AnimationOptions,
|
||||
subInstructions?: ElementInstructionMap): AnimationTransitionInstruction {
|
||||
const errors: any[] = [];
|
||||
|
||||
@ -55,8 +56,8 @@ export class AnimationTransitionFactory {
|
||||
const animationOptions = {params: {...transitionAnimationParams, ...nextAnimationParams}};
|
||||
|
||||
const timelines = buildAnimationTimelines(
|
||||
driver, element, this.ast.animation, currentStateStyles, nextStateStyles, animationOptions,
|
||||
subInstructions, errors);
|
||||
driver, element, this.ast.animation, enterClassName, leaveClassName, currentStateStyles,
|
||||
nextStateStyles, animationOptions, subInstructions, errors);
|
||||
|
||||
if (errors.length) {
|
||||
return createTransitionInstruction(
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user