Подтвердить что ты не робот

Динамическая загрузка внешнего веб-пакета в комплекте ngModule в качестве обработчика маршрута

Мы хотим разделить наши крупные проекты с интерфейсом на несколько отдельно развернутых проектов, с которыми легче работать. Я пытаюсь включить связанный ngModule для обработки маршрута из другого приложения. Приложения должны не знать друг друга. Пакеты будут делиться некоторыми большими зависимостями (например, Angular) через глобальные переменные. Нам не нужно встряхивать пучки, и нам просто нужно принять некоторые дублирующие зависимости.

Корневой маршрутизатор жалуется, что

Error: No NgModule metadata found for 'TestsetModule'.

что заставляет меня думать, что дочерний модуль не является angular, скомпилированным при загрузке, или по какой-либо причине не регистрирует его модуль. Я думаю, что может понадобиться вручную скомпилировать модуль, но я не уверен, как использовать этот https://angular.io/api/core/Compiler#compileModuleAndAllComponentsAsync

Корневое приложение загружает ребенка по маршруту:

import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const load = require("little-loader");


const routes: Routes = [
  { path: ``, loadChildren: () => new Promise(function (resolve) {
      load('http://localhost:3100/testset-module-bundle.js',(err: any) => {
        console.log('global loaded bundle is: ', (<any>global).TestsetModule )
        resolve((<any>global).TestsetModule)
      }
    )
  })}
];

export const HostRouting: ModuleWithProviders = RouterModule.forRoot(routes);

Я также попытался использовать синтаксис синтаксиса строкового синтаксиса angular, а не эту странную глобальную вещь, которую вы видите, но у меня были подобные проблемы.

Вот загружаемый модуль, очень стандартный, за исключением глобального экспорта:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpModule } from '@angular/http';
//import { MaterialModule } from '@angular/material';
import { FlexLayoutModule } from '@angular/flex-layout';
import { FormsModule }   from '@angular/forms';
import { LoggerModule, Level } from '@churro/ngx-log';

import { FeatureLoggerConfig } from './features/logger/services/feature-logger-config';


import { TestsetComponent } from './features/testset/testset.component';
import { TestsetRouting } from './testset.routing';

@NgModule({
    imports: [
        CommonModule,
        //MaterialModule,
        FlexLayoutModule,
        HttpModule,
        FormsModule,
        LoggerModule.forChild({
          moduleName: 'Testset',
          minLevel: Level.INFO
        }),
        TestsetRouting,
    ],
    declarations: [TestsetComponent],
    providers: [
      /* TODO: Providers go here */
    ]
})
class TestsetModule { }
(<any>global).TestsetModule = TestsetModule

export {TestsetModule as default, TestsetModule};

Вот конфигурация веб-пакета корневого пакета. Обратите внимание на глобальный экспорт через слабо названный "ProvidePlugin".

const webpack = require('webpack');
const AotPlugin = require('@ngtools/webpack').AotPlugin;
const path = require('path');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const IgnorePlugin = require('webpack/lib/IgnorePlugin');
const PolyfillsPlugin = require('webpack-polyfills-plugin');
const WebpackSystemRegister = require('webpack-system-register');


module.exports = (envOptions) => {
    envOptions = envOptions || {};
    const config = {

        entry: {
          'bundle': './root.ts'
        },
        output: {

          libraryTarget: 'umd',
          filename: '[name].js',//"bundle.[hash].js",
          chunkFilename: '[name]-chunk.js',
          path: __dirname
          },
          externals: {

          },
        resolve: {
            extensions: ['.ts', '.js', '.html'],
        },
        module: {
            rules: [
                { test: /\.html$/, loader: 'raw-loader' },
                { test: /\.css$/, loader: 'raw-loader' },

            ]
        },
        devtool: '#source-map',
        plugins: [
          new webpack.ProvidePlugin({
            'angular': '@angular/core',
            'ngrouter': '@angular/router',
            'ngxlog':'@churro/ngx-log'
          })

        ]
    };
    config.module.rules.push(
      { test: /\.ts$/, loaders: [
        'awesome-typescript-loader', 
        'angular-router-loader',
        'angular2-template-loader', 
        'source-map-loader'
      ] } 
    );
  }



    return config;
};

И вот конфигурация веб-пакета дочернего пакета. Обратите внимание на "внешние", которые ищут angular как глобальный.

module.exports = (envOptions) => {
    envOptions = envOptions || {};
    const config = {
        entry: {
          'testset-module-bundle': './src/index.ts'
        },
        output: {
          //library: 'TestsetModule',
          libraryTarget: 'umd',
          filename: '[name].js',//"bundle.[hash].js",
          chunkFilename: '[name]-chunk.js',
          path: path.resolve(__dirname, "dist")
          },
          externals: {
            //expect these to come from the app that imported us
            // name to be required : name from global
             'angular': '@angular/core',
             'ngrouter': '@angular/router',
             'ngxlog': '@churro/ngx-log'       
          },
        resolve: {
            extensions: ['.ts', '.js', '.html'],
        },
        module: {
            rules: [
                { test: /\.html$/, loader: 'raw-loader' },
                { test: /\.css$/, loader: 'raw-loader' },

            ]
        },
        devtool: '#source-map',
        plugins: [

        ]
    };

    config.module.rules.push(
      { test: /\.ts$/, loaders: [
        'awesome-typescript-loader', 
        'angular-router-loader',
        'angular2-template-loader', 
        'source-map-loader'
      ] }
    );
  }



    return config;
};

И для хорошей меры здесь находится мой файл tsconfig, который читает "awesome- typescript -loader".

{
  "compilerOptions": {
    "target": "es5",
    "module": "es2015",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true,
    "baseUrl": ".",
    "rootDir": "src",
    "outDir": "app",
    "paths": {
      "@capone/*": [
        "*"
      ],
      "@angular/*": [
        "node_modules/@angular/*"
      ],
      "rxjs/*": [
        "node_modules/rxjs/*"
      ]
    }
  },

  "exclude": ["node_modules", "src/node_modules", "compiled", "src/dev_wrapper_app"],
  "angularCompilerOptions": {
    "genDir": "./compiled",
    "skipMetadataEmit": true
  }
}

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

4b9b3361

Ответ 1

Как вы уже упоминали

Приложения должны не знать друг друга.

У меня была аналогичная проблема в Angular2. Я решил это, создав под-приложение. Отдельный файл sub-main.browser.ts и index.html. Он имел свои собственные зависимости, используя одни и те же модули node. Оба основных модуля загружают различные компоненты приложения. Мы работали над Angular без angular -cli.

В конфигурации webpack я добавил

entry: {

  'polyfills': './src/polyfills.browser.ts',
  'main' .   :     './src/main.browser.aot.ts',
  'sub-main' : '/src/sub-main.browser.ts'

},

и более подробный HtmlWebpackPlugin. В кусках мы загружаем только модули, которые будут использоваться в обоих приложениях. Если мы видим, что полисы являются общими.

   new HtmlWebpackPlugin({
    template: 'src/index.html',
    title: METADATA.title,
    chunksSortMode: 'dependency',
    metadata: METADATA,
    inject: 'head',
    chunks: ['polyfills','main']
  }),

  new HtmlWebpackPlugin({
    template: 'src/index2.html',
    title: 'Sub app',
    chunksSortMode: 'dependency',
    metadata: METADATA,
     inject: 'head',
    filename: './sub-app.html',
    chunks: ['polyfills','sub-main']
  }),

Следующей задачей было создание отдельных конечных точек для обоих приложений для среды разработки.

devServer: {
      port: METADATA.port,
      host: METADATA.host,
      historyApiFallback: true,
      watchOptions: {
        aggregateTimeout: 300,
        poll: 1000
      },
      proxy: {
   "/sub-app": {
    target: "http://localhost:3009",
    bypass: function(req, res, proxyOptions) {
        return "/index2.html";
    }
  }
}
    },

Теперь, когда я создаю проект, генерируются два разных файла HTML. Каждый из них имеет свои собственные зависимости javascript и общие активы. Они также могут быть развернуты на другом сервере.

Я смог закончить свой POC с большим количеством проб и ошибок. Мое предложение - посмотреть на шаг выше angular. Посмотрите, как webpack развертывает ваш текущий проект. И если вы можете настроить его для своей цели.