Tuesday, May 24, 2016

Angular2 migration from Beta to RC1

I had a chance to explore this Angular2 space again and found that voila, RC1 is out! How fun it would be migrate over.. Using my last post as a basis for moving forward, here is what I went through.

Please note that I'm not an expert in Angular2. Just exploring it in my free time.

First off the dependencies in your packages.json file changed:
Before:
 {  
  "version": "1.0.0",  
  "name": "ASP.NET",  
  "private": true,  
  "dependencies": {  
   "angular2": "^2.0.0-beta.7",  
   "es6-promise": "^3.1.2",  
   "es6-shim": "0.35.0",  
   "reflect-metadata": "^0.1.3",  
   "rxjs": "^5.0.0-beta.2",  
   "systemjs": "^0.19.23",  
   "whatwg-fetch": "^0.11.0",  
   "zone.js": "^0.6.1"  
  },  
  "devDependencies": {}  
 }  

After:
 {  
  "version": "1.0.0",  
  "name": "ASP.NET",  
  "private": true,  
  "dependencies": {  
   "@angular/common": "2.0.0-rc.1",  
   "@angular/compiler": "2.0.0-rc.1",  
   "@angular/core": "2.0.0-rc.1",  
   "@angular/http": "2.0.0-rc.1",  
   "@angular/platform-browser": "2.0.0-rc.1",  
   "@angular/platform-browser-dynamic": "2.0.0-rc.1",  
   "@angular/router": "2.0.0-rc.1",  
   "@angular/router-deprecated": "2.0.0-rc.1",  
   "@angular/upgrade": "2.0.0-rc.1",  
   "es6-promise": "^3.1.2",  
   "es6-shim": "0.35.0",  
   "reflect-metadata": "^0.1.3",  
   "rxjs": "^5.0.0-beta.6", //Be sure to use beta.6  
   "systemjs": "^0.19.27",  
   "whatwg-fetch": "^0.11.0",  
   "zone.js": "^0.6.12"  
  },  
  "devDependencies": {  
   "typescript": "^1.8.10"  
  }  
 }  

After setting up these dependencies in packages.json I performed my npm install and found some errors:

Went ahead and deleted the "angular2" folders from my "wwwroot" folder.
Modified the gulpfile.js file to replace the "angular2" copies from node_modules to wwwroot/node_modules with new "@angular/*" calls:
 gulp.task('thirdparty', function () {  
   gulp.src('./node_modules/@angular/**/*.js')  
     .pipe(gulp.dest('./wwwroot/node_modules/@angular'));  
   // gulp.src('./node_modules/angular2/**/*.js')  
   //   .pipe(gulp.dest('./wwwroot/node_modules/angular2'));  

Then I had to go and update my index.html file to replace the angular2 references with @angular. However I decided life would be harder if I took this opportunity to see what systemjs is about. So of course lets digress and go down systemjs learning alley.

Systemjs allows us to specify our third party references in a separate javascript file. Systemjs has the added ability of loading these 3rd party references on demand vs on page load. So anyways, here is the before and after of my index.html file: Before:
 <!DOCTYPE html>  
 <html>  
 <head>  
   <base href="/">  
   <meta charset="utf-8" />  
   <title></title>  
 </head>  
 <body>  
   <app-shell><h2>Loading...</h2></app-shell>  
   <script src="node_modules/es6-shim/es6-shim.js"></script>  
   <script src="node_modules/angular2/es6/dev/src/testing/shims_for_IE.js"></script>  
   <script src="node_modules/systemjs/dist/system.src.js"></script>  
   <script src="node_modules/rxjs/bundles/Rx.js"></script>  
   <script src="node_modules/es6-shim/es6-shim.js"></script>  
   <script src="node_modules/angular2/bundles/angular2.dev.js"></script>  
   <script src="node_modules/angular2/bundles/router.dev.js"></script>  
   <script src="node_modules/angular2/bundles/http.dev.js"></script>  
   <script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>  
   <script>  
     System.config({  
       packages: {  
         app: {  
           format: 'register',  
           defaultExtension: 'js',  
         }  
       }  
     });  
     System.import('app/boot')  
       .then(console.log('started application'), console.error.bind(console));  
   </script>  
 </body>  
 </html>  

After:
 <!DOCTYPE html>  
 <html>  
 <head>  
   <base href="/">  
   <meta charset="utf-8" />  
   <title></title>  
 </head>  
 <body>  
   <app-shell><h2>Loading...</h2></app-shell>  
   <script src="node_modules/es6-shim/es6-shim.js"></script>  
   <script src="node_modules/typescript/lib/typescript.js"></script>  
   <script src="node_modules/es6-shim/es6-shim.js"></script>  
   <script src="node_modules/reflect-metadata/Reflect.js"></script>  
   <script src="node_modules/systemjs/dist/system.js"></script> <!--//Changed from system.src.js -->  
   <script src="node_modules/zone.js/dist/zone.js"></script>   <!--//Had to add Zonejs as it is used by @angular/core -->  
   <script src="systemjs.config.js"></script>  
   <script>  
     System.import('app/boot')  
       .then(console.log('started application'), console.error.bind(console));  
   </script>  
 </body>  
 </html>  

This leads us to where are the "@angular" references? Well I've got them in a file called systemjs.config.js which is in the root of my project directory (i.e. at the same level as my packages.json, etc).
Here is what I've got in my systemjs.config.js file:
 System.config({  
   transpiler: 'typescript',  
   typescriptOptions: {emitDecoratorMetadata: true},  
   map: {  
     'app' : 'app',  
     'rxjs': 'node_modules/rxjs',  
     '@angular/core'          : 'node_modules/@angular/core',  
     '@angular/common'         : 'node_modules/@angular/common',  
     '@angular/compiler'        : 'node_modules/@angular/compiler',  
     '@angular/router'         : 'node_modules/@angular/router',  
     '@angular/platform-browser'    : 'node_modules/@angular/platform-browser',  
     '@angular/platform-browser-dynamic': 'node_modules/@angular/platform-browser-dynamic',  
     '@angular/http'          : 'node_modules/@angular/http'  
 },  
   packages: {  
     'app'               : {main: 'app/boot.ts', defaultExtension: 'ts'},  
     'rxjs'               : {main: 'index.js'},  
     '@angular/core'          : {main: 'index.js'},  
     '@angular/common'         : {main: 'index.js'},  
     '@angular/compiler'        : {main: 'index.js'},  
     '@angular/router'         : {main: 'index.js'},  
     '@angular/platform-browser'    : {main: 'index.js'},  
     '@angular/platform-browser-dynamic': {main: 'index.js'},  
     '@angular/http'          : { main: 'index.js'}  
 }  
 });  

So I've got my app, rxjs and @angular setup in systemjs and it seems to work just fine. But before it will work you need to be sure to copy it to the wwwroot folder via your gulpfile.js:
 gulp.task('copy', function () {  
   gulp.src('./app/**/*.*')  
     .pipe(gulp.dest('./wwwroot/app'));  
   gulp.src('./systemjs.config.js')  
     .pipe(gulp.dest('./wwwroot'));  
 });  

So our basic setup is done. Now onto the interesting changes that have taken place since Angular2 Beta 7: 1. It looks like @View is no more. They've moved it's properties into @Componment. Here is some before and after: Before:
 @Component({  
   selector: 'book-edit.component'  
 })  
 @View({  
   templateUrl: './app/components/book/edit/book-edit.component.html',  
   directives: [CORE_DIRECTIVES, FORM_DIRECTIVES, NgFormControl]  
 })  

After:
 @Component({  
   selector: 'book-edit.component',  
   templateUrl: './app/components/book/edit/book-edit.component.html',  
   directives: [CORE_DIRECTIVES, FORM_DIRECTIVES, NgFormControl]  
 })  

2. Of course, with angular2 gone, you have to go through and clean up all your imports. Here is a brief example: Before:
 import {Component, View, Inject} from 'angular2/core';  
 import {RouteParams} from 'angular2/router';  
 import {CORE_DIRECTIVES, FORM_DIRECTIVES, FormBuilder, Control, ControlGroup, Validators, NgFormControl, AbstractControl } from 'angular2/common';  

After:
 import {Component, Inject} from '@angular/core';  
 import {RouteSegment} from '@angular/router';  
 import {CORE_DIRECTIVES, FORM_DIRECTIVES, FormBuilder, Control, ControlGroup, Validators, NgFormControl, AbstractControl } from '@angular/common';  

3. Routers had a major overhaul. Here are some before and after changes I ran into:
 
 before: import {RouteParams} from '@angular/router';  
 after : import {RouteSegment} from '@angular/router';  

 before: constructor( @Inject(RouteParams) params: RouteParams,  
 after : constructor( @Inject(RouteSegment) params: RouteSegment,  

 before: this.getBook(params.get('id'));  
 after : this.getBook(params.getParam('id'));  

 before:  
 @RouteConfig([  
     { path: '/list-admin',      component: BookListAdminComponent,  as: 'ListAdmin' },  
     { path: '/list-readonly',     component: BookListReadonlyComponent, as: 'ListReadonly' },  
     { path: '/create',        component: BookCreateComponent,    as: 'Create' },  
     { path: '/edit/:id',       component: BookEditComponent,     as: 'Edit' },  
     { path: '/view/:id/',       component: BookViewComponent,     as: 'View' }  
 ])  
 after:   
 @Routes([  
     { path: '/list-admin', component: BookListAdminComponent }, //  as: 'ListAdmin' },  
     { path: '/list-readonly', component: BookListReadonlyComponent }, // as: 'ListReadonly' },  
     { path: '/create', component: BookCreateComponent }, //    as: 'Create' },  
     { path: '/edit/:id', component: BookEditComponent }, //     as: 'Edit' },  
     { path: '/view/:id', component: BookViewComponent }, //     as: 'View' }  
 ])  

 before: this.router.parent.navigate(['/View', { id: book.id }]);  
 after: this.router.navigate(['/view/'+ book.id ]);  

 before: <a [routerLink]="['ListReadonly']">View List of Books</a>  
 after: <a [routerLink]="['/list-readonly']">View List of Books</a>  
Now at this point I've made all my import changes, etc and think I'm ready to continue on. Unfortunately, VS.Net 2015 is throwing a lot of errors my way now, like:
1. Observable does not contain property ‘map’
2. TS2304: Cannot find name 'Promise'
3. Build: Ambient modules cannot be nested in other modules or namespaces
4. Build: Ambient declaration cannot specify relative module name

Looking at the details of these errors, for the most part all the errors I'm seeing are all associated to rxjs.

In my little world I've been brought up to respect and bow before the VS.Net 2015 gods when they throw red error lightning bolts my way. In this case its about 650 of 'em and I'm resembling a pin cushion. For I must fix them before I proceed, so I did the following: (note: I found later on that the code would run fine. It was VS.net and it's typescript version that were the problem and there is nothing I can do about it at this point...)
1. Explored 'typings'
2. Explored more 'gulp' functionality
3. In the end not sure if I needed 'typings' and didn't need the additional 'gulp' functionality.

I'll explore my journey with 'typings' in another post.

So yeah, life is good again. VS.net is throwing errors, but when I run the app all is good. All my crud functionality is working again after the upgrade. I look forward to when VS.net stops throwing errors on the rxjs stuff. So we'll see.