Angular 6 - 是否可以在同一页面上使用两个 Angular 元素组件?

29

我想托管独立的Angular元素,以便可以将其放入非Angular网页中。当我只有一个元素时,它很好地运行..但是当我在同一页上加载两个或更多时,我会收到此错误:

Uncaught Error: Zone already loaded.

我使用一个构建脚本将我的dist文件连接成一个js文件,然后将其放入我的项目中。

const fs = require('fs-extra')
const concat = require('concat')


const files = [
    './dist/elementsApp/runtime.js',
    './dist/elementsApp/polyfills.js',
    './dist/elementsApp/scripts.js',
    './dist/elementsApp/main.js',
]


fs.ensureDir('elements')
.then(() => {
  console.log('success!')
  concat(files, 'elements/include-me-somewhere-else.js')
})
.catch(err => {
  console.error(err)
})

我在一页上只能有一个这些js文件。 我知道Angular 7会使整个过程更加容易,但我想知道如何立即完成此操作。

我还尝试将导出的js文件包装在自执行函数中,以尝试限制范围...不幸的是,它并没有解决任何问题。

有什么想法吗?

人们似乎对Angular元素感到困惑。 为了澄清,您可以观看此视频https://www.youtube.com/watch?v=4u9_kdkvTsc,其中最后几分钟展示了导出的Angular元素,并将其包括在非Angular页面上。 我正在尝试在同一页上包含多个元素。


你在一个网站上使用两个Angular应用的目的是什么? - androbin
你是否将每个所需组件都制作成独立的模块? - MichaelSolati
我也很好奇,因为我最终也需要做这个。在非Angular应用程序上使用多个Angular元素。我成功创建了一个元素并在页面上显示,但是有多个冲突。 - Taranjit Kang
@hamobi,这个问题有什么更新吗?你有找到解决方案吗? - Milad
在 Angular 7 之前,没有解决此问题的方法。 - hamobi
显示剩余5条评论
4个回答

5

我认为你不应该在一个网站上使用两个Angular。

解决方案可以是:

  • 用Angular构建整个网站。其他部分的代码可以添加到应用程序中。
  • 使用子模块并使用父路由器合并两个应用程序。
  • 为每个Angular组件使用iframe。然后ngzone就不会发生冲突。

编辑:

自从我发布原始帖子以来,出现了一种新技术。使用微前端(MFE),您可以将更多应用程序添加到同一页中。在Angular中,这将成为一个新的路由器元素。任何地方都可以使用ESM import()进行懒加载,而Angular默认仅支持Router。


4
这是两个角度元素组件。它们旨在在Angular上下文之外作为小部件使用。它们是Angular 6中的新功能,但并不像传统意义上的Angular应用程序那样。 - hamobi
我已经成功地不使用 zones 来工作了。https://blog.angularindepth.com/do-you-still-think-that-ngzone-zone-js-is-required-for-change-detection-in-angular-16f7a575afef - Jared Christensen
从 Angular 13 开始,您可以使用微前端架构。使用它,您可以将两个应用程序添加到同一页中,并使用一些路由进行加载。 - androbin

5

根据我们之前的讨论,我的想法是这样的。因为每个包含元素的捆绑包都会加载zone.js,所以您会收到错误消息:

Uncaught Error: Zone already loaded.

你可以考虑的并且应该避免的是将'./dist/elementsApp/polyfills.js'与你打包生成的每个元素捆绑在一起。相反,将polyfill.js文件作为单独的导入到你的HTML中,并在导入任何和所有元素捆绑之前先导入它。

我会尝试一下并让您知道结果。 - hamobi
抱歉耽搁了。我尝试执行此操作,但出现错误:“错误:在此配置中,Angular 需要 Zone.js”。我确保在其他文件引用之前包含 polyfills.js。 - hamobi
无法工作,这与polyfill被加载的次数无关,而是当您加载两个Angular元素时,Zone本身被构建了两次。 - Milad

2
Google的Polymer可能是你寻找的,而不是Angular:

https://www.polymer-project.org/

Polymer库提供了一组功能,用于创建自定义元素。这些功能旨在使创建工作像标准DOM元素的自定义元素更加容易和快速。

1
谢谢您的建议,但是我的团队已经探索过了。这不是我们正在寻找的。我需要一个纯Angular解决方案来包含多个Angular元素。 - hamobi

1
这是一个在Angular 5+上完美运行的解决方案。对于Angular 6,我尚未进行过此操作。我已经在一个大型CMS项目中完成了此操作,并在多个页面中插入了组件,甚至在同一页中插入了两个相同的Angular模块。您可以使用一个Angular项目。在您的站点中,编写组件标记。您需要创建另一个模块(例如mainHelper),以导出每个选定的Angular标记。
import {NgModule, ApplicationRef, ComponentFactoryResolver} from '@angular/core';
import {FirstModule} from 'YOUR MODULE PATH';
import {FirstComponent} from 'YOU COMPONENT PATH';
import {SecondModule} from 'YOUR MODULE PATH';
import {SecondComponent} from 'YOU COMPONENT PATH';


@NgModule({
  imports: [
    FirstModule,
    SecondModule
  ],
})

export class MainHelper {
  constructor(private resolver: ComponentFactoryResolver) {
  }
  components = [
FirstComponent,
SecondComponent
  ];


  ngDoBootstrap(ref: ApplicationRef) {
    this.components.forEach((component) => {
      const factory = this.resolver.resolveComponentFactory(component);
      const elements = document.getElementsByTagName(factory.selector); //get angular tags on your main website
      if (elements.length > 0) {
        if (elements.length > 1) {
          const selector = factory.selector;
          for (let i = 0; i < elements.length; i++) {
            elements[i].id = selector + '-' + i;
            (<any>factory).factory.selector = '#' + elements[i].id;
            ref.bootstrap((<any>factory).factory);
            // elements[i].id = '';
          }
          (<any>factory).factory.selector = selector;
        } else {
          ref.bootstrap(component);
        }
      }
    });
  }
}

在你的 main.ts 中,你可以导入 MainHelper 模块并启动 MainHelper。
你的 FirstModule 应该是这样的:
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {BrowserModule} from '@angular/platform-browser'; 
import {FirstComponent} from './first.component';
import {AnotherComponent} from './another/another.component';
@NgModule({
  imports: [
    CommonModule,
    BrowserModule,
  ],
  providers: [
    SampleService // you need provide your services for all the modules
  ],
  declarations: [
    FirstComponent,
    AnotherComponent,
  ],
  bootstrap: [FirstComponent] // important
})
export class ECommerceModule {
}

我希望这可以帮助你。

1
请观看此视频:https://www.youtube.com/watch?v=4u9_kdkvTsc请注意观看最后部分,他将其Angular元素导出为一个连接的JavaScript文件,并将其添加到外部网站中。我正在尝试在同一页上包含两个这样的文件。 - hamobi
我明白了。这个周末我会进行检查,但是我更倾向于手动操作。我认为解决方案是,在想要引导angularElements时,需要对它们中的每一个进行引导。我会进一步研究。 - m.akbari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接