AngularJS + Angular 8 无法协同工作 [混合应用]

AngularJS + Angular 8 not working together [ Hybrid application ]

我有一个迁移大型 angularjs 应用程序的用例,我想首先在一个小型应用程序中执行此过程。因此,出于这个原因,我参观了英雄 angularjs webapp 并开始向其中添加 angular (通过使用 angular-cli 创建一个新项目)然后添加 NgUpgrade 模块。

我现在遇到的问题是 angularjs webapp 在 angular 8 应用程序中运行良好,但属于 angular 8 的组件未呈现。

我的印象是我的 angular 组件未被 bootstrapped,因为我已经手动引导 angularjs 但我不确定......当我明确添加时@NgModules 中的 bootstrap 属性 它仅适用于 angular 组件,但不适用于 angularjs(这是有道理的)。所以我在想也许我必须升级 angularjs 组件或降级最新的 angular 组件,但我不这么认为。

Here 您将找到包含代码的 git 存储库。以下是与我的项目相关的更多信息:

项目结构

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根模块)

// 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));

英雄列表组件(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组件(文件名: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 模板

<p>helloworld works!</p>

angular.json(angular-cli 文件)

{
  "$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"
}

结果...

隔离您的 Angular 1 代码和 Angular 8 代码

<body>
  <app-root></app-root>
<div ng-controller="MyController">
  <hero-list></hero-list>
</div>
</body>

最后我发现我需要的解决方案永远不会在我的应用程序的根目录下并排使用每个框架的两个组件。相反,我使用了以下模式作为 angular documentation 所说:

Once you're running a hybrid app, you can start the gradual process of upgrading code. One of the more common patterns for doing that is to use an Angular component in an AngularJS context. This could be a completely new component or one that was previously AngularJS but has been rewritten for Angular.

因此,由于我的旧应用程序是用 angularjs 编写的,因此该框架将始终包含我的根应用程序,并且还将成为我所有未来功能的包装器。所以如果有一天我决定在 Angular 8 中编写一个新组件,这个组件将存在于 angularjs 中并且它将被降级以使其能够工作。

我有一个类似的问题,即使在我的 index.html 我有这个:

<body>
  <app-root></app-root>
</body>

我发现通过 Angular docs for ngUpgrade 的设置,top-level 组件 需要 成为 Angular.js 中的组件,而不是一个来自 Angular:

<body>
  <hero-list></hero-list>
</body>