I have a use case to migrate a large angularjs application and I want to start by doing this process in a small application first. So for that reason, I've took the tour of heroes angularjs webapp and I started to add angular to it ( by creating a new project with the angular-cli ) and then adding the NgUpgrade module.
The problem that I have now is that the angularjs webapp runs pretty well inside the angular 8 application but the components that belong to angular 8 they are not rendered.
I have the impression that my angular components are not bootstrapped since I've bootsraped manually angularjs but I'm not sure ... when I add explicitly the bootstrap property inside the @NgModules it works only for the angular components but not for angularjs (it makes sense ). So I was thinking maybe I have to upgrade the angularjs components or downgrade the newest angular components but I don't think so.
Here you will find the git repository with the code. Below more information related to my project:
- angular version: 8.2.8
- angularJS version: 1.6.10
- angular-cli: 8.3.6
Project structure
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Common</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
<hero-list></hero-list>
</body>
</html>
index.ts ( angularjs root module)
// initialize root module and make it exportable to be able to bootstrap it
// inside angular
export const heroAppModule = angular.module('heroApp', [
'ngMaterial',
'perfect_scrollbar',
'ngJsTree',
'ngTagsInput',
'ui.router',
'heroApp.underscore'
]).config(['$stateProvider', function ($stateProvider) {
var heroState = {
name: 'hero',
url: '/hero',
template: '<hero-list></hero-list>'
};
$stateProvider.state(heroState);
}]);
/** start: REQUIRE ZONE for angularjs
* Add angularjs files since they aren't yet fully ES6 modules
* we use requirejs as module loader
*/
require('./editable-field/editable-field');
require('./hero-detail/hero-detail');
require('./hero-list/hero-list');
require('./underscore/underscore.module');
require('./underscore/underscore.service');
/**
* end: REQUIRE ZONE for angularjs
*/
app.module.ts ( bootstraping angularjs with NgUpgrade )
import * as angular from 'angular';
import { UpgradeModule, setAngularJSGlobal } from '@angular/upgrade/static';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { heroAppModule } from './../ngjs/index';
import { HelloworldComponent } from './helloworld/helloworld.component';
@NgModule({
declarations: [ HelloworldComponent ],
imports: [
BrowserModule,
UpgradeModule
] // ,
// bootstrap: [HelloworldComponent]
})
export class AppModule {
constructor(private upgrade: UpgradeModule) { }
ngDoBootstrap() {
setAngularJSGlobal(angular);
this.upgrade.bootstrap(document.body, [heroAppModule.name], { strictDi: true });
}
}
main.ts
import 'zone.js/dist/zone'; // Included with Angular CLI.
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/ngx/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
hero list component (angularjs)
declare var angular: angular.IAngularStatic;
(function () {
'use strict';
angular.module('heroApp').component('heroList', {
template: require('html-loader!./hero-list.html'),
controller: HeroListController,
controllerAs: 'vm'
});
HeroListController.$inject = ['$scope', '$element', '$attrs'];
function HeroListController($scope, $element, $attrs) {
var vm = this;
vm.list = [
{
name: 'Superman',
location: 'The sky'
},
{
name: 'Batman',
location: 'Baticueva'
}
];
vm.updateHero = function (hero, prop, value) {
hero[prop] = value;
};
vm.deleteHero = function (hero) {
var idx = vm.list.indexOf(hero);
if (idx >= 0) {
vm.list.splice(idx, 1);
}
};
}
})();
app-root component ( file name: helloworld.component)
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './helloworld.component.html',
styleUrls: ['./helloworld.component.scss']
})
export class HelloworldComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
app-root template
<p>helloworld works!</p>
angular.json ( angular-cli file )
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"common": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/common",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": [
"src/favicon.ico",
"src/assets" ],
"styles": [
"bower_components/jstree/dist/themes/default/style.min.css",
"bower_components/ng-tags-input/ng-tags-input.bootstrap.min.css",
"bower_components/utatti-perfect-scrollbar/css/perfect-scrollbar.css",
"bower_components/angular-material/angular-material.css",
"src/styles.scss"
],
"scripts": [
"bower_components/jquery/dist/jquery.js",
"bower_components/angular/angular.js",
"bower_components/angular-material/angular-material.js",
"bower_components/angular-animate/angular-animate.js",
"bower_components/angular-aria/angular-aria.js",
"bower_components/jstree/dist/jstree.js",
"bower_components/ng-js-tree/dist/ngJsTree.js",
"bower_components/ng-tags-input/ng-tags-input.js",
"bower_components/utatti-perfect-scrollbar/dist/perfect-scrollbar.js",
"bower_components/angular-perfect-scrollbar/src/angular-perfect-scrollbar.js",
"node_modules/@uirouter/angularjs/release/angular-ui-router.min.js",
"node_modules/underscore/underscore.js"
]
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "common:build"
},
"configurations": {
"production": {
"browserTarget": "common:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "common:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "common:serve"
},
"configurations": {
"production": {
"devServerTarget": "common:serve:production"
}
}
}
}
}},
"defaultProject": "common"
}
Result...