Skip to content
This repository was archived by the owner on Dec 4, 2017. It is now read-only.

Commit 4acbef1

Browse files
teropawardbell
authored andcommitted
docs(upgrade): add ngUpgrade guide version 2
closes #1538 This is a major reorganization of the Upgrade guide. * Compatible with the new version of the AngularJS 1 PhoneCat tutorial. * No longer switching Angular 1 code to SystemJS for PhoneCat, to allow beginning Angular 2 migration with fewer preparation steps. SystemJS switch now happens simultaneously with upgrade. (This is based on input from @joeeames) * Testing moved to an appendix to make the main narrative shorter and easier to follow. * Use component methods to do phone filtering and ordering instead of introducing pipes to replace filterFilter and orderByFilter. * Cover issue with camelCase inputs on downgraded components. For authors: * All examples now fully integrated with the example boilerplate. Uses the same Angular 2 version as all other guides. E2E tests are executed along with all the others. * Reduced number of PhoneCat versions from five to three. * Each directory has a README explaining how to run it and what might be peculiar about it. Closes angular/angular#8622 Relates to angular/angular.js#14416 Relates to angular/angular-phonecat#326
1 parent bd6d788 commit 4acbef1

File tree

352 files changed

+17298
-5963
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

352 files changed

+17298
-5963
lines changed

public/docs/_examples/upgrade-adapter/ts/app/1-to-2-providers/app.module.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ declare var angular:any;
77
// #docregion register
88
angular.module('heroApp', [])
99
.service('heroes', HeroesService)
10-
.directive('heroDetail', upgradeAdapter.downgradeNg2Component(HeroDetailComponent));
10+
.directive('heroDetail',
11+
upgradeAdapter.downgradeNg2Component(HeroDetailComponent));
1112

1213
upgradeAdapter.upgradeNg1Provider('heroes');
1314

public/docs/_examples/upgrade-adapter/ts/index-downgrade-io.html

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@
3030
<!-- #docregion usecomponent -->
3131
<div ng-controller="MainController as mainCtrl">
3232
<hero-detail [hero]="mainCtrl.hero"
33-
(deleted)="mainCtrl.onDelete($event)">
33+
(deleted)="mainCtrl.onDelete($event)">
3434
</hero-detail>
3535
</div>
3636
<!-- #enddocregion usecomponent -->
3737
<!-- #docregion userepeatedcomponent -->
3838
<div ng-controller="MainController as mainCtrl">
3939
<hero-detail [hero]="hero"
40-
(deleted)="mainCtrl.onDelete($event)"
41-
ng-repeat="hero in mainCtrl.heroes">
40+
(deleted)="mainCtrl.onDelete($event)"
41+
ng-repeat="hero in mainCtrl.heroes">
4242
</hero-detail>
4343
</div>
4444
<!-- #enddocregion userepeatedcomponent-->

public/docs/_examples/upgrade-adapter/ts/index-ng-app.html

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
</head>
88

99
<body ng-app="heroApp" ng-strict-di>
10-
<div id="message" ng-controller="MainCtrl as mainCtrl">{{ mainCtrl.message }}</div>
10+
<div id="message" ng-controller="MainCtrl as mainCtrl">
11+
{{ mainCtrl.message }}
12+
</div>
1113
</body>
1214
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
This is the Angular Phonecat application adjusted to fit our boilerplate project
2+
structure.
3+
4+
The following changes from vanilla Phonecat are applied:
5+
6+
* The TypeScript config file shown in the guide is `tsconfig.ng1.json` instead
7+
of the default, because we don't want to enable `noImplicitAny` for migration.
8+
* Karma config for unit tests is in karma.conf.ng1.js because the boilerplate
9+
Karma config is not compatible with the way Angular 1 tests need to be run.
10+
The shell script run-unit-tests.sh can be used to run the unit tests.
11+
* Instead of using Bower, Angular 1 and its dependencies are fetched from a CDN
12+
in index.html and karma.conf.ng1.js.
13+
* E2E tests have been moved to the parent directory, where `gulp run-e2e-tests` can
14+
discover and run them along with all the other examples.
15+
* Angular 1 typings (from DefinitelyTyped) are added to typings-ng1 so that
16+
TypeScript can recognize Angular 1 code. (typings.json comes from boilerplate
17+
so we can't add them there).
18+
* Most of the phone JSON and image data removed in the interest of keeping
19+
repo weight down. Keeping enough to retain testability of the app.
20+
21+
## Running the app
22+
23+
Start like any example
24+
25+
npm run start
26+
27+
You'll find the app under the /app path: http://localhost:3002/app/index.html
28+
29+
## Running unit tests
30+
31+
./run-unit-tests.sh
32+
33+
## Running E2E tests
34+
35+
Like for any example (at the project root):
36+
37+
gulp run-e2e-tests --filter=phonecat-1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
'use strict';
2+
3+
// Angular E2E Testing Guide:
4+
// https://docs.angularjs.org/guide/e2e-testing
5+
6+
describe('PhoneCat Application', function() {
7+
8+
beforeAll(function() {
9+
browser.baseUrl = 'http://localhost:8080/app/';
10+
setProtractorToNg1Mode();
11+
});
12+
13+
it('should redirect `index.html` to `index.html#!/phones', function() {
14+
browser.get('index.html');
15+
expect(browser.getLocationAbsUrl()).toBe('/phones');
16+
});
17+
18+
describe('View: Phone list', function() {
19+
20+
beforeEach(function() {
21+
browser.get('index.html#!/phones');
22+
});
23+
24+
it('should filter the phone list as a user types into the search box', function() {
25+
var phoneList = element.all(by.repeater('phone in $ctrl.phones'));
26+
var query = element(by.model('$ctrl.query'));
27+
28+
expect(phoneList.count()).toBe(20);
29+
30+
query.sendKeys('nexus');
31+
expect(phoneList.count()).toBe(1);
32+
33+
query.clear();
34+
query.sendKeys('motorola');
35+
expect(phoneList.count()).toBe(8);
36+
});
37+
38+
it('should be possible to control phone order via the drop-down menu', function() {
39+
var queryField = element(by.model('$ctrl.query'));
40+
var orderSelect = element(by.model('$ctrl.orderProp'));
41+
var nameOption = orderSelect.element(by.css('option[value="name"]'));
42+
var phoneNameColumn = element.all(by.repeater('phone in $ctrl.phones').column('phone.name'));
43+
44+
function getNames() {
45+
return phoneNameColumn.map(function(elem) {
46+
return elem.getText();
47+
});
48+
}
49+
50+
queryField.sendKeys('tablet'); // Let's narrow the dataset to make the assertions shorter
51+
52+
expect(getNames()).toEqual([
53+
'Motorola XOOM\u2122 with Wi-Fi',
54+
'MOTOROLA XOOM\u2122'
55+
]);
56+
57+
nameOption.click();
58+
59+
expect(getNames()).toEqual([
60+
'MOTOROLA XOOM\u2122',
61+
'Motorola XOOM\u2122 with Wi-Fi'
62+
]);
63+
});
64+
65+
it('should render phone specific links', function() {
66+
var query = element(by.model('$ctrl.query'));
67+
query.sendKeys('nexus');
68+
69+
element.all(by.css('.phones li a')).first().click();
70+
expect(browser.getLocationAbsUrl()).toBe('/phones/nexus-s');
71+
});
72+
73+
});
74+
75+
describe('View: Phone detail', function() {
76+
77+
beforeEach(function() {
78+
browser.get('index.html#!/phones/nexus-s');
79+
});
80+
81+
it('should display the `nexus-s` page', function() {
82+
expect(element(by.binding('$ctrl.phone.name')).getText()).toBe('Nexus S');
83+
});
84+
85+
it('should display the first phone image as the main phone image', function() {
86+
var mainImage = element(by.css('img.phone.selected'));
87+
88+
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
89+
});
90+
91+
it('should swap the main image when clicking on a thumbnail image', function() {
92+
var mainImage = element(by.css('img.phone.selected'));
93+
var thumbnails = element.all(by.css('.phone-thumbs img'));
94+
95+
thumbnails.get(2).click();
96+
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.2.jpg/);
97+
98+
thumbnails.get(0).click();
99+
expect(mainImage.getAttribute('src')).toMatch(/img\/phones\/nexus-s.0.jpg/);
100+
});
101+
102+
});
103+
104+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/* Animate `ngRepeat` in `phoneList` component */
2+
.phone-list-item.ng-enter,
3+
.phone-list-item.ng-leave,
4+
.phone-list-item.ng-move {
5+
overflow: hidden;
6+
transition: 0.5s linear all;
7+
}
8+
9+
.phone-list-item.ng-enter,
10+
.phone-list-item.ng-leave.ng-leave-active,
11+
.phone-list-item.ng-move {
12+
height: 0;
13+
margin-bottom: 0;
14+
opacity: 0;
15+
padding-bottom: 0;
16+
padding-top: 0;
17+
}
18+
19+
.phone-list-item.ng-enter.ng-enter-active,
20+
.phone-list-item.ng-leave,
21+
.phone-list-item.ng-move.ng-move-active {
22+
height: 120px;
23+
margin-bottom: 20px;
24+
opacity: 1;
25+
padding-bottom: 4px;
26+
padding-top: 15px;
27+
}
28+
29+
/* Animate view transitions with `ngView` */
30+
.view-container {
31+
position: relative;
32+
}
33+
34+
.view-frame {
35+
margin-top: 20px;
36+
}
37+
38+
.view-frame.ng-enter,
39+
.view-frame.ng-leave {
40+
background: white;
41+
left: 0;
42+
position: absolute;
43+
right: 0;
44+
top: 0;
45+
}
46+
47+
.view-frame.ng-enter {
48+
animation: 1s fade-in;
49+
z-index: 100;
50+
}
51+
52+
.view-frame.ng-leave {
53+
animation: 1s fade-out;
54+
z-index: 99;
55+
}
56+
57+
@keyframes fade-in {
58+
from { opacity: 0; }
59+
to { opacity: 1; }
60+
}
61+
62+
@keyframes fade-out {
63+
from { opacity: 1; }
64+
to { opacity: 0; }
65+
}
66+
67+
/* Older browsers might need vendor-prefixes for keyframes and animation! */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
3+
angular.
4+
module('phonecatApp').
5+
animation('.phone', function phoneAnimationFactory() {
6+
return {
7+
addClass: animateIn,
8+
removeClass: animateOut
9+
};
10+
11+
function animateIn(element: JQuery, className: string, done: () => void) {
12+
if (className !== 'selected') return;
13+
14+
element.css({
15+
display: 'block',
16+
position: 'absolute',
17+
top: 500,
18+
left: 0
19+
}).animate({
20+
top: 0
21+
}, done);
22+
23+
return function animateInEnd(wasCanceled: boolean) {
24+
if (wasCanceled) element.stop();
25+
};
26+
}
27+
28+
function animateOut(element: JQuery, className: string, done: () => void) {
29+
if (className !== 'selected') return;
30+
31+
element.css({
32+
position: 'absolute',
33+
top: 0,
34+
left: 0
35+
}).animate({
36+
top: -500
37+
}, done);
38+
39+
return function animateOutEnd(wasCanceled: boolean) {
40+
if (wasCanceled) element.stop();
41+
};
42+
}
43+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// #docregion
2+
angular.
3+
module('phonecatApp').
4+
config(['$locationProvider' ,'$routeProvider',
5+
function config($locationProvider: angular.ILocationProvider,
6+
$routeProvider: angular.route.IRouteProvider) {
7+
$locationProvider.hashPrefix('!');
8+
9+
$routeProvider.
10+
when('/phones', {
11+
template: '<phone-list></phone-list>'
12+
}).
13+
when('/phones/:phoneId', {
14+
template: '<phone-detail></phone-detail>'
15+
}).
16+
otherwise('/phones');
17+
}
18+
]);

0 commit comments

Comments
 (0)