AngularJS + Angular 8 не работают вместе [гибридное приложение]

У меня есть вариант использования для миграции большого приложения angularjs, и я хочу начать с выполнения этого процесса сначала в небольшом приложении. По этой причине я совершил экскурсию по веб-приложению angularjs героев и начал добавлять к нему angular (создав новый проект с angular-cli), а затем добавил модуль NgUpgrade.

Проблема, которая возникла у меня сейчас, заключается в том, что веб-приложение angularjs довольно хорошо работает внутри приложения angular 8, но компоненты, принадлежащие к angular 8, не отображаются.

У меня сложилось впечатление, что мои компоненты angular не загружаются, так как я вручную загрузил angularjs, но я не уверен ... когда я явно добавляю свойство bootstrap внутри @NgModules, оно работает только для компонентов angular, но не для angularjs ( это имеет смысл ). Итак, я подумал, может быть, мне нужно обновить компоненты angularjs или понизить версию новейших компонентов angular, но я так не думаю.

Здесь вы найдете репозиторий git с кодом. Ниже более подробная информация, связанная с моим проектом:

  • угловая версия: 8.2.8
  • версия angularJS: 1.6.10
  • угловой-cli: 8.3.6

Структура проекта

введите описание изображения здесь

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 (загрузка angularjs с помощью 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);
            }
        };
    }
})();

корневой компонент приложения (имя файла: 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() {
  }

}

шаблон корневого каталога приложения

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

Результат ...

Результат


person Maximiliano Becerra    schedule 01.10.2019    source источник
comment
попробуйте это в своем app.module.ts: this.upgrade.bootstrap (document.body, ['heroApp'], {strictDi: false});   -  person Maximilian Both    schedule 01.10.2019
comment
Привет @MaximilianBoth, спасибо за ваш комментарий, да, я пробовал, но это не сработало. Я не понимаю, как strictDi может мне здесь помочь, поскольку предназначен для управления использованием явной аннотации функций, как описано здесь: docs.angularjs.org/api/ng/directive/ngApp#with-ngstrictdi-   -  person Maximiliano Becerra    schedule 02.10.2019


Ответы (3)


Наконец, я понял, что решение, которое мне нужно, никогда не будет использовать оба компонента каждого фреймворка бок о бок в корне моего приложения. Вместо этого я использовал следующий шаблон в качестве угловой документации говорит:

Запустив гибридное приложение, вы можете начать постепенный процесс обновления кода. Один из наиболее распространенных шаблонов для этого - использование компонента Angular в контексте AngularJS. Это может быть совершенно новый компонент или тот, который ранее был AngularJS, но был переписан для Angular.

Так как мое старое приложение написано на angularjs, эта структура всегда будет содержать мое корневое приложение, а также будет оболочкой для всех моих будущих функций. Итак, если когда-нибудь я решу написать новый компонент в Angular 8, этот компонент будет жить внутри angularjs, и его версия будет понижена, чтобы он мог работать.

person Maximiliano Becerra    schedule 25.10.2019

Изолируйте код Angular 1 и код Angular 8

<body>
  <app-root></app-root>
<div ng-controller="MyController">
  <hero-list></hero-list>
</div>
</body>
person dota2pro    schedule 01.10.2019
comment
Привет @ dota2pro, спасибо за ваш комментарий, Tour of Heroes - это компонентное приложение, поэтому ваш подход невозможен. Даже если я загружаю конкретный div, поскольку приложение angularjs, а другой angular 8 живет снаружи (как метод изоляции), это не сработает. - person Maximiliano Becerra; 02.10.2019
comment
Тогда, возможно, вы сможете создать рабочую демонстрацию своего кода, которая лучше объяснит - person dota2pro; 02.10.2019
comment
Я добавил ссылку на github с проектом, если хотите поиграть :) - person Maximiliano Becerra; 03.10.2019

У меня была аналогичная проблема, хотя в моем index.html у меня было следующее:

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

Я обнаружил, что с настройкой Angular docs для ngUpgrade, компонент уровня должен быть из Angular.js, а не из Angular:

<body>
  <hero-list></hero-list>
</body>
person Daniel    schedule 06.08.2020