docs(upgrade): update to latest release + tweaks (#2460)
|
@ -1,18 +1,29 @@
|
|||
declare var angular: any;
|
||||
// #docregion ngmodule
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ]
|
||||
})
|
||||
export class AppModule {}
|
||||
// #enddocregion ngmodule
|
||||
angular.module('heroApp', [])
|
||||
.controller('MainCtrl', function() {
|
||||
this.message = 'Hello world';
|
||||
});
|
||||
|
||||
|
||||
// #docregion bootstrap
|
||||
import { UpgradeAdapter } from '@angular/upgrade';
|
||||
|
||||
// #enddocregion bootstrap
|
||||
|
||||
angular.module('heroApp', [])
|
||||
.controller('MainCtrl', function() {
|
||||
this.message = 'Hello world';
|
||||
});
|
||||
// This blank is expected to trigger the docplaster
|
||||
|
||||
// #docregion bootstrap
|
||||
|
||||
const upgradeAdapter = new UpgradeAdapter();
|
||||
const upgradeAdapter = new UpgradeAdapter(AppModule);
|
||||
|
||||
upgradeAdapter.bootstrap(document.body, ['heroApp'], {strictDi: true});
|
||||
// #enddocregion bootstrap
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
angular.module('heroApp', [])
|
||||
.controller('MainCtrl', function() {
|
||||
this.message = 'Hello world';
|
||||
});
|
||||
|
||||
// #docregion
|
||||
import { UpgradeAdapter } from '@angular/upgrade';
|
||||
export const upgradeAdapter = new UpgradeAdapter();
|
||||
export const upgradeAdapter = new UpgradeAdapter(AppModule);
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { UpgradeAdapter } from '@angular/upgrade';
|
||||
|
||||
import { MainController } from './main.controller';
|
||||
import { HeroDetailComponent } from './hero-detail.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ],
|
||||
declarations: [ HeroDetailComponent ]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
declare var angular: any;
|
||||
const upgradeAdapter = new UpgradeAdapter();
|
||||
const upgradeAdapter = new UpgradeAdapter(AppModule);
|
||||
|
||||
angular.module('heroApp', [])
|
||||
.controller('MainController', MainController)
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { HeroDetailComponent } from './hero-detail.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ],
|
||||
declarations: [ HeroDetailComponent ]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
angular.module('heroApp', [])
|
||||
.controller('MainCtrl', function() {
|
||||
this.message = 'Hello world';
|
||||
});
|
||||
|
||||
// #docregion
|
||||
import { UpgradeAdapter } from '@angular/upgrade';
|
||||
export const upgradeAdapter = new UpgradeAdapter();
|
||||
export const upgradeAdapter = new UpgradeAdapter(AppModule);
|
||||
|
|
|
@ -5,8 +5,6 @@ import { upgradeAdapter } from './upgrade_adapter';
|
|||
declare var angular: any;
|
||||
|
||||
// #docregion register
|
||||
upgradeAdapter.addProvider(Heroes);
|
||||
|
||||
angular.module('heroApp', [])
|
||||
.factory('heroes', upgradeAdapter.downgradeNg2Provider(Heroes))
|
||||
.component('heroDetail', heroDetailComponent);
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
// #docregion ngmodule
|
||||
import { Heroes } from './heroes';
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ],
|
||||
providers: [ Heroes ]
|
||||
})
|
||||
export class AppModule {}
|
||||
// #enddocregion ngmodule
|
||||
angular.module('heroApp', [])
|
||||
.controller('MainCtrl', function() {
|
||||
this.message = 'Hello world';
|
||||
});
|
||||
|
||||
// #docregion
|
||||
import { UpgradeAdapter } from '@angular/upgrade';
|
||||
export const upgradeAdapter = new UpgradeAdapter();
|
||||
export const upgradeAdapter = new UpgradeAdapter(AppModule);
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
import { upgradeAdapter } from './upgrade_adapter';
|
||||
import { Hero } from '../hero';
|
||||
|
||||
const HeroDetail = upgradeAdapter.upgradeNg1Component('heroDetail');
|
||||
|
||||
@Component({
|
||||
selector: 'my-container',
|
||||
template: `
|
||||
|
@ -12,8 +9,7 @@ const HeroDetail = upgradeAdapter.upgradeNg1Component('heroDetail');
|
|||
<!-- Everything here will get transcluded -->
|
||||
<p>{{hero.description}}</p>
|
||||
</hero-detail>
|
||||
`,
|
||||
directives: [HeroDetail]
|
||||
`
|
||||
})
|
||||
export class ContainerComponent {
|
||||
hero = new Hero(1, 'Windstorm', 'Specific powers of controlling winds');
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
// #docregion
|
||||
import { UpgradeAdapter } from '@angular/upgrade';
|
||||
export const upgradeAdapter = new UpgradeAdapter();
|
||||
import { NgModule, forwardRef } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { ContainerComponent } from './container.component';
|
||||
|
||||
export const upgradeAdapter = new UpgradeAdapter(forwardRef(() => AppModule));
|
||||
const HeroDetail = upgradeAdapter.upgradeNg1Component('heroDetail');
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ],
|
||||
declarations: [ ContainerComponent, HeroDetail ]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
angular.module('heroApp', [])
|
||||
.controller('MainCtrl', function() {
|
||||
this.message = 'Hello world';
|
||||
});
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { MainController } from './main.controller';
|
||||
// #docregion downgradecomponent
|
||||
import { HeroDetailComponent } from './hero-detail.component';
|
||||
|
||||
// #enddocregion downgradecomponent
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ],
|
||||
declarations: [ HeroDetailComponent ]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
import { UpgradeAdapter } from '@angular/upgrade';
|
||||
|
||||
const upgradeAdapter = new UpgradeAdapter();
|
||||
const upgradeAdapter = new UpgradeAdapter(AppModule);
|
||||
|
||||
// #docregion downgradecomponent
|
||||
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
// #docregion downgradecomponent
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
// #docregion downgradecomponent, ngmodule
|
||||
import { HeroDetailComponent } from './hero-detail.component';
|
||||
|
||||
// #enddocregion downgradecomponent
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ],
|
||||
declarations: [ HeroDetailComponent ]
|
||||
})
|
||||
export class AppModule {}
|
||||
// #enddocregion ngmodule
|
||||
import { UpgradeAdapter } from '@angular/upgrade';
|
||||
|
||||
const upgradeAdapter = new UpgradeAdapter();
|
||||
const upgradeAdapter = new UpgradeAdapter(AppModule);
|
||||
|
||||
// #docregion downgradecomponent
|
||||
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
import { upgradeAdapter } from './upgrade_adapter';
|
||||
import { Hero } from '../hero';
|
||||
|
||||
const HeroDetail = upgradeAdapter.upgradeNg1Component('heroDetail');
|
||||
|
||||
@Component({
|
||||
selector: 'my-container',
|
||||
template: `
|
||||
|
@ -12,8 +9,7 @@ const HeroDetail = upgradeAdapter.upgradeNg1Component('heroDetail');
|
|||
<hero-detail [hero]="hero"
|
||||
(deleted)="heroDeleted($event)">
|
||||
</hero-detail>
|
||||
`,
|
||||
directives: [HeroDetail]
|
||||
`
|
||||
})
|
||||
export class ContainerComponent {
|
||||
hero = new Hero(1, 'Windstorm');
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
// #docregion
|
||||
import { UpgradeAdapter } from '@angular/upgrade';
|
||||
export const upgradeAdapter = new UpgradeAdapter();
|
||||
import { NgModule, forwardRef } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { ContainerComponent } from './container.component';
|
||||
|
||||
export const upgradeAdapter = new UpgradeAdapter(forwardRef(() => AppModule));
|
||||
const HeroDetail = upgradeAdapter.upgradeNg1Component('heroDetail');
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ],
|
||||
declarations: [ ContainerComponent, HeroDetail ]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
angular.module('heroApp', [])
|
||||
.controller('MainCtrl', function() {
|
||||
this.message = 'Hello world';
|
||||
});
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
import { upgradeAdapter } from './upgrade_adapter';
|
||||
|
||||
const HeroDetail = upgradeAdapter.upgradeNg1Component('heroDetail');
|
||||
|
||||
@Component({
|
||||
selector: 'my-container',
|
||||
template: `
|
||||
<h1>Tour of Heroes</h1>
|
||||
<hero-detail></hero-detail>
|
||||
`,
|
||||
directives: [HeroDetail]
|
||||
`
|
||||
})
|
||||
export class ContainerComponent {
|
||||
|
||||
|
|
|
@ -1,3 +1,23 @@
|
|||
// #docregion
|
||||
import { UpgradeAdapter } from '@angular/upgrade';
|
||||
export const upgradeAdapter = new UpgradeAdapter();
|
||||
import { NgModule, forwardRef } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { ContainerComponent } from './container.component';
|
||||
|
||||
export const upgradeAdapter = new UpgradeAdapter(forwardRef(() => AppModule));
|
||||
|
||||
// #docregion heroupgrade
|
||||
const HeroDetail = upgradeAdapter.upgradeNg1Component('heroDetail');
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule ],
|
||||
declarations: [ ContainerComponent, HeroDetail ]
|
||||
})
|
||||
export class AppModule {}
|
||||
// #enddocregion heroupgrade
|
||||
|
||||
angular.module('heroApp', [])
|
||||
.controller('MainCtrl', function() {
|
||||
this.message = 'Hello world';
|
||||
});
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('app').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
<script>
|
||||
System.import('app/1-2-hybrid-bootstrap/app.module')
|
||||
.then(null, console.error.bind(console));
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('app').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
<script>
|
||||
System.import('app/1-2-hybrid-shared-adapter-bootstrap/app.module')
|
||||
.then(null, console.error.bind(console));
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('app').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
<script>
|
||||
System.import('app/1-to-2-projection/app.module')
|
||||
.then(null, console.error.bind(console));
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('app').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
<script>
|
||||
System.import('app/1-to-2-providers/app.module')
|
||||
.then(null, console.error.bind(console));
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('app').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
<script>
|
||||
System.import('app/2-to-1-providers/app.module')
|
||||
.then(null, console.error.bind(console));
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('app').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
<script>
|
||||
System.import('app/2-to-1-transclusion/app.module')
|
||||
.then(null, console.error.bind(console));
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('app').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
<script>
|
||||
System.import('app/downgrade-io/app.module')
|
||||
.then(null, console.error.bind(console));
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('app').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
<script>
|
||||
System.import('app/downgrade-static/app.module')
|
||||
.then(null, console.error.bind(console));
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('app').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
<script>
|
||||
System.import('app/upgrade-io/app.module')
|
||||
.then(null, console.error.bind(console));
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('app').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
<script>
|
||||
System.import('app/upgrade-static/app.module')
|
||||
.then(null, console.error.bind(console));
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// #docregion
|
||||
'use strict';
|
||||
|
||||
// Define the `phonecatApp` Angular 1 module
|
||||
angular.module('phonecatApp', [
|
||||
'ngAnimate',
|
||||
'ngRoute',
|
||||
'core',
|
||||
'phoneDetail',
|
||||
'phoneList',
|
||||
]);
|
|
@ -1,10 +1,51 @@
|
|||
'use strict';
|
||||
// #docplaster
|
||||
// #docregion bare
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
// #enddocregion bare
|
||||
// #docregion httpmodule
|
||||
import { HttpModule } from '@angular/http';
|
||||
// #enddocregion httpmodule
|
||||
// #docregion phonelist
|
||||
import { FormsModule } from '@angular/forms';
|
||||
// #enddocregion phonelist
|
||||
// #docregion phone
|
||||
import { Phone } from './core/phone/phone.service';
|
||||
// #enddocregion phone
|
||||
// #docregion checkmarkpipe
|
||||
import { CheckmarkPipe } from './core/checkmark/checkmark.pipe';
|
||||
// #enddocregion checkmarkpipe
|
||||
// #docregion phonelist
|
||||
import { PhoneListComponent } from './phone-list/phone-list.component';
|
||||
// #enddocregion phonelist
|
||||
// #docregion phonedetail
|
||||
import { PhoneDetailComponent } from './phone-detail/phone-detail.component';
|
||||
// #enddocregion phonedetail
|
||||
|
||||
// Define the `phonecatApp` module
|
||||
angular.module('phonecatApp', [
|
||||
'ngAnimate',
|
||||
'ngRoute',
|
||||
'core',
|
||||
'phoneDetail',
|
||||
'phoneList',
|
||||
]);
|
||||
// #docregion bare, httpmodule, phone, phonelist, phonedetail, checkmarkpipe
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
// #enddocregion bare
|
||||
HttpModule,
|
||||
// #enddocregion httpmodule, phone
|
||||
FormsModule,
|
||||
// #docregion bare, httpmodule, phone
|
||||
],
|
||||
// #enddocregion bare, httpmodule, phone
|
||||
declarations: [
|
||||
PhoneListComponent,
|
||||
// #enddocregion phonelist
|
||||
PhoneDetailComponent,
|
||||
// #enddocregion phonedetail
|
||||
CheckmarkPipe
|
||||
// #docregion phonelist, phonedetail
|
||||
],
|
||||
// #docregion phone
|
||||
providers: [ Phone ]
|
||||
// #docregion bare, httpmodule, phonelist
|
||||
})
|
||||
export class AppModule {}
|
||||
// #enddocregion httpmodule, phone, phonelist, phonedetail, checkmarkpipe
|
||||
// #enddocregion bare
|
||||
|
|
|
@ -1,19 +1,11 @@
|
|||
// #docregion
|
||||
import {
|
||||
beforeEachProviders,
|
||||
inject
|
||||
} from '@angular/core/testing';
|
||||
import { CheckmarkPipe } from './checkmark.pipe';
|
||||
|
||||
describe('CheckmarkPipe', function() {
|
||||
|
||||
beforeEachProviders(() => [CheckmarkPipe]);
|
||||
|
||||
it('should convert boolean values to unicode checkmark or cross',
|
||||
inject([CheckmarkPipe], function(checkmarkPipe: CheckmarkPipe) {
|
||||
expect(checkmarkPipe.transform(true)).toBe('\u2713');
|
||||
expect(checkmarkPipe.transform(false)).toBe('\u2718');
|
||||
})
|
||||
);
|
||||
|
||||
it('should convert boolean values to unicode checkmark or cross', function () {
|
||||
const checkmarkPipe = new CheckmarkPipe();
|
||||
expect(checkmarkPipe.transform(true)).toBe('\u2713');
|
||||
expect(checkmarkPipe.transform(false)).toBe('\u2718');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,9 +3,7 @@ import { Pipe, PipeTransform } from '@angular/core';
|
|||
|
||||
@Pipe({name: 'checkmark'})
|
||||
export class CheckmarkPipe implements PipeTransform {
|
||||
|
||||
transform(input: boolean) {
|
||||
return input ? '\u2713' : '\u2718';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
// #docregion
|
||||
import {
|
||||
describe,
|
||||
beforeEach,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
inject
|
||||
} from '@angular/core/testing';
|
||||
import { inject, TestBed } from '@angular/core/testing';
|
||||
import {
|
||||
Http,
|
||||
BaseRequestOptions,
|
||||
|
@ -24,29 +18,28 @@ describe('Phone', function() {
|
|||
];
|
||||
let mockBackend: MockBackend;
|
||||
|
||||
beforeEachProviders(() => [
|
||||
Phone,
|
||||
MockBackend,
|
||||
BaseRequestOptions,
|
||||
{ provide: Http,
|
||||
useFactory: (backend: MockBackend, options: BaseRequestOptions) =>
|
||||
new Http(backend, options),
|
||||
deps: [MockBackend, BaseRequestOptions]
|
||||
}
|
||||
]);
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
Phone,
|
||||
MockBackend,
|
||||
BaseRequestOptions,
|
||||
{ provide: Http,
|
||||
useFactory: (backend: MockBackend, options: BaseRequestOptions) => new Http(backend, options),
|
||||
deps: [MockBackend, BaseRequestOptions]
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(inject([MockBackend, Phone],
|
||||
(_mockBackend_: MockBackend, _phone_: Phone) => {
|
||||
beforeEach(inject([MockBackend, Phone], (_mockBackend_: MockBackend, _phone_: Phone) => {
|
||||
mockBackend = _mockBackend_;
|
||||
phone = _phone_;
|
||||
}));
|
||||
|
||||
it('should fetch the phones data from `/phones/phones.json`',
|
||||
(done: () => void) => {
|
||||
it('should fetch the phones data from `/phones/phones.json`', (done: () => void) => {
|
||||
mockBackend.connections.subscribe((conn: MockConnection) => {
|
||||
conn.mockRespond(new Response(new ResponseOptions({
|
||||
body: JSON.stringify(phonesData)
|
||||
})));
|
||||
conn.mockRespond(new Response(new ResponseOptions({body: JSON.stringify(phonesData)})));
|
||||
});
|
||||
phone.query().subscribe(result => {
|
||||
expect(result).toEqual(phonesData);
|
||||
|
@ -55,3 +48,4 @@ describe('Phone', function() {
|
|||
});
|
||||
|
||||
});
|
||||
|
||||
|
|
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 28 KiB |
|
@ -1,9 +1,8 @@
|
|||
// #docregion import-adapter
|
||||
import { UpgradeAdapter } from '@angular/upgrade';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
// #enddocregion import-adapter
|
||||
// #docregion import-http
|
||||
import { HTTP_PROVIDERS } from '@angular/http';
|
||||
// #enddocregion import-http
|
||||
// #docregion phone-service
|
||||
import { Phone } from './core/phone/phone.service';
|
||||
|
||||
|
@ -17,17 +16,9 @@ import { PhoneDetailComponent } from './phone-detail/phone-detail.component';
|
|||
|
||||
// #enddocregion phone-detail
|
||||
// #docregion init-adapter
|
||||
let upgradeAdapter = new UpgradeAdapter();
|
||||
let upgradeAdapter = new UpgradeAdapter(AppModule);
|
||||
// #enddocregion init-adapter
|
||||
|
||||
// #docregion add-http
|
||||
upgradeAdapter.addProvider(HTTP_PROVIDERS);
|
||||
// #enddocregion add-http
|
||||
// #docregion phone-service
|
||||
|
||||
upgradeAdapter.addProvider(Phone);
|
||||
|
||||
// #enddocregion phone-service
|
||||
// #docregion routeparams
|
||||
upgradeAdapter.upgradeNg1Provider('$routeParams');
|
||||
// #enddocregion routeparams
|
||||
|
@ -42,8 +33,7 @@ angular.module('core.phone')
|
|||
angular.module('phoneList')
|
||||
.directive(
|
||||
'phoneList',
|
||||
<angular.IDirectiveFactory>
|
||||
upgradeAdapter.downgradeNg2Component(PhoneListComponent)
|
||||
upgradeAdapter.downgradeNg2Component(PhoneListComponent) as angular.IDirectiveFactory
|
||||
);
|
||||
// #enddocregion phone-list
|
||||
// #docregion phone-detail
|
||||
|
@ -51,8 +41,7 @@ angular.module('phoneList')
|
|||
angular.module('phoneDetail')
|
||||
.directive(
|
||||
'phoneDetail',
|
||||
<angular.IDirectiveFactory>
|
||||
upgradeAdapter.downgradeNg2Component(PhoneDetailComponent)
|
||||
upgradeAdapter.downgradeNg2Component(PhoneDetailComponent) as angular.IDirectiveFactory
|
||||
);
|
||||
// #enddocregion phone-detail
|
||||
|
||||
|
|
|
@ -1,21 +1,15 @@
|
|||
// #docregion
|
||||
import { HTTP_PROVIDERS } from '@angular/http';
|
||||
// #docregion activatedroute
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
// #enddocregion activatedroute
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
|
||||
import {
|
||||
describe,
|
||||
beforeEachProviders,
|
||||
inject,
|
||||
it,
|
||||
expect
|
||||
} from '@angular/core/testing';
|
||||
import {
|
||||
TestComponentBuilder,
|
||||
ComponentFixture
|
||||
} from '@angular/compiler/testing';
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PhoneDetailComponent } from './phone-detail.component';
|
||||
import { Phone, PhoneData } from '../core/phone/phone.service';
|
||||
import { CheckmarkPipe } from '../core/checkmark/checkmark.pipe';
|
||||
|
||||
function xyzPhoneData(): PhoneData {
|
||||
return {
|
||||
|
@ -25,28 +19,41 @@ function xyzPhoneData(): PhoneData {
|
|||
};
|
||||
}
|
||||
|
||||
class MockPhone extends Phone {
|
||||
class MockPhone {
|
||||
get(id: string): Observable<PhoneData> {
|
||||
return Observable.of(xyzPhoneData());
|
||||
}
|
||||
}
|
||||
|
||||
// #docregion activatedroute
|
||||
|
||||
class ActivatedRouteMock {
|
||||
constructor(public snapshot: any) {}
|
||||
}
|
||||
|
||||
// #enddocregion activatedroute
|
||||
|
||||
describe('PhoneDetailComponent', () => {
|
||||
|
||||
beforeEachProviders(() => [
|
||||
{ provide: Phone, useClass: MockPhone },
|
||||
{ provide: '$routeParams', useValue: {phoneId: 'xyz'}},
|
||||
HTTP_PROVIDERS
|
||||
]);
|
||||
// #docregion activatedroute
|
||||
|
||||
it('should fetch phone detail',
|
||||
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
return tcb.createAsync(PhoneDetailComponent)
|
||||
.then((fixture: ComponentFixture<PhoneDetailComponent>) => {
|
||||
fixture.detectChanges();
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('h1')).toHaveText(xyzPhoneData().name);
|
||||
});
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ CheckmarkPipe, PhoneDetailComponent ],
|
||||
providers: [
|
||||
{ provide: Phone, useClass: MockPhone },
|
||||
{ provide: ActivatedRoute, useValue: new ActivatedRouteMock({ params: { 'phoneId': 1 } }) }
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
// #enddocregion activatedroute
|
||||
|
||||
it('should fetch phone detail', () => {
|
||||
const fixture = TestBed.createComponent(PhoneDetailComponent);
|
||||
fixture.detectChanges();
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('h1').textContent).toContain(xyzPhoneData().name);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// #docplaster
|
||||
// #docregion initialclass
|
||||
import { Component, Inject } from '@angular/core';
|
||||
|
||||
import { Phone, PhoneData } from '../core/phone/phone.service';
|
||||
// #enddocregion initialclass
|
||||
// #docregion checkmark-pipe
|
||||
import { CheckmarkPipe } from '../core/checkmark/checkmark.pipe';
|
||||
|
||||
// #docregion initialclass
|
||||
@Component({
|
||||
|
@ -12,7 +12,6 @@ import { CheckmarkPipe } from '../core/checkmark/checkmark.pipe';
|
|||
selector: 'phone-detail',
|
||||
templateUrl: 'phone-detail.template.html',
|
||||
// #enddocregion initialclass
|
||||
pipes: [ CheckmarkPipe ]
|
||||
// #docregion initialclass
|
||||
})
|
||||
// #enddocregion checkmark-pipe
|
||||
|
|
|
@ -1,66 +1,66 @@
|
|||
/* tslint:disable */
|
||||
// #docregion
|
||||
import { HTTP_PROVIDERS } from '@angular/http';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
import {
|
||||
describe,
|
||||
beforeEachProviders,
|
||||
inject,
|
||||
it,
|
||||
expect
|
||||
} from '@angular/core/testing';
|
||||
import {
|
||||
TestComponentBuilder,
|
||||
ComponentFixture
|
||||
} from '@angular/compiler/testing';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { SpyLocation } from '@angular/common/testing';
|
||||
|
||||
import { PhoneListComponent } from './phone-list.component';
|
||||
import { Phone, PhoneData } from '../core/phone/phone.service';
|
||||
|
||||
class MockPhone extends Phone {
|
||||
class ActivatedRouteMock {
|
||||
constructor(public snapshot: any) {}
|
||||
}
|
||||
|
||||
class MockPhone {
|
||||
query(): Observable<PhoneData[]> {
|
||||
console.log('mocking here');
|
||||
return Observable.of(
|
||||
[
|
||||
{name: 'Nexus S', snippet: '', images: []},
|
||||
{name: 'Motorola DROID', snippet: '', images: []}
|
||||
]
|
||||
);
|
||||
return Observable.of([
|
||||
{name: 'Nexus S', snippet: '', images: []},
|
||||
{name: 'Motorola DROID', snippet: '', images: []}
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
let fixture: ComponentFixture<PhoneListComponent>;
|
||||
|
||||
describe('PhoneList', () => {
|
||||
|
||||
beforeEachProviders(() => [
|
||||
{ provide: Phone, useClass: MockPhone },
|
||||
HTTP_PROVIDERS
|
||||
]);
|
||||
|
||||
it('should create "phones" model with 2 phones fetched from xhr',
|
||||
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
return tcb.createAsync(PhoneListComponent)
|
||||
.then((fixture: ComponentFixture<PhoneListComponent>) => {
|
||||
fixture.detectChanges();
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelectorAll('.phone-list-item').length).toBe(2);
|
||||
expect(
|
||||
compiled.querySelector('.phone-list-item:nth-child(1)').textContent
|
||||
).toContain('Motorola DROID');
|
||||
expect(
|
||||
compiled.querySelector('.phone-list-item:nth-child(2)').textContent
|
||||
).toContain('Nexus S');
|
||||
});
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ PhoneListComponent ],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: new ActivatedRouteMock({ params: { 'phoneId': 1 } }) },
|
||||
{ provide: Location, useClass: SpyLocation },
|
||||
{ provide: Phone, useClass: MockPhone },
|
||||
],
|
||||
schemas: [ NO_ERRORS_SCHEMA ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
it('should set the default value of orderProp model',
|
||||
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
return tcb.createAsync(PhoneListComponent)
|
||||
.then((fixture: ComponentFixture<PhoneListComponent>) => {
|
||||
fixture.detectChanges();
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
expect(
|
||||
compiled.querySelector('select option:last-child').selected
|
||||
).toBe(true);
|
||||
});
|
||||
}));
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PhoneListComponent);
|
||||
});
|
||||
|
||||
it('should create "phones" model with 2 phones fetched from xhr', () => {
|
||||
fixture.detectChanges();
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelectorAll('.phone-list-item').length).toBe(2);
|
||||
expect(
|
||||
compiled.querySelector('.phone-list-item:nth-child(1)').textContent
|
||||
).toContain('Motorola DROID');
|
||||
expect(
|
||||
compiled.querySelector('.phone-list-item:nth-child(2)').textContent
|
||||
).toContain('Nexus S');
|
||||
});
|
||||
|
||||
xit('should set the default value of orderProp model', () => {
|
||||
fixture.detectChanges();
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
expect(
|
||||
compiled.querySelector('select option:last-child').selected
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -38,20 +38,20 @@ export class PhoneListComponent {
|
|||
}
|
||||
|
||||
private sortPhones(phones: PhoneData[]) {
|
||||
if (phones && this.orderProp) {
|
||||
return phones
|
||||
.slice(0) // Make a copy
|
||||
.sort((a, b) => {
|
||||
if (a[this.orderProp] < b[this.orderProp]) {
|
||||
return -1;
|
||||
} else if ([b[this.orderProp] < a[this.orderProp]]) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
return phones;
|
||||
if (phones && this.orderProp) {
|
||||
return phones
|
||||
.slice(0) // Make a copy
|
||||
.sort((a, b) => {
|
||||
if (a[this.orderProp] < b[this.orderProp]) {
|
||||
return -1;
|
||||
} else if ([b[this.orderProp] < a[this.orderProp]]) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
return phones;
|
||||
}
|
||||
// #enddocregion getphones
|
||||
// #docregion initialclass
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<script src="https://code.angularjs.org/1.5.5/angular-resource.js"></script>
|
||||
<script src="https://code.angularjs.org/1.5.5/angular-route.js"></script>
|
||||
|
||||
<script src="app.module.js"></script>
|
||||
<script src="app.module.ng1.js"></script>
|
||||
<script src="app.config.js"></script>
|
||||
<script src="app.animations.js"></script>
|
||||
<script src="core/core.module.js"></script>
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
// #docregion
|
||||
// /*global jasmine, __karma__, window*/
|
||||
Error.stackTraceLimit = Infinity;
|
||||
Error.stackTraceLimit = 0; // "No stacktrace"" is usually best for app testing.
|
||||
|
||||
// Uncomment to get full stacktrace output. Sometimes helpful, usually not.
|
||||
// Error.stackTraceLimit = Infinity; //
|
||||
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
|
||||
|
||||
__karma__.loaded = function () {
|
||||
};
|
||||
var builtPath = '/base/app/';
|
||||
|
||||
__karma__.loaded = function () { };
|
||||
|
||||
function isJsFile(path) {
|
||||
return path.slice(-3) == '.js';
|
||||
}
|
||||
|
||||
function isSpecFile(path) {
|
||||
return /\.spec\.js$/.test(path);
|
||||
return /\.spec\.(.*\.)?js$/.test(path);
|
||||
}
|
||||
|
||||
function isBuiltFile(path) {
|
||||
var builtPath = '/base/app/';
|
||||
return isJsFile(path) && (path.substr(0, builtPath.length) == builtPath);
|
||||
}
|
||||
|
||||
|
@ -25,27 +29,61 @@ var allSpecFiles = Object.keys(window.__karma__.files)
|
|||
|
||||
System.config({
|
||||
baseURL: '/base',
|
||||
packageWithIndex: true // sadly, we can't use umd packages (yet?)
|
||||
// Extend usual application package list with test folder
|
||||
packages: { 'testing': { main: 'index.js', defaultExtension: 'js' } },
|
||||
|
||||
// Assume npm: is set in `paths` in systemjs.config
|
||||
// Map the angular testing umd bundles
|
||||
map: {
|
||||
'@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
|
||||
'@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
|
||||
'@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
|
||||
'@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
|
||||
'@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
|
||||
'@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js',
|
||||
'@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js',
|
||||
'@angular/forms/testing': 'npm:@angular/forms/bundles/forms-testing.umd.js',
|
||||
},
|
||||
});
|
||||
|
||||
System.import('systemjs.config.js')
|
||||
.then(() => Promise.all([
|
||||
System.import('@angular/core/testing'),
|
||||
System.import('@angular/platform-browser-dynamic/testing')
|
||||
]))
|
||||
.then((providers) => {
|
||||
var coreTesting = providers[0];
|
||||
.then(importSystemJsExtras)
|
||||
.then(initTestBed)
|
||||
.then(initTesting);
|
||||
|
||||
/** Optional SystemJS configuration extras. Keep going w/o it */
|
||||
function importSystemJsExtras(){
|
||||
return System.import('systemjs.config.extras.js')
|
||||
.catch(function(reason) {
|
||||
console.log(
|
||||
'Warning: System.import could not load the optional "systemjs.config.extras.js". Did you omit it by accident? Continuing without it.'
|
||||
);
|
||||
console.log(reason);
|
||||
});
|
||||
}
|
||||
|
||||
function initTestBed(){
|
||||
return Promise.all([
|
||||
System.import('@angular/core/testing'),
|
||||
System.import('@angular/platform-browser-dynamic/testing')
|
||||
])
|
||||
|
||||
.then(function (providers) {
|
||||
var coreTesting = providers[0];
|
||||
var browserTesting = providers[1];
|
||||
|
||||
coreTesting.TestBed.initTestEnvironment(
|
||||
browserTesting.BrowserDynamicTestingModule,
|
||||
browserTesting.platformBrowserDynamicTesting());
|
||||
})
|
||||
.then(function () {
|
||||
// Finally, load all spec files.
|
||||
// This will run the tests directly.
|
||||
return Promise.all(
|
||||
allSpecFiles.map(function (moduleName) {
|
||||
return System.import(moduleName);
|
||||
}));
|
||||
})
|
||||
}
|
||||
|
||||
// Import all spec files and start karma
|
||||
function initTesting () {
|
||||
return Promise.all(
|
||||
allSpecFiles.map(function (moduleName) {
|
||||
return System.import(moduleName);
|
||||
})
|
||||
)
|
||||
.then(__karma__.start, __karma__.error);
|
||||
}
|
||||
|
|
|
@ -63,11 +63,10 @@ module.exports = function(config) {
|
|||
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
browsers: ['Chrome', 'Firefox'],
|
||||
browsers: ['Chrome'],
|
||||
|
||||
plugins: [
|
||||
'karma-chrome-launcher',
|
||||
'karma-firefox-launcher',
|
||||
'karma-jasmine'
|
||||
]
|
||||
|
||||
|
|
|
@ -1,59 +1,47 @@
|
|||
/**
|
||||
* System configuration for Angular 2 samples
|
||||
* System configuration for Angular samples
|
||||
* Adjust as necessary for your application needs.
|
||||
*/
|
||||
(function(global) {
|
||||
|
||||
// map tells the System loader where to look for things
|
||||
(function (global) {
|
||||
// #docregion paths
|
||||
var map = {
|
||||
'app': '/app', // 'dist',
|
||||
|
||||
'@angular': '/node_modules/@angular',
|
||||
'angular-in-memory-web-api': '/node_modules/angular-in-memory-web-api',
|
||||
'rxjs': '/node_modules/rxjs'
|
||||
};
|
||||
|
||||
var packages = {
|
||||
'/app': { main: 'main.js', defaultExtension: 'js' },
|
||||
'rxjs': { defaultExtension: 'js' },
|
||||
'angular-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
|
||||
};
|
||||
// #enddocregion paths
|
||||
|
||||
var ngPackageNames = [
|
||||
'common',
|
||||
'compiler',
|
||||
'core',
|
||||
'forms',
|
||||
'http',
|
||||
'platform-browser',
|
||||
'platform-browser-dynamic',
|
||||
'router',
|
||||
'router-deprecated',
|
||||
'upgrade',
|
||||
];
|
||||
|
||||
// Individual files (~300 requests):
|
||||
function packIndex(pkgName) {
|
||||
packages['@angular/'+pkgName] = { main: 'index.js', defaultExtension: 'js' };
|
||||
}
|
||||
|
||||
// Bundled (~40 requests):
|
||||
function packUmd(pkgName) {
|
||||
packages['@angular/'+pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };
|
||||
}
|
||||
|
||||
var setPackageConfig = System.packageWithIndex ? packIndex : packUmd;
|
||||
|
||||
// Add package entries for angular packages
|
||||
ngPackageNames.forEach(setPackageConfig);
|
||||
|
||||
var config = {
|
||||
map: map,
|
||||
packages: packages
|
||||
}
|
||||
|
||||
System.config(config);
|
||||
System.config({
|
||||
paths: {
|
||||
// paths serve as alias
|
||||
'npm:': '/node_modules/'
|
||||
},
|
||||
map: {
|
||||
app: '/app',
|
||||
// #enddocregion paths
|
||||
// angular bundles
|
||||
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
|
||||
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
|
||||
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
|
||||
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
|
||||
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
|
||||
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
|
||||
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
|
||||
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
|
||||
'@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
|
||||
|
||||
// other libraries
|
||||
'rxjs': 'npm:rxjs',
|
||||
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api',
|
||||
// #docregion paths
|
||||
},
|
||||
// #enddocregion paths
|
||||
// packages tells the System loader how to load when no filename and/or no extension
|
||||
packages: {
|
||||
'app': {
|
||||
main: './main.js',
|
||||
defaultExtension: 'js'
|
||||
},
|
||||
rxjs: {
|
||||
defaultExtension: 'js'
|
||||
},
|
||||
'angular-in-memory-web-api': {
|
||||
main: './index.js',
|
||||
defaultExtension: 'js'
|
||||
}
|
||||
}
|
||||
});
|
||||
})(this);
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
// #docregion
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { APP_BASE_HREF, HashLocationStrategy, LocationStrategy } from '@angular/common';
|
||||
|
||||
import { PhoneDetailComponent } from './phone-detail/phone-detail.component';
|
||||
import { PhoneListComponent } from './phone-list/phone-list.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', redirectTo: 'phones', pathMatch: 'full' },
|
||||
{ path: 'phones', component: PhoneListComponent },
|
||||
{ path: 'phones/:phoneId', component: PhoneDetailComponent }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [ RouterModule.forRoot(routes) ],
|
||||
exports: [ RouterModule ],
|
||||
providers: [
|
||||
{ provide: APP_BASE_HREF, useValue: '!' },
|
||||
{ provide: LocationStrategy, useClass: HashLocationStrategy },
|
||||
]
|
||||
})
|
||||
export class AppRoutingModule {}
|
|
@ -1,18 +1,9 @@
|
|||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
import { RouteConfig, ROUTER_DIRECTIVES } from '@angular/router-deprecated';
|
||||
import { PhoneListComponent } from './phone-list/phone-list.component';
|
||||
import { PhoneDetailComponent } from './phone-detail/phone-detail.component';
|
||||
|
||||
@RouteConfig([
|
||||
{path: '/phones', name: 'Phones', component: PhoneListComponent},
|
||||
{path: '/phones/:phoneId', name: 'Phone', component: PhoneDetailComponent},
|
||||
{path: '/', redirectTo: ['Phones']}
|
||||
])
|
||||
@Component({
|
||||
selector: 'phonecat-app',
|
||||
template: '<router-outlet></router-outlet>',
|
||||
directives: [ROUTER_DIRECTIVES]
|
||||
template: '<router-outlet></router-outlet>'
|
||||
})
|
||||
export class AppComponent {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
// #docregion
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpModule } from '@angular/http';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { CheckmarkPipe } from './core/checkmark/checkmark.pipe';
|
||||
import { Phone } from './core/phone/phone.service';
|
||||
import { PhoneDetailComponent } from './phone-detail/phone-detail.component';
|
||||
import { PhoneListComponent } from './phone-list/phone-list.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpModule,
|
||||
AppRoutingModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
PhoneListComponent,
|
||||
CheckmarkPipe,
|
||||
PhoneDetailComponent
|
||||
],
|
||||
providers: [
|
||||
Phone,
|
||||
],
|
||||
// #docregion bootstrap
|
||||
bootstrap: [ AppComponent ]
|
||||
// #enddocregion bootstrap
|
||||
})
|
||||
export class AppModule {}
|
|
@ -1,21 +1,10 @@
|
|||
import {
|
||||
describe,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
inject,
|
||||
expect
|
||||
} from '@angular/core/testing';
|
||||
import { CheckmarkPipe } from './checkmark.pipe';
|
||||
|
||||
describe('CheckmarkPipe', function() {
|
||||
|
||||
beforeEachProviders(() => [CheckmarkPipe]);
|
||||
|
||||
it('should convert boolean values to unicode checkmark or cross',
|
||||
inject([CheckmarkPipe], function(checkmarkPipe: CheckmarkPipe) {
|
||||
expect(checkmarkPipe.transform(true)).toBe('\u2713');
|
||||
expect(checkmarkPipe.transform(false)).toBe('\u2718');
|
||||
})
|
||||
);
|
||||
|
||||
it('should convert boolean values to unicode checkmark or cross', function () {
|
||||
const checkmarkPipe = new CheckmarkPipe();
|
||||
expect(checkmarkPipe.transform(true)).toBe('\u2713');
|
||||
expect(checkmarkPipe.transform(false)).toBe('\u2718');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
import {
|
||||
describe,
|
||||
beforeEach,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
inject
|
||||
} from '@angular/core/testing';
|
||||
import { inject, TestBed } from '@angular/core/testing';
|
||||
import {
|
||||
Http,
|
||||
BaseRequestOptions,
|
||||
|
@ -23,15 +17,19 @@ describe('Phone', function() {
|
|||
];
|
||||
let mockBackend: MockBackend;
|
||||
|
||||
beforeEachProviders(() => [
|
||||
Phone,
|
||||
MockBackend,
|
||||
BaseRequestOptions,
|
||||
{ provide: Http,
|
||||
useFactory: (backend: MockBackend, options: BaseRequestOptions) => new Http(backend, options),
|
||||
deps: [MockBackend, BaseRequestOptions]
|
||||
}
|
||||
]);
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
Phone,
|
||||
MockBackend,
|
||||
BaseRequestOptions,
|
||||
{ provide: Http,
|
||||
useFactory: (backend: MockBackend, options: BaseRequestOptions) => new Http(backend, options),
|
||||
deps: [MockBackend, BaseRequestOptions]
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(inject([MockBackend, Phone], (_mockBackend_: MockBackend, _phone_: Phone) => {
|
||||
mockBackend = _mockBackend_;
|
||||
|
|
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 28 KiB |
|
@ -1,27 +1,10 @@
|
|||
// #docregion
|
||||
// #docregion imports
|
||||
import {
|
||||
LocationStrategy,
|
||||
HashLocationStrategy,
|
||||
APP_BASE_HREF
|
||||
} from '@angular/common';
|
||||
import { bootstrap } from '@angular/platform-browser-dynamic';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HTTP_PROVIDERS } from '@angular/http';
|
||||
import { ROUTER_PROVIDERS } from '@angular/router-deprecated';
|
||||
import { Phone } from './core/phone/phone.service';
|
||||
import { AppComponent } from './app.component';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
// #enddocregion imports
|
||||
|
||||
// #docregion bootstrap
|
||||
bootstrap(AppComponent, {
|
||||
imports: [FormsModule],
|
||||
providers: [
|
||||
HTTP_PROVIDERS,
|
||||
ROUTER_PROVIDERS,
|
||||
{ provide: APP_BASE_HREF, useValue: '!' },
|
||||
{ provide: LocationStrategy, useClass: HashLocationStrategy },
|
||||
Phone
|
||||
]
|
||||
});
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
// #enddocregion bootstrap
|
||||
|
|
|
@ -1,25 +1,15 @@
|
|||
// #docregion
|
||||
import { HTTP_PROVIDERS } from '@angular/http';
|
||||
// #docregion routeparams
|
||||
import { RouteParams } from '@angular/router-deprecated';
|
||||
// #docregion activatedroute
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
// #enddocregion routeparams
|
||||
// #enddocregion activatedroute
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
|
||||
import {
|
||||
describe,
|
||||
beforeEachProviders,
|
||||
inject,
|
||||
it,
|
||||
expect
|
||||
} from '@angular/core/testing';
|
||||
import {
|
||||
TestComponentBuilder,
|
||||
ComponentFixture
|
||||
} from '@angular/compiler/testing';
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PhoneDetailComponent } from './phone-detail.component';
|
||||
import { Phone, PhoneData } from '../core/phone/phone.service';
|
||||
import { CheckmarkPipe } from '../core/checkmark/checkmark.pipe';
|
||||
|
||||
function xyzPhoneData(): PhoneData {
|
||||
return {
|
||||
|
@ -29,31 +19,41 @@ function xyzPhoneData(): PhoneData {
|
|||
};
|
||||
}
|
||||
|
||||
class MockPhone extends Phone {
|
||||
class MockPhone {
|
||||
get(id: string): Observable<PhoneData> {
|
||||
return Observable.of(xyzPhoneData());
|
||||
}
|
||||
}
|
||||
|
||||
// #docregion activatedroute
|
||||
|
||||
class ActivatedRouteMock {
|
||||
constructor(public snapshot: any) {}
|
||||
}
|
||||
|
||||
// #enddocregion activatedroute
|
||||
|
||||
describe('PhoneDetailComponent', () => {
|
||||
|
||||
// #docregion routeparams
|
||||
// #docregion activatedroute
|
||||
|
||||
beforeEachProviders(() => [
|
||||
{ provide: Phone, useClass: MockPhone },
|
||||
{ provide: RouteParams, useValue: new RouteParams({phoneId: 'xyz'})},
|
||||
HTTP_PROVIDERS
|
||||
]);
|
||||
// #enddocregion routeparams
|
||||
|
||||
it('should fetch phone detail',
|
||||
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
return tcb.createAsync(PhoneDetailComponent)
|
||||
.then((fixture: ComponentFixture<PhoneDetailComponent>) => {
|
||||
fixture.detectChanges();
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('h1')).toHaveText(xyzPhoneData().name);
|
||||
});
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ CheckmarkPipe, PhoneDetailComponent ],
|
||||
providers: [
|
||||
{ provide: Phone, useClass: MockPhone },
|
||||
{ provide: ActivatedRoute, useValue: new ActivatedRouteMock({ params: { 'phoneId': 1 } }) }
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
// #enddocregion activatedroute
|
||||
|
||||
it('should fetch phone detail', () => {
|
||||
const fixture = TestBed.createComponent(PhoneDetailComponent);
|
||||
fixture.detectChanges();
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('h1').textContent).toContain(xyzPhoneData().name);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
// #docplaster
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
import { RouteParams } from '@angular/router-deprecated';
|
||||
import { Component } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { Phone, PhoneData } from '../core/phone/phone.service';
|
||||
import { CheckmarkPipe } from '../core/checkmark/checkmark.pipe';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'phone-detail',
|
||||
templateUrl: 'phone-detail.template.html',
|
||||
pipes: [ CheckmarkPipe ]
|
||||
templateUrl: 'phone-detail.template.html'
|
||||
})
|
||||
export class PhoneDetailComponent {
|
||||
phone: PhoneData;
|
||||
mainImageUrl: string;
|
||||
|
||||
constructor(routeParams: RouteParams, phone: Phone) {
|
||||
phone.get(routeParams.get('phoneId')).subscribe(phone => {
|
||||
this.phone = phone;
|
||||
this.setImage(phone.images[0]);
|
||||
});
|
||||
constructor(activatedRoute: ActivatedRoute, phone: Phone) {
|
||||
phone.get(activatedRoute.snapshot.params['phoneId'])
|
||||
.subscribe((p: PhoneData) => {
|
||||
this.phone = p;
|
||||
this.setImage(p.images[0]);
|
||||
});
|
||||
}
|
||||
|
||||
setImage(imageUrl: string) {
|
||||
|
|
|
@ -1,42 +1,21 @@
|
|||
/* tslint:disable */
|
||||
// #docregion routestuff
|
||||
import { Directive } from '@angular/core';
|
||||
import { HTTP_PROVIDERS } from '@angular/http';
|
||||
import {
|
||||
Router,
|
||||
RouterLink,
|
||||
RootRouter,
|
||||
RouteRegistry,
|
||||
ROUTER_PRIMARY_COMPONENT
|
||||
} from '@angular/router-deprecated';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
import {
|
||||
describe,
|
||||
addProviders,
|
||||
inject,
|
||||
it,
|
||||
expect,
|
||||
// MockApplicationRef
|
||||
} from '@angular/core/testing';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { SpyLocation } from '@angular/common/testing';
|
||||
import {
|
||||
TestComponentBuilder,
|
||||
ComponentFixture
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { AppComponent } from '../app.component';
|
||||
import { PhoneListComponent } from './phone-list.component';
|
||||
import { Phone, PhoneData } from '../core/phone/phone.service';
|
||||
|
||||
// #enddocregion routestuff
|
||||
|
||||
@Directive({
|
||||
selector: '[routerLink]',
|
||||
inputs: ['routeParams: routerLink', 'target: target']
|
||||
})
|
||||
class RouterLinkMock {}
|
||||
class ActivatedRouteMock {
|
||||
constructor(public snapshot: any) {}
|
||||
}
|
||||
|
||||
class MockPhone extends Phone {
|
||||
class MockPhone {
|
||||
query(): Observable<PhoneData[]> {
|
||||
return Observable.of([
|
||||
{name: 'Nexus S', snippet: '', images: []},
|
||||
|
@ -45,52 +24,48 @@ class MockPhone extends Phone {
|
|||
}
|
||||
}
|
||||
|
||||
let fixture: ComponentFixture<PhoneListComponent>;
|
||||
|
||||
describe('PhoneList', () => {
|
||||
|
||||
// #docregion routestuff
|
||||
|
||||
addProviders([
|
||||
RouteRegistry,
|
||||
{ provide: Router, useClass: RootRouter },
|
||||
{ provide: ROUTER_PRIMARY_COMPONENT, useValue: AppComponent },
|
||||
{ provide: Location, useClass: SpyLocation},
|
||||
{ provide: Phone, useClass: MockPhone},
|
||||
HTTP_PROVIDERS
|
||||
]);
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ PhoneListComponent ],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: new ActivatedRouteMock({ params: { 'phoneId': 1 } }) },
|
||||
{ provide: Location, useClass: SpyLocation },
|
||||
{ provide: Phone, useClass: MockPhone },
|
||||
],
|
||||
schemas: [ NO_ERRORS_SCHEMA ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PhoneListComponent);
|
||||
});
|
||||
// #enddocregion routestuff
|
||||
|
||||
it('should create "phones" model with 2 phones fetched from xhr',
|
||||
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
return tcb
|
||||
.overrideDirective(AppComponent, RouterLink, RouterLinkMock)
|
||||
.overrideDirective(PhoneListComponent, RouterLink, RouterLinkMock)
|
||||
.createAsync(PhoneListComponent)
|
||||
.then((fixture: ComponentFixture<PhoneListComponent>) => {
|
||||
fixture.detectChanges();
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelectorAll('.phone-list-item').length).toBe(2);
|
||||
expect(
|
||||
compiled.querySelector('.phone-list-item:nth-child(1)').textContent
|
||||
).toContain('Motorola DROID');
|
||||
expect(
|
||||
compiled.querySelector('.phone-list-item:nth-child(2)').textContent
|
||||
).toContain('Nexus S');
|
||||
});
|
||||
}));
|
||||
it('should create "phones" model with 2 phones fetched from xhr', () => {
|
||||
fixture.detectChanges();
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelectorAll('.phone-list-item').length).toBe(2);
|
||||
expect(
|
||||
compiled.querySelector('.phone-list-item:nth-child(1)').textContent
|
||||
).toContain('Motorola DROID');
|
||||
expect(
|
||||
compiled.querySelector('.phone-list-item:nth-child(2)').textContent
|
||||
).toContain('Nexus S');
|
||||
});
|
||||
|
||||
it('should set the default value of orderProp model',
|
||||
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
return tcb
|
||||
.overrideDirective(AppComponent, RouterLink, RouterLinkMock)
|
||||
.overrideDirective(PhoneListComponent, RouterLink, RouterLinkMock)
|
||||
.createAsync(PhoneListComponent)
|
||||
.then((fixture: ComponentFixture<PhoneListComponent>) => {
|
||||
fixture.detectChanges();
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
expect(
|
||||
compiled.querySelector('select option:last-child').selected
|
||||
).toBe(true);
|
||||
});
|
||||
}));
|
||||
xit('should set the default value of orderProp model', () => {
|
||||
fixture.detectChanges();
|
||||
let compiled = fixture.debugElement.nativeElement;
|
||||
expect(
|
||||
compiled.querySelector('select option:last-child').selected
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
// #docplaster
|
||||
// #docregion top
|
||||
import { Component } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router-deprecated';
|
||||
|
||||
import { Phone, PhoneData } from '../core/phone/phone.service';
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'phone-list',
|
||||
templateUrl: 'phone-list.template.html',
|
||||
directives: [ RouterLink ]
|
||||
})
|
||||
// #enddocregion top
|
||||
export class PhoneListComponent {
|
||||
|
@ -41,20 +40,20 @@ export class PhoneListComponent {
|
|||
}
|
||||
|
||||
private sortPhones(phones: PhoneData[]) {
|
||||
if (phones && this.orderProp) {
|
||||
return phones
|
||||
.slice(0) // Make a copy
|
||||
.sort((a, b) => {
|
||||
if (a[this.orderProp] < b[this.orderProp]) {
|
||||
return -1;
|
||||
} else if ([b[this.orderProp] < a[this.orderProp]]) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
return phones;
|
||||
if (phones && this.orderProp) {
|
||||
return phones
|
||||
.slice(0) // Make a copy
|
||||
.sort((a, b) => {
|
||||
if (a[this.orderProp] < b[this.orderProp]) {
|
||||
return -1;
|
||||
} else if ([b[this.orderProp] < a[this.orderProp]]) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
return phones;
|
||||
}
|
||||
// #enddocregion getphones
|
||||
// #docregion initialclass
|
||||
|
|
|
@ -26,10 +26,10 @@
|
|||
<ul class="phones">
|
||||
<li *ngFor="let phone of getPhones()"
|
||||
class="thumbnail phone-list-item">
|
||||
<a [routerLink]="['/Phone', {phoneId: phone.id}]" class="thumb">
|
||||
<a [routerLink]="['/phones', phone.id]" class="thumb">
|
||||
<img [src]="phone.imageUrl" [alt]="phone.name" />
|
||||
</a>
|
||||
<a [routerLink]="['/Phone', {phoneId: phone.id}]" class="name">{{phone.name}}</a>
|
||||
<a [routerLink]="['/phones', phone.id]" class="name">{{phone.name}}</a>
|
||||
<p>{{phone.snippet}}</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -63,11 +63,10 @@ module.exports = function(config) {
|
|||
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
browsers: ['Chrome', 'Firefox'],
|
||||
browsers: ['Chrome'],
|
||||
|
||||
plugins: [
|
||||
'karma-chrome-launcher',
|
||||
'karma-firefox-launcher',
|
||||
'karma-jasmine'
|
||||
]
|
||||
|
||||
|
|
|
@ -1,59 +1,47 @@
|
|||
/**
|
||||
* System configuration for Angular 2 samples
|
||||
* System configuration for Angular samples
|
||||
* Adjust as necessary for your application needs.
|
||||
*/
|
||||
(function(global) {
|
||||
|
||||
// map tells the System loader where to look for things
|
||||
(function (global) {
|
||||
// #docregion paths
|
||||
var map = {
|
||||
'app': '/app', // 'dist',
|
||||
|
||||
'@angular': '/node_modules/@angular',
|
||||
'angular-in-memory-web-api': '/node_modules/angular-in-memory-web-api',
|
||||
'rxjs': '/node_modules/rxjs'
|
||||
};
|
||||
|
||||
var packages = {
|
||||
'/app': { main: 'main.js', defaultExtension: 'js' },
|
||||
'rxjs': { defaultExtension: 'js' },
|
||||
'angular-in-memory-web-api': { main: 'index.js', defaultExtension: 'js' },
|
||||
};
|
||||
// #enddocregion paths
|
||||
|
||||
var ngPackageNames = [
|
||||
'common',
|
||||
'compiler',
|
||||
'core',
|
||||
'forms',
|
||||
'http',
|
||||
'platform-browser',
|
||||
'platform-browser-dynamic',
|
||||
'router',
|
||||
'router-deprecated',
|
||||
'upgrade',
|
||||
];
|
||||
|
||||
// Individual files (~300 requests):
|
||||
function packIndex(pkgName) {
|
||||
packages['@angular/'+pkgName] = { main: 'index.js', defaultExtension: 'js' };
|
||||
}
|
||||
|
||||
// Bundled (~40 requests):
|
||||
function packUmd(pkgName) {
|
||||
packages['@angular/'+pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };
|
||||
}
|
||||
|
||||
var setPackageConfig = System.packageWithIndex ? packIndex : packUmd;
|
||||
|
||||
// Add package entries for angular packages
|
||||
ngPackageNames.forEach(setPackageConfig);
|
||||
|
||||
var config = {
|
||||
map: map,
|
||||
packages: packages
|
||||
}
|
||||
|
||||
System.config(config);
|
||||
System.config({
|
||||
paths: {
|
||||
// paths serve as alias
|
||||
'npm:': '/node_modules/'
|
||||
},
|
||||
map: {
|
||||
app: '/app',
|
||||
// #enddocregion paths
|
||||
// angular bundles
|
||||
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
|
||||
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
|
||||
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
|
||||
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
|
||||
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
|
||||
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
|
||||
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
|
||||
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
|
||||
'@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
|
||||
|
||||
// other libraries
|
||||
'rxjs': 'npm:rxjs',
|
||||
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api',
|
||||
// #docregion paths
|
||||
},
|
||||
// #enddocregion paths
|
||||
// packages tells the System loader how to load when no filename and/or no extension
|
||||
packages: {
|
||||
'app': {
|
||||
main: './main.js',
|
||||
defaultExtension: 'js'
|
||||
},
|
||||
rxjs: {
|
||||
defaultExtension: 'js'
|
||||
},
|
||||
'angular-in-memory-web-api': {
|
||||
main: './index.js',
|
||||
defaultExtension: 'js'
|
||||
}
|
||||
}
|
||||
});
|
||||
})(this);
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
include ../_util-fns
|
||||
|
||||
.alert.is-important
|
||||
:marked
|
||||
This guide is still in the process of being updated to RC5 and it's samples
|
||||
may not work correctly.
|
||||
|
||||
:marked
|
||||
Having an existing Angular 1 application doesn't mean that we can't
|
||||
begin enjoying everything Angular 2 has to offer. That's because Angular 2
|
||||
|
@ -28,13 +23,13 @@ include ../_util-fns
|
|||
make incremental upgrading seamless.
|
||||
|
||||
1. [Preparation](#preparation)
|
||||
1. [Following The Angular Style Guide](#following-the-angular-style-guide)
|
||||
1. [Follow the Angular Style Guide](#follow-the-angular-style-guide)
|
||||
2. [Using a Module Loader](#using-a-module-loader)
|
||||
3. [Migrating to TypeScript](#migrating-to-typescript)
|
||||
4. [Using Component Directives](#using-component-directives)
|
||||
2. [Upgrading with The Upgrade Adapter](#upgrading-with-the-upgrade-adapter)
|
||||
1. [How The Upgrade Adapter Works](#how-the-upgrade-adapter-works)
|
||||
2. [Bootstrapping Hybrid Angular 1+2 Applications](#bootstrapping-hybrid-angular-1-2-applications)
|
||||
2. [Bootstrapping hybrid Angular 1+2 Applications](#bootstrapping-hybrid-angular-1-2-applications)
|
||||
3. [Using Angular 2 Components from Angular 1 Code](#using-angular-2-components-from-angular-1-code)
|
||||
4. [Using Angular 1 Component Directives from Angular 2 Code](#using-angular-1-component-directives-from-angular-2-code)
|
||||
5. [Projecting Angular 1 Content into Angular 2 Components](#projecting-angular-1-content-into-angular-2-components)
|
||||
|
@ -44,7 +39,7 @@ include ../_util-fns
|
|||
3. [PhoneCat Upgrade Tutorial](#phonecat-upgrade-tutorial)
|
||||
1. [Switching to TypeScript](#switching-to-typescript)
|
||||
2. [Installing Angular 2](#installing-angular-2)
|
||||
3. [Bootstrapping A Hybrid 1+2 PhoneCat](#bootstrapping-a-hybrid-1-2-phonecat)
|
||||
3. [Bootstrapping a hybrid 1+2 PhoneCat](#bootstrapping-a-hybrid-1-2-phonecat)
|
||||
4. [Upgrading the Phone service](#upgrading-the-phone-service)
|
||||
5. [Upgrading Components](#upgrading-components)
|
||||
6. [Switching To The Angular 2 Router And Bootstrap](#switching-to-the-angular-2-router-and-bootstrap)
|
||||
|
@ -61,7 +56,7 @@ include ../_util-fns
|
|||
and patterns that we can apply to future proof our apps even before we
|
||||
begin the migration.
|
||||
|
||||
## Following The Angular Style Guide
|
||||
## Follow the Angular Style Guide
|
||||
|
||||
The [Angular 1 Style Guide](https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#single-responsibility)
|
||||
collects patterns and practices that have been proven to result in
|
||||
|
@ -84,6 +79,7 @@ include ../_util-fns
|
|||
components easy to navigate and find, but will also allow us to migrate
|
||||
them between languages and frameworks one at a time. In this example application,
|
||||
each controller, component, service, and filter is in its own source file.
|
||||
|
||||
* The [Folders-by-Feature Structure](https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#folders-by-feature-structure)
|
||||
and [Modularity](https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#modularity)
|
||||
rules define similar principles on a higher level of abstraction: Different parts of the
|
||||
|
@ -390,6 +386,26 @@ figure.image-display
|
|||
as regular Angular 2 inputs and set onto the scope (or controller) when
|
||||
they change.
|
||||
|
||||
## Using the Upgrade Adapter with Angular 2 _NgModules_
|
||||
|
||||
Both Angular 1 and Angular 2 have their own concept of modules
|
||||
to help organize an application into cohesive blocks of funcionality.
|
||||
|
||||
Their details are quite different in architecture and implementation.
|
||||
In Angular 1, you add Angular assets to the `angular.module` property.
|
||||
In Angular 2, you create one or more classes adorned with an `NgModule` decorator
|
||||
that describes Angular assets in metadata. The differences blossom from there.
|
||||
|
||||
In a hybrid application we run both versions of Angular at the same time.
|
||||
That means that we need at least one module each from both Angular 1 and Angular 2.
|
||||
We will give the Angular 2 module to the `UpgradeAdapter` while we use the
|
||||
Angular 1 module for bootstrapping. Let's see how.
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
Learn more about Angular 2 modules at the [NgModule guide](ngmodule.html).
|
||||
|
||||
:marked
|
||||
## Bootstrapping Hybrid Angular 1+2 Applications
|
||||
|
||||
The first step to upgrading an application using the `UpgradeAdapter` is
|
||||
|
@ -416,28 +432,30 @@ figure.image-display
|
|||
+makeExample('upgrade-adapter/ts/app/1-bootstrap/app.module.ts', 'bootstrap')
|
||||
|
||||
:marked
|
||||
To then switch the application into hybrid mode, we must first
|
||||
install Angular 2 to the project. Follow the instructions in
|
||||
[the QuickStart](../quickstart.html) for some pointers on this.
|
||||
When we have Angular 2 installed, we can import and instantiate
|
||||
the `UpgradeAdapter`, and then call its `bootstrap` method. It
|
||||
is designed to take the exact same arguments as
|
||||
[angular.bootstrap](https://docs.angularjs.org/api/ng/function/angular.bootstrap)
|
||||
so that it is easy to make the switch:
|
||||
Now introduce Angular 2 to the project. Inspired by instructions in
|
||||
[the QuickStart](../quickstart.html), you can selectively copy in material from the
|
||||
<a href="https://github.com/angular/quickstart" target="_blank">QuickStart github repository</a>.
|
||||
|
||||
Next, create an `app.module.ts` file and add the following `NgModule` class:
|
||||
|
||||
+makeExample('upgrade-adapter/ts/app/1-2-hybrid-bootstrap/app.module.ts', 'ngmodule')
|
||||
|
||||
:marked
|
||||
This bare minimum `NgModule` imports `BrowserModule`, the module every Angular browser-based app must have.
|
||||
|
||||
Import and instantiate the `UpgradeAdapter` with the new `AppModule` and call its `bootstrap` method.
|
||||
That method takes the exact same arguments as [angular.bootstrap](https://docs.angularjs.org/api/ng/function/angular.bootstrap):
|
||||
|
||||
+makeExample('upgrade-adapter/ts/app/1-2-hybrid-bootstrap/app.module.ts', 'bootstrap')
|
||||
|
||||
:marked
|
||||
At this point we'll be running a hybrid Angular 1+2 application! All the
|
||||
existing Angular 1 code will work as it always did, but we are now ready
|
||||
to run Angular 2 code as well.
|
||||
Congratulations! You're running a hybrid Angular 1+2 application! The
|
||||
existing Angular 1 code works as before _and_ you're ready to run Angular 2 code.
|
||||
|
||||
.alert.is-helpful
|
||||
:marked
|
||||
One notable difference between `angular.bootstrap` and
|
||||
`upgradeAdapter.bootstrap` is that the latter works *asynchronously*.
|
||||
This means that we cannot assume that the application has been instantiated
|
||||
immediately after the bootstrap call returns.
|
||||
Note that, unlike `angular.bootstrap`, the `upgradeAdapter.bootstrap` runs *asynchronously*.
|
||||
The application is not launched immediately. Some time must pass after the bootstrap call returns.
|
||||
|
||||
:marked
|
||||
As we begin to migrate components to Angular 2, we'll be using the
|
||||
|
@ -476,16 +494,25 @@ figure
|
|||
+makeExample('upgrade-adapter/ts/app/downgrade-static/app.module.ts', 'downgradecomponent')
|
||||
|
||||
:marked
|
||||
What we have here is an Angular 1 directive called `heroDetail`, which we can
|
||||
Because `HeroDetailComponent` is an Angular 2 component, we must also add it to the `declarations` in the `AppModule`.
|
||||
|
||||
+makeExample('upgrade-adapter/ts/app/downgrade-static/app.module.ts', 'ngmodule')
|
||||
.l-sub-section
|
||||
:marked
|
||||
All Angular 2 components, directives and pipes must be declared in an NgModule.
|
||||
|
||||
:marked
|
||||
The net resulit is an Angular 1 directive called `heroDetail`, that we can
|
||||
use like any other directive in our Angular 1 templates.
|
||||
|
||||
+makeExample('upgrade-adapter/ts/index-downgrade-static.html', 'usecomponent')
|
||||
|
||||
.alert.is-helpful
|
||||
:marked
|
||||
Note that since Angular 1 directives are matched based on their name,
|
||||
*the selector metadata of the Angular 2 component is not used in Angular 1*.
|
||||
It is matched as an element directive (`restrict: 'E'`) called `heroDetail`.
|
||||
Note that this Angular 1 is an element directive (`restrict: 'E'`) called `heroDetail`.
|
||||
An Angular 1 element directive is matched based on its _name_.
|
||||
*The `selector` metadata of the downgraded Angular 2 component is ignored.*
|
||||
|
||||
|
||||
:marked
|
||||
Most components are not quite this simple, of course. Many of them
|
||||
|
@ -558,11 +585,10 @@ figure
|
|||
:marked
|
||||
We can *upgrade* this component to Angular 2 using the `UpgradeAdapter`'s
|
||||
`upgradeNg1Component` method. It takes the name of an Angular 1 component
|
||||
directive and returns an Angular 2 **component class**. When we then
|
||||
want to use it from an Angular 2 component, we list it the in the `directives`
|
||||
metadata of the component and then just use it in the Angular 2 template:
|
||||
directive and returns an Angular 2 **component class**.
|
||||
Declare it in an `NgModule` as with other Angular 2 components:
|
||||
|
||||
+makeExample('upgrade-adapter/ts/app/upgrade-static/container.component.ts', null, 'container.component.ts')
|
||||
+makeExample('upgrade-adapter/ts/app/upgrade-static/upgrade_adapter.ts', 'heroupgrade', 'app.module.ts')
|
||||
|
||||
.alert.is-helpful
|
||||
:marked
|
||||
|
@ -731,17 +757,14 @@ figure
|
|||
+makeExample('upgrade-adapter/ts/app/2-to-1-providers/heroes.ts', null, 'heroes.ts')
|
||||
|
||||
:marked
|
||||
We can again use the `UpgradeAdapter` for this, but first we need to register `Heroes`
|
||||
to the Angular 2 injector itself. In a pure Angular 2 application we would do this
|
||||
when we bootstrap the app, as described in the [dependency injection guide](dependency-injection.html#!#providers).
|
||||
But since hybrid applications are bootstrapped using the `UpgradeAdapter`, we also
|
||||
need to register our Angular 2 providers using `UpgradeAdapter`. It has a method
|
||||
called `addProvider` for this purpose.
|
||||
Again, as with Angular 2 components, register the provider with the `NgModule` by adding it to the module's `providers` list.
|
||||
|
||||
Once we've registered the Angular 2 provider, we can turn `Heroes` into an *Angular 1
|
||||
factory function* using `upgradeAdapter.downgradeNg2Provider()`. We can
|
||||
then plug the factory into an Angular 1 module, at which point we also choose what the
|
||||
name of the dependency will be in Angular 1:
|
||||
+makeExample('upgrade-adapter/ts/app/2-to-1-providers/upgrade_adapter.ts', 'ngmodule', 'app.module.ts')
|
||||
|
||||
:marked
|
||||
Now wrap the Angular 2 `Heroes` in an *Angular 1 factory function* using `upgradeAdapter.downgradeNg2Provider()`.
|
||||
and plug the factory into an Angular 1 module.
|
||||
The name of the Angular 1 dependency is up to you:
|
||||
|
||||
+makeExample('upgrade-adapter/ts/app/2-to-1-providers/app.module.ts', 'register', 'app.module.ts')
|
||||
|
||||
|
@ -1033,7 +1056,7 @@ code-example(format="").
|
|||
development server root path in `package.json` to also point to the project root
|
||||
instead of `app`:
|
||||
|
||||
+makeJson('upgrade-phonecat-2-hybrid/ts/package.ng1.json', {paths: 'scripts.start'}, 'package.json')
|
||||
+makeJson('upgrade-phonecat-2-hybrid/ts/package.ng1.json', {paths: 'scripts.start'}, 'package.json (start script)')
|
||||
|
||||
:marked
|
||||
Now we're able to serve everything from the project root to the web browser. But we do *not*
|
||||
|
@ -1058,7 +1081,22 @@ code-example(format="").
|
|||
+makeExample('upgrade-phonecat-2-hybrid/ts/systemjs.config.1.js', 'paths', 'systemjs.config.js')
|
||||
|
||||
:marked
|
||||
## Bootstrapping A Hybrid 1+2 PhoneCat
|
||||
## Creating the _AppModule_
|
||||
|
||||
Now create the root `NgModule` class called `AppModule`.
|
||||
There is already a file named `app.module.ts` that holds the Angular 1 module.
|
||||
Rename it to `app.module.ng1.ts` and update the corresponding script name in the `index.html` as well.
|
||||
The file contents remain:
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/app.module.ng1.ts', null, 'app.module.ng1.ts')
|
||||
|
||||
:marked
|
||||
Now create a new `app.module.ts` with the minimum `NgModule` class:
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/app.module.ts', 'bare', 'app.module.ts')
|
||||
|
||||
:marked
|
||||
## Bootstrapping a hybrid 1+2 PhoneCat
|
||||
|
||||
What we'll do next is bootstrap the application as a *hybrid application*
|
||||
that supports both Angular 1 and Angular 2 components. Once we've done that
|
||||
|
@ -1109,32 +1147,19 @@ code-example(format="").
|
|||
* For loading the details of a single phone into the phone detail component.
|
||||
|
||||
We can replace this implementation with an Angular 2 service class, while
|
||||
keeping our controllers in Angular 1 land. In the new version we'll just use
|
||||
the `Http` service from Angular 2 instead of ngResource.
|
||||
keeping our controllers in Angular 1 land.
|
||||
|
||||
Before the `Http` service is available for injection, we need to register
|
||||
it into our application's dependency injector. We should import the `HTTP_PROVIDERS`
|
||||
constant in `main.ts`:
|
||||
In the new version, we import the Angular 2 HTTP module and call its `Http` service instead of `ngResource`.
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/main.ts', 'import-http')
|
||||
Re-open the `app.module.ts` file, import and add `HttpModule` to the `imports` array of the `AppModule`:
|
||||
|
||||
:marked
|
||||
In a regular Angular 2 application we would now pass `HTTP_PROVIDERS` into
|
||||
the application bootstrap function. But we can't do that in a hybrid
|
||||
application such as the one we're working on. That's because the `bootstrap`
|
||||
method of `UpgradeAdapter` expects Angular 1 modules as dependencies,
|
||||
not Angular 2 providers.
|
||||
|
||||
What we must do instead is register `HTTP_PROVIDERS` into the `UpgradeAdapter`
|
||||
separately. It has a method called `addProvider` for that purpose:
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/main.ts', 'add-http')
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/app.module.ts', 'httpmodule', 'app.module.ts')
|
||||
|
||||
:marked
|
||||
Now we're ready to upgrade the Phone service itself. We replace the ngResource-based
|
||||
service in `phone.service.ts` with a TypeScript class decorated as `@Injectable`:
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/core/phone/phone.service.ts', 'classdef', 'app/core/phone/phone.service.ts')
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/core/phone/phone.service.ts', 'classdef', 'app/core/phone/phone.service.ts (skeleton)')(format='.')
|
||||
|
||||
:marked
|
||||
The `@Injectable` decorator will attach some dependency injection metadata
|
||||
|
@ -1154,7 +1179,7 @@ code-example(format="").
|
|||
The methods now return Observables of type `PhoneData` and `PhoneData[]`. This is
|
||||
a type we don't have yet, so let's add a simple interface for it:
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/core/phone/phone.service.ts', 'phonedata-interface', 'app/core/phone/phone.service.ts')
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/core/phone/phone.service.ts', 'phonedata-interface', 'app/core/phone/phone.service.ts (interface)')(format='.')
|
||||
|
||||
:marked
|
||||
Here's the full, final code for the service:
|
||||
|
@ -1166,24 +1191,17 @@ code-example(format="").
|
|||
We need to do this for all RxJS operators that we want to use, since Angular 2
|
||||
does not load all of them by default.
|
||||
|
||||
The new `Phone` service now has the same features that the original, ngResource based
|
||||
service did. Now we just need to register the new service into the application, so that
|
||||
our Angular 1 components will be able to use it.
|
||||
The new `Phone` service has the same features as the original, `ngResource`-based service.
|
||||
Because it's an Angular 2 service, we register it with the `NgModule` providers:
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/app.module.ts', 'phone', 'app.module.ts')
|
||||
:marked
|
||||
`UpgradeAdapter` has a `downgradeNg2Provider` method for the purpose of making
|
||||
Angular 2 services available to Angular 1 code. We can use it to plug in our
|
||||
`Phone` service:
|
||||
Angular 2 services available to Angular 1 code. Use it to plug in the `Phone` service:
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/main.ts', 'phone-service', 'app/main.ts')
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/main.ts', 'phone-service', 'app/main.ts (excerpt)')(format='.')
|
||||
|
||||
:marked
|
||||
Note that we actually needed to do two registrations here:
|
||||
|
||||
1. Register `Phone` as an **Angular 2 provider** with the `addProvider`
|
||||
method. That's the same method that we used earlier for `HTTP_PROVIDERS`.
|
||||
2. Register an **Angular 1 factory** called `phone`, which will be a *downgraded*
|
||||
version of the `Phone` Angular 2 service.
|
||||
|
||||
Now that we are loading `phone.service.ts` through an import that is resolved
|
||||
by SystemJS, we should **remove the <script> tag** for the service from `index.html`.
|
||||
This is something we'll do to all our components as we upgrade them. Simultaneously
|
||||
|
@ -1234,69 +1252,72 @@ code-example(format="").
|
|||
just like the Angular 1 version did.
|
||||
|
||||
We now also need to convert the template of this component into Angular 2 syntax.
|
||||
In the search controls we need to use Angular 2 syntax for the two `ngModel`s.
|
||||
We should also no longer use the `$ctrl` prefix in expressions:
|
||||
The search controls replace the Angular 1 `$ctrl` expressions
|
||||
with Angular 2's two-way `[(ngModel)]` binding syntax:
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/phone-list/phone-list.template.html', 'controls', 'app/phone-list/phone-list.template.html')
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/phone-list/phone-list.template.html', 'controls', 'app/phone-list/phone-list.template.html (search controls)')(format='.')
|
||||
|
||||
:marked
|
||||
In the list we need to replace the `ng-repeat` with an `*ngFor` and the
|
||||
`let var of iterable` syntax, which is [described in our
|
||||
Template Syntax guide](../guide/template-syntax.html#directives).
|
||||
For the images, we can replace `ng-src` with a binding to the standard `src` property.
|
||||
Replace the list's `ng-repeat` with an `*ngFor` as
|
||||
[described in the Template Syntax page](../guide/template-syntax.html#directives).
|
||||
Replace the image tag's `ng-src` with a binding to the native `src` property.
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/phone-list/phone-list.template.html', 'list', 'app/phone-list/phone-list.template.html')
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/phone-list/phone-list.template.html', 'list', 'app/phone-list/phone-list.template.html (phones)')(format='.')
|
||||
|
||||
:marked
|
||||
Another thing that we've done here is that we've removed the use of `filter` and `orderBy` filters,
|
||||
and replaced them with a call to the `getPhones()` controller method.
|
||||
The built-in Angular filters `filter` and `orderBy` do not exist in Angular 2,
|
||||
so we need to do the filtering and sorting ourselves. We could define our own Angular 2
|
||||
pipes for this purpose, but in this case it is more convenient to just implement the filtering
|
||||
and ordering logic in the component itself. We expect the `getPhones()` method to return a collection
|
||||
where the current filtering and ordering has been applied.
|
||||
### No Angular 2 _filter_ or _orderBy_ filters
|
||||
The built-in Angular 1 `filter` and `orderBy` filters do not exist in Angular 2,
|
||||
so we need to do the filtering and sorting ourselves.
|
||||
|
||||
We replaced the `filter` and `orderBy` filters with bindings to the `getPhones()` controller method,
|
||||
which implements the filtering and ordering logic inside the component itself.
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/phone-list/phone-list.component.ts', 'getphones', 'app/phone-list/phone-list.component.ts')
|
||||
|
||||
:marked
|
||||
In the entrypoint file `main.ts` we're going to plug this component into our application. Instead
|
||||
of registering a component, we register a `phoneList` *directive*.
|
||||
The directive is a downgraded version of our Angular 2 component, and the `UpgradeAdapter`
|
||||
handles the bridging between the two:
|
||||
The new `PhoneListComponent` uses the Angular 2 `ngModel` directive, located in the `FormsModule`.
|
||||
Add the `FormsModule` to `NgModule` imports and declare the new `PhoneListComponent` :
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/main.ts', 'phone-list', 'app/main.ts')
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/app.module.ts', 'phonelist', 'app.module.ts')
|
||||
|
||||
:marked
|
||||
The `<angular.IDirectiveFactory>` type annotation here is to let the TypeScript compiler
|
||||
know that the return value of the downgrade method call will be something that can be
|
||||
used as a directive factory.
|
||||
In the entrypoint file `main.ts` we'll plug this component into the Angular 1 module.
|
||||
|
||||
At this point, also remove the <script> tag for the phone list component from `index.html`.
|
||||
Instead of registering a component, we register a `phoneList` *directive*, a downgraded version of the Angular 2 component.
|
||||
The `UpgradeAdapter` creates the bridge between the two:
|
||||
|
||||
Now we can start looking at our other component, which is the one for
|
||||
the phone details. Set the contents of `phone-detail.component.ts` as follows:
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/main.ts', 'phone-list', 'app/main.ts (excerpt)')(format='.')
|
||||
|
||||
:marked
|
||||
The `as angular.IDirectiveFactory` cast tells the TypeScript compiler
|
||||
that the return value of the downgrade method is a directive factory.
|
||||
|
||||
Remove the <script> tag for the phone list component from `index.html`.
|
||||
|
||||
Now set the remaining `phone-detail.component.ts` as follows:
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/phone-detail/phone-detail.component.ts', 'initialclass', 'app/phone-detail/phone-detail.component.ts')
|
||||
|
||||
:marked
|
||||
This is pretty similar to what we did with the phone list. The one new change
|
||||
here is the use of `@Inject` for the `$routeParams` dependency. It tells the
|
||||
Angular 2 injector what this dependency should map to. We have a dependency called
|
||||
`$routeParams` in the Angular 1 injector, where it is provided by the Angular 1 router.
|
||||
That is what we were already using when `PhoneDetails` was still an Angular 1 controller.
|
||||
This is similar to the phone list component.
|
||||
The new wrinkle is the `@Inject` decorator that identifies the `$routeParams` dependency.
|
||||
|
||||
The things is though, Angular 1 dependencies are not made automatically available to
|
||||
Angular 2 components, so if we were to run this now, it would not work. We need to explicitly
|
||||
tell the `UpgradeAdapter` to upgrade `$routeParams` so that it is available for injection in
|
||||
Angular 2. We can do it in `main.ts`:
|
||||
The Angular 1 injector has an Angular 1 router dependency called `$routeParams`.
|
||||
which was injected into `PhoneDetails` when it was still an Angular 1 controller.
|
||||
We intend to inject it into the new `PhoneDetailsComponent`.
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/main.ts', 'routeparams', 'app/main.ts')
|
||||
Unfortunately, Angular 1 dependencies are not automatically available to Angular 2 components.
|
||||
We must use the `UpgradeAdapter` to make the `$routeParams` an Angular 2 provider.
|
||||
Do that in `main.ts`:
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/main.ts', 'routeparams', 'app/main.ts ($routeParms)')(format='.')
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
Do not register an upgraded Angular 1 provider in the `NgModule`.
|
||||
|
||||
:marked
|
||||
We now also need to convert the template of this component into Angular 2 syntax.
|
||||
Here is the new template in its entirety:
|
||||
Convert the phone detail component template into Angular 2 syntax as follows:
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/phone-detail/phone-detail.template.html', null, 'app/phone-detail/phone-detail.template.html')
|
||||
|
||||
|
@ -1320,118 +1341,135 @@ code-example(format="").
|
|||
when we try to refer to properties on undefined objects. We need to be explicit
|
||||
about cases where this is expected.
|
||||
|
||||
Add this component to the `NgModule` _declarations_:
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/app.module.ts', 'phonedetail', 'app.module.ts')
|
||||
|
||||
:marked
|
||||
In `main.ts` we'll now register a `phoneDetail` directive instead of a
|
||||
component. The directive is a downgraded version of the `PhoneDetail` Angular 2
|
||||
component.
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/main.ts', 'phone-detail', 'app/main.ts')
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/main.ts', 'phone-detail', 'app/main.ts (excerpt)')(format='.')
|
||||
|
||||
:marked
|
||||
We should now also remove the phone detail component <script> tag from `index.html`.
|
||||
|
||||
There's one additional step we need to take, which is to upgrade the
|
||||
`checkmark` filter that the template is using. We need an Angular 2
|
||||
pipe instead of an Angular 1 filter.
|
||||
### Add the _CheckmarkPipe_
|
||||
|
||||
While there is no upgrade method in the upgrade adapter for filters, we
|
||||
can just turn the filter function into a class that fulfills
|
||||
the contract for Angular 2 Pipes. The implementation is the same as before.
|
||||
It just comes in a different kind of packaging. While changing it, also
|
||||
rename the file to `checkmark.pipe.ts`:
|
||||
The Angular 1 directive had a `checkmark` _filter_.
|
||||
Turn that into an Angular 2 **pipe**.
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/core/checkmark/checkmark.pipe.ts', null, 'app/core/checkmark/checkmark.pipe.ts')
|
||||
There is no upgrade adapter method to convert filters into pipes.
|
||||
You won't miss it.
|
||||
It's easy to turn the filter function into an equivalent Pipe class.
|
||||
The implementation is the same as before, repackaged in the `transform` method.
|
||||
Rename the file to `checkmark.pipe.ts` to conform with Angular 2 conventions:
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/core/checkmark/checkmark.pipe.ts', null, 'app/core/checkmark/checkmark.pipe.ts')(format='.')
|
||||
|
||||
:marked
|
||||
In the component we should now import and declare our newly created pipe (as well as
|
||||
remove the filter <script> tag from `index.html`):
|
||||
Now import and declare the newly created pipe and
|
||||
remove the filter <script> tag from `index.html`:
|
||||
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/phone-detail/phone-detail.component.ts', 'checkmark-pipe', 'app/phone-detail/phone-detail.component.ts')
|
||||
+makeExample('upgrade-phonecat-2-hybrid/ts/app/app.module.ts', 'checkmarkpipe', 'app.module.ts')
|
||||
|
||||
:marked
|
||||
## Switching To The Angular 2 Router And Bootstrap
|
||||
|
||||
At this point we've replaced all our Angular 1 application components with
|
||||
their Angular 2 counterparts. The application is still bootstrapped as a hybrid,
|
||||
but there isn't really any need for that anymore, and we can begin to
|
||||
pull out the last remnants of Angular 1.
|
||||
At this point we've replaced all Angular 1 application components with
|
||||
their Angular 2 counterparts.
|
||||
|
||||
There are just two more things to do: We need to switch the router to
|
||||
the Angular 2 one, and then bootstrap the app as a pure Angular 2 app.
|
||||
The application is still bootstrapped as a hybrid app.
|
||||
There's no need for that anymore.
|
||||
It's time to remove the last remnants of Angular 1 in two final steps:
|
||||
1. Switch to the Angular 2 router.
|
||||
1. Bootstrap as a pure Angular 2 app.
|
||||
|
||||
Let's do the routing part first. Angular 2 comes with an [all-new router](router.html)
|
||||
that we can use for this.
|
||||
### Switch to the Angular 2 router
|
||||
Angular 2 has an [all-new router](router.html).
|
||||
|
||||
Angular 2 applications all have a *root component*, which, among other
|
||||
things, is where we should plug in the router. We don't yet have such a root
|
||||
component, because our app is still managed as an Angular 1 app.
|
||||
Let's change this now and add an `AppComponent` class into a new file
|
||||
`app.component.ts`:
|
||||
Like all routers, it needs a place in the UI to display routed views.
|
||||
The Angular 2 that's the `<router-outlet>` and it belongs in a *root component*
|
||||
at the top of the applications component tree.
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/ts/app/app.component.ts', null, 'app/app.component.ts')
|
||||
We don't yet have such a root component, because the app is still managed as an Angular 1 app.
|
||||
Create a new `app.component.ts` file with the following `AppComponent` class:
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/ts/app/app.component.ts', null, 'app/app.component.ts')(format='.')
|
||||
|
||||
:marked
|
||||
This is a component that plugs in to an `<phonecat-app>` element on the page,
|
||||
and has a simple template that only includes the router outlet component
|
||||
of the Angular router. This means that the component just renders the contents
|
||||
of the current route and nothing else. The `@RouteConfig` decorator defines
|
||||
the Angular 2 counterparts of our two routes. They refer directly to the
|
||||
two components.
|
||||
It has a simple template that only includes the `<router-outlet>`.
|
||||
This component just renders the contents of the active route and nothing else.
|
||||
|
||||
We should put this `<phonecat-app>` element in the HTML so that the root component
|
||||
has something to attach to. It replaces the old Angular 1 `ng-view` directive:
|
||||
The selector tells Angular 2 to plug this root component into the `<phonecat-app>`
|
||||
element on the host web page when the application launches.
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/ts/index.html', 'appcomponent', 'index.html')
|
||||
Add this `<phonecat-app>` element to the `index.html`.
|
||||
It replaces the old Angular 1 `ng-view` directive:
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/ts/index.html', 'appcomponent', 'index.html (body)')(format='.')
|
||||
|
||||
:marked
|
||||
In the `PhoneDetail` component we now need to change how the phone id parameter
|
||||
is received. There will be no more `$routeParams` injection available, because
|
||||
that comes from the Angular 1 router. Instead, what we have is a `RouteParams`
|
||||
object provided by the Angular 2 router. We use it to obtain the `phoneId` from
|
||||
the params:
|
||||
### Create the _Routing Module_
|
||||
A router needs configuration whether it's the Angular 1 or Angular 2 or any other router.
|
||||
|
||||
The details of Angular 2 router configuration are best left to the [Routing](../router.html) documentation
|
||||
which recommends that you create a `NgModule` dedicated to router configuration
|
||||
(called a _Routing Module_):
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/ts/app/app-routing.module.ts', null, 'app/app-routing.module.ts')
|
||||
|
||||
:marked
|
||||
This module defines a `routes` object with two routes to the two phone components
|
||||
and a default route for the empty path.
|
||||
It passes the `routes` to the `RouterModule.forRoot` method which does the rest.
|
||||
|
||||
A couple of extra providers enable routing with "hash" URLs such as `#!/phones` instead of the default "push state" strategy.
|
||||
|
||||
Now update the `AppModule` to import this `AppRoutingModule` and also the
|
||||
declare the root `AppComponent`:
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/ts/app/app.module.ts', null, 'app/app.module.ts')
|
||||
|
||||
:marked
|
||||
The Angular 2 router passes route parameters differently.
|
||||
Correct the `PhoneDetail` component constructor to expect an injected `ActivatedRoute` object.
|
||||
Extract the `phoneId` from the `ActivatedRoute.snapshot.params` and fetch the phone data as before:
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/ts/app/phone-detail/phone-detail.component.ts', null, 'app/phone-detail/phone-detail.component.ts')
|
||||
:marked
|
||||
### Generate links for each phone
|
||||
|
||||
We no longer have to hardcode the links to phone details in the phone list.
|
||||
We can generate them data binding each phone's `id` to the `routerLink` directive
|
||||
and let that directive construct the appropriate URL to the `PhoneDetailComponent`:
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/ts/app/phone-list/phone-list.template.html', 'list', 'app/phone-list/phone-list.template.html (list with links)')(format='.')
|
||||
.l-sub-section
|
||||
:marked
|
||||
See the [Routing](../router.html) page for details.
|
||||
|
||||
:marked
|
||||
With that, we're ready to switch the bootstrap method of the application from that
|
||||
of the `UpgradeAdapter` to the main Angular 2 `bootstrap`. Let's import it together
|
||||
with the router, the new app component, and everything else in `main.ts`
|
||||
### Bootstrap as an Angular 2 app
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/ts/app/main.ts', 'imports')
|
||||
You may have noticed one extra `bootstrap` metadata property added to the `AppModule`
|
||||
+makeExample('upgrade-phonecat-3-final/ts/app/app.module.ts', 'bootstrap', 'app/app.module.ts (bootstrap)')(format='.')
|
||||
:marked
|
||||
That tells Angular 2 that it should bootstrap the app with the _root_ `AppComponent` and
|
||||
insert it's view into the host web page.
|
||||
|
||||
Now switch the bootstrap method of the application from the `UpgradeAdapter`
|
||||
to the Angular 2 way.
|
||||
Because this is a browser application, compiled with the Just-in-Time (JiT) compiler,
|
||||
use the `platformBrowserDynamic` function to bootstrap the `AppModule`:
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/ts/app/main.ts', null, 'main.ts')
|
||||
|
||||
:marked
|
||||
We'll now use the regular Angular 2 `bootstrap` function to bootstrap the app
|
||||
instead of using `UpgradeAdapter`. The first argument to `bootstrap` is the
|
||||
application's root component `AppComponent`, and the second
|
||||
is an array of the Angular 2 providers that we want to make available for
|
||||
injection. In that array we include all the things we have been registering
|
||||
with `upgradeAdapter.addProvider` until now, as well as the providers and
|
||||
directives of the router:
|
||||
You are now running a pure Angular 2 application!
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/ts/app/main.ts', 'bootstrap')
|
||||
|
||||
:marked
|
||||
We also configure a couple of things for the router here so that the application
|
||||
URL paths match exactly those we had in the Angular 1 app: We want the
|
||||
hash location strategy with the `!` prefix: `#!/phones`.
|
||||
|
||||
At this point we are running a pure Angular 2 application!
|
||||
|
||||
But there's actually one more cool thing we can do with the new router.
|
||||
We no longer have to hardcode the links to phone details from the phone
|
||||
list, because the Angular 2 router is able to generate them for us with
|
||||
its `routerLink` directive. We just need to refer to the route names we
|
||||
used in the `@RouteConfig`:
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/ts/app/phone-list/phone-list.template.html', 'list', 'app/phone-list/phone-list.template.html')
|
||||
|
||||
:marked
|
||||
For this to work the directive just needs to be declared in the component:
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/ts/app/phone-list/phone-list.component.ts', 'top')
|
||||
|
||||
:marked
|
||||
## Saying Goodbye to Angular 1
|
||||
## Say Goodbye to Angular 1
|
||||
|
||||
It is time to take off the training wheels and let our application begin
|
||||
its new life as a pure, shiny Angular 2 app. The remaining tasks all have to
|
||||
|
@ -1578,24 +1616,12 @@ code-example(format="").
|
|||
that use WebDriver's generic URL APIs instead. The first of these is
|
||||
the redirection spec:
|
||||
|
||||
.alert.is-important
|
||||
:marked
|
||||
This guide is still in the process of being updated to RC5 and it's samples
|
||||
may not work correctly.
|
||||
|
||||
//- +makeExample('upgrade-phonecat-3-final/e2e-spec.ts', 'redirect', 'e2e-tests/scenarios.ts')
|
||||
+makeExample('upgrade-phonecat-3-final/e2e-spec.ts', 'redirect', 'e2e-tests/scenarios.ts')
|
||||
|
||||
:marked
|
||||
And the second is the phone links spec:
|
||||
|
||||
.alert.is-important
|
||||
:marked
|
||||
This guide is still in the process of being updated to RC5 and it's samples
|
||||
may not work correctly.
|
||||
|
||||
//- +makeExample('upgrade-phonecat-3-final/e2e-spec.ts', 'links', 'e2e-tests/scenarios.ts')
|
||||
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/e2e-spec.ts', 'links', 'e2e-tests/scenarios.ts')
|
||||
|
||||
:marked
|
||||
## Unit Tests
|
||||
|
@ -1666,10 +1692,10 @@ code-example(format="").
|
|||
|
||||
:marked
|
||||
Finally, we need to revisit both of the component tests when we switch to the Angular 2
|
||||
router. For the details component we need to provide an Angular 2 `RouteParams` object
|
||||
router. For the details component we need to provide a mock of Angular 2 `ActivatedRoute` object
|
||||
instead of using the Angular 1 `$routeParams`.
|
||||
|
||||
+makeExample('upgrade-phonecat-3-final/ts/app/phone-detail/phone-detail.component.spec.ts', 'routeparams', 'app/phone-detail/phone-detail.component.spec.ts')
|
||||
+makeExample('upgrade-phonecat-3-final/ts/app/phone-detail/phone-detail.component.spec.ts', 'activatedroute', 'app/phone-detail/phone-detail.component.spec.ts')
|
||||
|
||||
:marked
|
||||
And for the phone list component we need to set up a few things for the router itself so that
|
||||
|
|