AngularJS 1 to Angular 2 Upgrade Strategy - angularjs

I decide to prepare updating my application from angular1.x to angular2.x. There is I have no find something useful. I have studied this document about 1 to 2 upgrade strategy. I figured out that all magic in migration is that you have to start Angular 1 and 2 in one time with Angular 1 in the root of the application, cut off the Angular1 unsupported code(filters, decorators and etc) and adapt(read wrap!) all Angular1 supported code(directives, services and etc).
The document that I have given above, you can see the pseudo code of the wrappers. I think if I wrap all of my current code - it doesn't explicitly give it speed. Who really have experience about it, write please how is it in real? Can I feared that my application starts to slow down, and may be easier to rewrite it once a new Angular2? But it`s very big, it will be a big piece of work and I have to think before. That why I ask about real experience who have production real life big projects and already migrated.
Also, I want to ask how I check libraries compatibilities. Maybe there is some service that checks my app and output results what libraries are good and what fails?

Yes, You can use both Angular 1 & Angular 2 in the same application. Just follow the below steps:
First of all you need to create "package.json" with all required angular 2 dependencies + #angular/upgrade
Create "tsconfig.json" and set "module": "system" in "compilerOptions" object. (To avoid require js errors)
Create "system.config.js" file (you can copy this file from angular 2 quick-start repo.). Don't forget to add '#angular/upgrade/static': 'npm:#angular/upgrade/bundles/upgrade-static.umd.js' bundle in a map object.
Include JS files for Reflect.js, zone.js, and system.config.js in your index.html just before the </body> tag (To avoid reflect-metadata & zone js errors).
Add this code just below that to import your app:
<script type="text/javascript">
System.import('app').catch(function(error){
console.log(error);
});
</script>
Create "main.ts" file and add below code :
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { UpgradeModule } from '#angular/upgrade/static';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { downgradeComponent } from '#angular/upgrade/static';
platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
upgrade.bootstrap(document.body, ['<app-name>']);
});
angular.module('<app-name>', []).directive('angular2App',
downgradeComponent({component: AppComponent}) as angular.IDirectiveFactory);
Create "app.module.ts" file and add the below code:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { UpgradeModule } from '#angular/upgrade/static';
import { AppComponent } from './app.component';
#NgModule({imports: [BrowserModule,UpgradeModule],declarations: [AppComponent],entryComponents: [AppComponent]})
export class AppModule {ngDoBootstrap() {console.log("Bootstrap Angular 2");}}
Create "app.component.ts" file and add below code":
import { Component } from '#angular/core';
#Component({selector: 'angular2',template: '<h1>Angular 2 Component</h1>'})
export class AppComponent { }
Now add the below code in your Angular 1 view file :
<angular2-app>Loading Angular 2...</angular2-app>
That's it. :)

Related

Angular migration - Hybrid application is not bootstrapping

I am working on migrating my Angularjs 1.6 application to Angular 7 via ngUpgrade route, after all done when I'm trying to bootstrap my hybrid application, it's failing to bootstrap, I noticed that my main.ts file isn't executing at all where as I've added it as entry point in my webpack config.
I'm using template.ejs file as template in my HtmlWebpackPlugin. For Webpack settings and package.json, Please check my application bundling and bootstrapping code on github here:
https://github.com/mmmathur/AngularMigration
I tried with renaming it as index.ts as well but still no luck.
main.ts
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { UpgradeModule } from '#angular/upgrade/static';
import { AppModule } from '../client/ngApp/ngApp.module';
const app = require('../client/app');
let appRoot = document.createElement('div');
const uiView = document.createElement('ui-view');
let body = document.querySelector('body');
appRoot.appendChild(uiView);
body.appendChild(appRoot);
console.log('executing main.ts');//even this statement is not executed
platformBrowserDynamic().bootstrapModule(AppModule).then((platformRef) => {
const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
upgrade.bootstrap(document.documentElement, [app]);
console.log('hybrid application is bootstrapped');
})
.catch(err => console.error(err))
ngAppModule.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import {HttpModule} from '#angular/http';
import { FormsModule} from '#angular/forms';
import {UpgradeModule} from '#angular/upgrade/static';
import { AppComponent } from './ngApp.component';
#NgModule({
imports: [
BrowserModule,
FormsModule,
HttpModule,
UpgradeModule
],
declarations: [
AppComponent
],
bootstrap: [
AppComponent
],
entryComponents: [
]
})
export class AppModule {}
ngApp.component.ts
import { Component } from "#angular/core";
#Component({
selector: 'ng-app',
template: `
<div>
<h2>Angular 7 application bootstraped</h2>
</div>
`
})
export class AppComponent {}
Expected: my Hybrid application gets bootstrapped.
Actual: a blank screen with no error thrown.
UPDATE Issue was these lines:
optimization:
{
splitChunks: {
chunks: 'all'
}
}
Optimization was causing this issue, once I removed above config option from webpack, application at least starting bootstrapping, though this time I'm getting error like:
Uncaught Error: Can't resolve all parameters for ApplicationModule: (?).
at syntaxError (compiler.js:2547)
at CompileMetadataResolver../node_modules/#angular/compiler/fesm5/compiler.js.CompileMetadataResolver._getDependenciesMetadata (compiler.js:17874)
at CompileMetadataResolver../node_modules/#angular/compiler/fesm5/compiler.js.CompileMetadataResolver._getTypeMetadata (compiler.js:17767)
at CompileMetadataResolver../node_modules/#angular/compiler/fesm5/compiler.js.CompileMetadataResolver.getNgModuleMetadata (compiler.js:17635)
at CompileMetadataResolver../node_modules/#angular/compiler/fesm5/compiler.js.CompileMetadataResolver.getNgModuleSummary (compiler.js:17445)
at compiler.js:17559
at Array.forEach (<anonymous>)
at CompileMetadataResolver../node_modules/#angular/compiler/fesm5/compiler.js.CompileMetadataResolver.getNgModuleMetadata (compiler.js:17547)
at CompileMetadataResolver../node_modules/#angular/compiler/fesm5/compiler.js.CompileMetadataResolver.getNgModuleSummary (compiler.js:17445)
at compiler.js:17532
I tried to fix this by adding polyfills.ts file in my project
Polyfills.ts
import 'core-js/es7/reflect';
import 'core-js/client/shim';
import 'zone.js/dist/zone.js';
but still getting same error, I'm using core-js 3.2.1.
looking if someone have idea about what's going wrong with my code?
UPDATE I learned that I supposed to add polyfills.ts file in HtmlWebpackPlugin-->chunks array as well, after adding, it started picking polyfills file and now that error has gone and now my angularjs app also getting bootstrapped along side angular 7 app, however now I'm getting another error like:
Error: [$injector:modulerr] Failed to instantiate module $$UpgradeModule due to:
Error: [$injector:modulerr] Failed to instantiate module {"default":"ui.app"} due to:
Error: [ng:areq] Argument 'module' is not a function, got Object
https://errors.angularjs.org/1.6.10/$injector/modulerr?p0=%24%24UpgradeModule&p1=Error%3A%20%5B%24injector%3Amodulerr%5D%20Failed%20to%20instantiate%20module%20%7B%22default%22%3A%22ec.customer.servicing.ui.app%22%7D%20due%20to%3A%0AError%3A%20%5Bng%3Aareq%5D%20Argument%20'module'%20is%20not%20a%20function%2C%20got%20Object%0A%0Ahttps%3A%2F%2Ferrors.angularjs.org%2F1.6.10%2F%24injector%2Fmodulerr%3Fp0%3D%257B%2522default%2522%253A%2522ec.customer.servicing.ui.app%2522%257D%26p1%3DError%253A%2520%255Bng%253Aareq%255D%2520Argument%2520'module'%2520is%2520not%2520a%2520function%252C%2520got%2520Object%250Ahttps%253A%252F%252Ferrors.angularjs.org%252F1.6.10%252Fng%252Fareq%253Fp0%253Dmodule%2526p1%253Dnot%252520a%252520function%25252C%252520got%252520Object%250A%2520%2520%2520%2520at%2520http%253A%252F%252Flocalhost%253A8080%252Fng1.bundle.js%253F29bb21d813a74a626d53%253A32020%253A12%250A%2520%2520%2520%2520at%2520assertArg%2520(http%253A%252F%252Flocalhost%253A8080%252Fng1.bundle.js%253F29bb21d813a74a626d53%253A33994%253A11)%250A%2520%2520%2520%2520at%2520assertArgFn%2520(http%253A%252F%252Flocalhost%253A8080%252Fng1.bundle.js%253F29bb21d813a74a626d53%253A34004%253A3)%250A%2520%2520%2520%2520at%2520http%253A%252F%252Flocalhost%253A8080%252Fng1.bundle.js%253F29bb21d813a74a626d53%253A36915%253A11%250A%2520%2520%2520%2520at%2520forEach%2520(http%253A%252F%252Flocalhost%253A8080%252Fng1.bundle.js%253F29bb21d813a74a626d53%253A32313%253A20)%250A%2520%2520%2520%2520at%2520loadModules%2520(http%253A%252F%252Flocalhost%253A8080%252Fng1.bundle.js%253F29bb21d813a74a626d53%253A36889%253A5)%250A%2520%2520%2520%2520at%2520http%253A%252F%252Flocalhost%253A8080%252Fng1.bundle.js%253F29bb21d813a74a626d53%253A36907%253A40%250A%2520%2520%2520%2520at%2520forEach%2520(http%253A%252F%252Flocalhost%253A8080%252Fng1.bundle.js%253F29bb21d813a74a626d53%253A32313%253A20)%250A%2520%2520%2520%2520at%2520loadModules%2520(http%253A%252F%252Flocalhost%253A8080%252Fng1.bundle.js%253F29bb21d813a74a626d53%253A36889%253A5)%250A%2520%2520%2520%2520at%2520createInjector%2520(http%253A%252F%252Flocalhost%253A8080%252Fng1.bundle.js%253F29bb21d813a74a626d53%253A36806%253A19)%0A%20%20%20%20at%20http%3A%2F%2Flocalhost%3A8080%2Fng1.bundle.js%3F29bb21d813a74a626d53%3A32020%3A12%0A%20%20%20%20at%20http%3A%2F%2Flocalhost%3A8080%2Fng1.bundle.js%3F29bb21d813a74a626d53%3A36929%3A15%0A%20%20%20%20at%20forEach%20(http%3A%2F%2Flocalhost%3A8080%2Fng1.bundle.js%3F29bb21d813a74a626d53%3A32313%3A20)%0A%20%20%20%20at%20loadModules%20(http%3A%2F%2Flocalhost%3A8080%2Fng1.bundle.js%3F29bb21d813a74a626d53%3A36889%3A5)%0A%20%20%20%20at%20http%3A%2F%2Flocalhost%3A8080%2Fng1.bundle.js%3F29bb21d813a74a626d53%3A36907%3A40%0A%20%20%20%20at%20forEach%20(http%3A%2F%2Flocalhost%3A8080%2Fng1.bundle.js%3F29bb21d813a74a626d53%3A32313%3A20)%0A%20%20%20%20at%20loadModules%20(http%3A%2F%2Flocalhost%3A8080%2Fng1.bundle.js%3F29bb21d813a74a626d53%3A36889%3A5)%0A%20%20%20%20at%20createInjector%20(http%3A%2F%2Flocalhost%3A8080%2Fng1.bundle.js%3F29bb21d813a74a626d53%3A36806%3A19)%0A%20%20%20%20at%20doBootstrap%20(http%3A%2F%2Flocalhost%3A8080%2Fng1.bundle.js%3F29bb21d813a74a626d53%3A33860%3A20)%0A%20%20%20%20at%20Object.bootstrap%20(http%3A%2F%2Flocalhost%3A8080%2Fng1.bundle.js%3F29bb21d813a74a626d53%3A33881%3A12)
at angular.js:125
at angular.js:5034
at forEach (angular.js:418)
at loadModules (angular.js:4994)
at createInjector (angular.js:4911)
at doBootstrap (angular.js:1965)
at Object.bootstrap (angular.js:1986)
at bootstrap (static.js:76)
at static.js:1595
at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:391)
Any idea what's going wrong here?
If you're migrating from AngularJS probably the <script> tag for your bundle is inside the head segment. Move it to the end of body
<html>
<head>
</head>
<body>
... ...
<script src="dist/main.js" charset="utf-8"></script>
</body>
</html>

Not able to build Angular JS application integrate with Angular 4

I have a Angular JS application where each angular JS library files are included in html file for each functionality.Is it feasible to make this application work together with Angular 4.
Steps to make Angular JS work with Angular 4
1)install the #angular/upgrade How do I do make this work in sync with the old Angular JS files
2)I read it in a post that I need to define this in system.config.js
System.config({
map: {
'#angular/upgrade/static': 'npm:#angular/upgrade/bundles/upgrade-static.umd.js'
}
});
3)In app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { UpgradeModule } from '#angular/upgrade/static';
#NgModule({
imports: [
BrowserModule,
UpgradeModule
],
bootstrap: []
})
export class AppModule {
ngDoBootstrap() {}
}
4)main.ts
import { NgModule } from '#angular/core';
import { UpgradeModule } from '#angular/upgrade/static';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
upgrade.bootstrap(document.body, ['myApp'], { strictDi: true });
});
Are these steps sufficient to integrate Angular JS application to Angular 4.If so how do I do a build.The existing process is a monolithic application with Maven Build?Can Angular 4 be integrated with Maven?
The official Angular website has a migration guide here
The old versions of Angular (AngularJS 1.x) have a completely different project structure to the new Angular >2. This guide should help you out.

How to use 3rd party AngularJS (1.6) module (angular-translate) in Angular (2 or 4)

The case
I am in the process of upgrading an AngularJS (i.e. Angular 1.6) application to Angular (i.e. Angular 4.1.3). I chose to do the incremental upgrade so currently both AngularJS and Angular are bootstrapped and running. So far so good, but: One of the AngularJS services that should be rewritten to Angular relies on a well known service $translate which is part of the 3rd party AngularJS (read Angular 1) module pascalprecht.translate. In other words, $translate service is injected to MyService which should become an Angular (read Angular 4) service.
MyService (stripped down) looks like this:
import { Injectable } from '#angular/core';
#Injectable()
export class MyService {
// Here comes the injection (how to do it?).
constructor(private $translate: $translate) {
}
foo() {
// Use the service
this.$translate('HELLO_WORLD');
}
}
It is located within MyModule:
import { NgModule } from '#angular/core';
import { MyService } from './my.service';
#NgModule({
providers: [
MyService
]
})
export class MyModule {
}
The problem
Now, how can I inject $translate into MyService when MyService resides within an Angular module while $translate is part of a 3rd party AngularJS module?
I know how to inject an AngularJS service into an Angular service if the AngularJS service is located within the same module (or at least the module is part of my own solution). It is explained in the official docs. Is there any way to handle a 3rd party service? Do I need to register that service within MyModule's providers?
import { NgModule } from '#angular/core';
import { MyService } from './my.service';
// How this line should look line then?
import { $translate } from 'node_modules/angular-translate/...';
#NgModule({
providers: [
MyService,
$translate
]
})
export class MyModule {
}
Or am I trying to achieve impossible?
Well, after a few hours of struggling I've finally found the solution. Here it is:
Upgrade the 3rd party service
First of all, follow the Angular's official guidelines and upgrade the provider of the 3rd party service - $translate. Like this:
ajs-upgraded-providers.ts
import { InjectionToken } from '#angular/core';
import { downgradeInjectable } from '#angular/upgrade/static';
import 'angular-translate';
// Variable 'i' holds the standard AngularJS injector
export function $translateFactory(i: any) {
return i.get('$translate');
};
// There is no class representing the good old $translate service so we have
// a non-class dependency. Therefore we use an InjectionToken (Angular 4) or
// OpaqueToken (Angular 2).
export let $translate = new InjectionToken('$translate');
// Finally create the upgraded provider
export const $translateProvider = {
provide: $translate,
useFactory: $translateFactory,
deps: ['$injector']
};
One thing to notice, the $translate service might be dependent on other old AngularJS services like (in my case) $translateStaticFilesLoader or $translateMessageFormatInterpolation. If this is also your case, be sure to extend the code above and make upgraded providers for those services as well (keep them in the same file).
Make sure the import statement works
The angular-translate is installed as a node module so the statement
import 'angular-translate';
works just fine if your tsconfig.json is set up to use moduleResolution: "node".
Then, of course, you need to ensure that the import statement will work even after the code is transpiled from TypeScript to ES5 (or whichever target you use) and picked up by a module loader (SystemJS in my case).
Notice that we imported the angular-translate script without getting anything from it, just to cause side effects. Basically, the import ensures that the script containing the desired service $translate is simply executed and registers $translate service for the AngularJS $injector.
Register the upgraded provider in Angular module
Now ensure that the new upgraded provider of $translate service is registered among other providers of MyModule.
my.module.ts
import { NgModule } from '#angular/core';
import { MyService } from './my.service';
import { $translateProvider } from './ajs-upgraded-providers';
#NgModule({
providers: [
MyService,
$translateProvider
]
})
export class MyModule {
}
Use it
Finally, use the $translate service in MyService.
my.service.ts
import { Injectable, Inject } from '#angular/core';
import { $translate } from './ajs-upgraded-providers';
#Injectable()
export class MyService {
// Notice the usage of InjectionToken
constructor(#Inject($translate) private $translate: any) {
}
foo() {
this.$translate('hello.world').then((translation: string) => {
console.log(translation);
});
}
}

react-virtualized, SystemJS and CDN

I would like to use react-virtualized in a web application via cdnjs and SystemJS.
From all the examples that I have seen, it seems that react-virtualized and react libs are loaded locally, then bundled with Webpack before being included in a web page. Now I would like to use it via cdnjs without Webpack by simply importing it with SystemJS. But when I tried that I am getting errors saying that it can't find React.
I was wondering if anyone has tried this before, and whether this is supported at all by react-virtualized. Thanks.
UPDATE:
For clarity, I have included some code to show what I am trying to do.
index.html:
...
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.20.0-alpha.1/system.js"></script>
<script>
System.config({
map: {
"react":"https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.js",
"react-dom":"https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.js",
"react-virtualized":"https://unpkg.com/react-virtualized/dist/umd/react-virtualized.js"
}
</script>
</head>
...
main.jsx:
import React from 'react';
import ReactDOM from 'react-dom';
import {Table, Column} from 'react-virtualized';
...
What you are doing is possible using the UMD build of react-virtualized. You can see an example of that being done here. In order for it to work though, you'll need to also import UMD builds of react-with-addons and react-dom since react-virtualized depends on them.
For example:
<script src="https://unpkg.com/react/dist/react-with-addons.min.js"></script>
<script src="https://unpkg.com/react-dom/dist/react-dom.min.js"></script>
<script src="https://unpkg.com/react-virtualized/dist/umd/react-virtualized.js"></script>
The react-virtualized UMD build expects React and ReactDOM to be loaded in the global space.
Alternately there should be a way with SystemJS to expose react-dom and react-with-addons as globals for libraries like react-virtualized to consume. I'm not a SystemJS user, but I think it might be something like this:
System.config({
meta: {
'path/to/react-virtualized.js': {
globals: {
React: 'path/to/react-addons-shallow-compare.js',
ReactDOM: 'path/to/react-dom.js'
}
}
}
});
It's been a while, but I had some time to look at this again. Thanks to #brianvaughn's suggestion, I have managed to resolve this with the following:
System.config({
meta: {
"react-virtualized": {
exports: "ReactVirtualized",
format: "global",
globals: {
React: "react",
ReactDOM: "react-dom"
}
}
},
map: {
"react": "https://unpkg.com/react#16/umd/react.production.min.js",
"react-dom":"https://unpkg.com/react-dom#16/umd/react-dom.production.min.js",
"react-virtualized":"https://cdnjs.cloudflare.com/ajax/libs/react-virtualized/9.10.1/react-virtualized.min.js",
}
})
main.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import * as ReactVirtualized from 'react-virtualized';
let Table = ReactVirtualized.Table;
let Column = ReactVirtualized.Column;
let AutoSizer = ReactVirtualized.AutoSizer;
...

AngularJS 2 error: enableProdMode is not defined

I'm use https://angular.io/guide/quickstart to start learn Angular2, but I notice there's console log "Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode." So I added to main.ts as following.
import { bootstrap } from '#angular/platform-browser-dynamic';
import { AppComponent } from './app.component';
enableProdMode();
bootstrap(AppComponent);
Now the browser has error
enableProdMode is not defined(…)
So I found this article https://github.com/angular/angular/issues/6096.
Looks like I'm doing it right, and works on others, so maybe my package files or npm modules has something is wrong? please help, or do you know which module is using enableProdMode?
Seems like you missed to import enableProdMode
import {enableProdMode} from '#angular/core';

Resources