问题 如何使用Webpack或Angular CLI将角元素编译为Web组件?


我使用Pascal Precht通过Angular构建了一个简单的Web组件 教程,你可以看到工作 这里。它自动神奇地编译链接中的Stackblitz,但不是本地。

我的最终目标是将生成的Web组件的代码放在本地的单独文件中。 最终,我会将它上传到某个地方,并通过一个单一的方式将其上传 <script> 标签,就像普通的raw-html / javascript Web组件一样。我认为问题不言自明,但如果您愿意,可以阅读以下详细信息:


细节

要在上面的链接中总结我的代码,我有一个非常基本的组件:

import { Component } from '@angular/core';

@Component({
  selector: 'hello-world',
  template: `<h1>Hello world</h1>`
})

export class HelloComponent  {}

我有一个模块:

import { NgModule, Injector } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { createCustomElement } from '@angular/elements'
import { HelloComponent } from './hello.component';

@NgModule({
  imports: [BrowserModule],
  declarations: [HelloComponent],
  entryComponents: [HelloComponent]
})

export class AppModule { 
  constructor(private injector: Injector) {}
  ngDoBootstrap() {
    const HelloElement = createCustomElement(HelloComponent, {
      injector: this.injector 
    });

    customElements.define('hello-world', HelloElement);
  }
}

以下是上述模块的说明:

  1. 将我的组件添加到 entryComponents 数组,因此它不会被角度树振荡器取出(因为它在应用程序启动时无法访问: entryComponents: [HelloComponent] 
  2. 通过运行我的组件 createCustomElement 功能,以便我可以将它用作常规html Web Component

    const HelloElement = createCustomElement(HelloComponent,{   注射器:this.injector });

  3. 最后,我请Angular编译这个组件 main.ts

    platformBrowserDynamic().bootstrapModule(AppModule);


这是我完全阅读/观看的内容(在其他几十个链接中 - 大多数都是过时的,就像原始的Angular Elements简介一样):

来自TomekSułkowski的Scratch的Web组件 (他从不单独编译)
使用CLI的Web组件 (同样的问题)
Academind的Web组件 (再一次,这个家伙也在Angular应用程序中使用它们)

感谢您的任何帮助。


3525
2018-05-31 15:06


起源

@PrasannaSasne我没有发现它是问题的解决方案,虽然它确实帮助我理解了问题,这就是为什么你得到了赏金。 - VSO


答案:


import { NgModule} from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HelloComponent } from './hello.component';
import { AppComponent } from './app.component';

@NgModule({
  imports: [BrowserModule],
  declarations: [AppComponent, HelloComponent],
  entryComponents: [HelloComponent],
  bootstrap: [AppComponent]
})
export class AppModule { }

确保你使用

npm install --save @angular/elements 并添加 "@webcomponents/custom-elements" : "^1.0.8" 在package.json中。 在那之后 npm install & 除此之外,您还需要在polyfills.ts下面的行中取消注释

这会添加自定义元素工作所需的polyfill。

import '@webcomponents/custom-elements/custom-elements.min'; import '@webcomponents/custom-elements/src/native-shim';

<my-tag message="This is rendered dynamically">stack Overflow</my-tag>

Angular不会编译上面的代码,但是角度元素通过允许我们的角度组件并将其放入完全封装的自引导HTML元素来修复此问题,您可以通过以下方式将其转储到角度应用程序中,例如,工作。

在AppComponent.ts文件中

 import { Component, Injector } from '@angular/core'; 
 import { createCustomElement } from '@angular/elements'
 import { DomSanitizer } from '@angular/platform-browser';

 import { HelloComponent } from './hello.component';

 @Component({
  selector: 'app-root',
  template: '<div [innerHtml]="title"></div>',
  styleUrls: ['./app.component.css']
 })
 export class AppComponent {
 title = null;

 constructor(injector: Injector, domsanitizer: DomSanitizer){
   const customElement = createCustomElement(HelloComponent, {injector: 
   injector});

   //this feature is not provided by angular it is provided by javascript
   //this allows us to register custom web component
   customElements.define('my-tag', customElement);
   //instead of 'hello-world' i've used 'my-tag'    
   setTimeout(() => {
    //security mechanism is used to avoid cross site attacks
    this.title = domsanitizer.bypassSecurityTrustHtml('<my-tag message="This 
    is rendered dynamically">stack Overflow</my-tag>');     
    }, 1000);
   }
 }

在你的内心 HelloComponent 

 import { Component, OnInit, Input } from '@angular/core';

 @Component({
 selector: 'hello-world',
 template: `<div> hello component -- {{ message }}</div>`,
 styles: [`
  div {
    border: 1px solid black;
    background-color: red;
    padding: 1%;
   }
 `]
})
export class HelloComponent implements OnInit {
@Input() message : string;

constructor() { }

ngOnInit() {
}
}

现在这是作为本机Web组件加载的。仅在角度项目中可用,但已经可用于像这样的动态内容。

我希望这可以帮助您在本地运行代码


7
2018-06-11 07:26





从我读到的Angular 7之外的Angular Elements组件特有的Angular Elements组件包装。

你现在可以做的是用cli创建和角度应用。

ng new YourAppName

添加Angular Elements库:

ng add @angular/elements

这也增加了官方所述的所有必需的填充物 角度文档

然后,您将AppModule更改为不是引导程序模块,而只是注册自定义元素。您从NgModule中删除引导程序并将组件作为条目组件广告。然后将组件注册为自定义元素 ngDoBootstrap钩。我制作了默认的AppComponent和HelloComponent自定义元素。这就是我的app模块的样子:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';

import { AppComponent } from './app.component';
import { HelloComponent } from '../hello/hello.component';

@NgModule({
  declarations: [
    AppComponent,
    HelloComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  entryComponents: [AppComponent, HelloComponent]
})
export class AppModule {
  constructor(private injector: Injector) {
  }

  ngDoBootstrap() {
    customElements.define('app-root', createCustomElement(AppComponent, {injector: this.injector}));
    customElements.define('hello-world', createCustomElement(HelloComponent, {injector: this.injector}));
  }
 }

然后你可以像index.html一样使用index.html中的元素,例如:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>ElementsTest</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>
  <div>
    <app-root></app-root>
  </div>
  <div>
    <hello-world></hello-world>
  </div>
</body>
</html>

如果你用它来构建它 ng build --prod 通过包含编译器在index.html文件中包含的包脚本,您可以在其他html页面中获得最小化的包。

我已将我的样本添加到 GitHub上。在历史中,您可以看到我从最初的cli应用程序中所做的更改。


1
2018-06-11 22:34





当前的Angular版本不提供将组件导出为单个本地文件的选项,该文件可用于任何非角度应用程序。但是,可以通过更改构建和部署步骤来实现。在我的例子中,我创建了两个角度元素按钮和警告消息。这两个组件都被编译并导出为单个本地文件,我正在使用javascript在一个普通的html文件中加载。

以下是以下步骤: 1.在entryComponent列表中添加ButtonComponent和AlertComponent。在ngDoBootstrap中,将它们定义为自定义元素。这就是我的app.module的样子:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';

import { AppComponent } from './app.component';
import { ButtonComponent } from './button/button.component';
import { AlertComponent } from './alert/alert.component';

@NgModule({
  declarations: [AppComponent, ButtonComponent, AlertComponent],
  imports: [BrowserModule],
  entryComponents: [ButtonComponent, AlertComponent]
})
export class AppModule {
  constructor(private injector: Injector) {
  }

  ngDoBootstrap() {
    const customButton = createCustomElement(ButtonComponent, { injector: this.injector });
    customElements.define('my-button', customButton);

    const alertElement = createCustomElement(AlertComponent, { injector: this.injector});
    customElements.define('my-alert', alertElement);
  }
}
  1. 这是我的按钮组件:

    import {   输入,   零件,   ViewEncapsulation,   EventEmitter,   产量 来自'@ angular / core';

    @零件({   选择器:'custom-button',   模板: <button (click)="handleClick()">{{label}}</button>,   款式:[      button { border: solid 3px; padding: 8px 10px; background: #bada55; font-size: 20px; }   ]   encapsulation:ViewEncapsulation.Native }) 导出类ButtonComponent {   @Input()label ='default label';   @Output()action = new EventEmitter();   private clicksCt = 0;

    handleClick(){     this.clicksCt ++;     this.action.emit(this.clicksCt);   } }

  2. 这是我的警报组件:

    从'@ angular / core'导入{Component,Input,OnInit}; @零件({   选择器:'alert-message',   模板:'提醒消息:{{message}}',   款式:[     `     div {       border:1px solid#885800;       background-color:#ffcd3f;       填充:10px;       红色;       保证金:10px的;       font-family:Arial;

    }
    `]
    

    }) export class AlertComponent {   @Input()消息:string; }

  3. 在angular.json中构建配置:

    “build”:{   “builder”:“@ angular-devkit / build-angular:browser”,   “选项”:{     “outputPath”:“dist”,     “index”:“src / index.html”,     “main”:“src / main.ts”,     “polyfills”:“src / polyfills.ts”,     “tsConfig”:“src / tsconfig.app.json”,     “assets”:[“src / favicon.ico”,“src / assets”],     “styles”:[“src / styles.css”],     “剧本”:[       {         “输入”:           “node_modules /文件寄存器元件/建造/文档寄存器element.js”       }     ]   },   “配置”:{     “生产”: {       “fileReplacements”:[         {           “replace”:“src / environments / environment.ts”,           “with”:“src / environments / environment.prod.ts”         }       ]       “优化”:是的,       “输出哈希”:“全部”,       “sourceMap”:false,       “extractCss”:是的,       “namedChunks”:false,       “aot”:是的,       “extractLicenses”:是的,       “vendorChunk”:false,       “buildOptimizer”:是的     }   } }, “服务”:{   “builder”:“@ angular-devkit / build-angular:dev-server”,   “选项”:{     “browserTarget”:“angular6-elements:build”   },   “配置”:{     “生产”: {       “browserTarget”:“angular6-elements:build:production”     }   } }, “extract-i18n”:{   “builder”:“@ angular-devkit / build-angular:extract-i18n”,   “选项”:{     “browserTarget”:“angular6-elements:build”   } }

  4. 构建之后,我连接起来 runtime, polyfills, script 将js文件转换为单个脚本文件并导出 elements.js 其中包含自定义元素(可选:gzip这些文件)使用http-server deploy -gzip提供

    “脚本”:{   “ng”:“ng”,   “开始”:“ng serve”,   “build”:“ng build --prod --output-hashing = none”,   “package”:“npm run package-base && npm run package-elements”,   “package-base”:“cat dist / {runtime,polyfills,scripts} .js | gzip> deploy / script.js.gz”,   “package-elements”:“cat dist / main.js | gzip> deploy / elements.js.gz”,   “serve”:“http-server deploy --gzip”,   “测试”:“ng测试”,   “lint”:“ng lint”,   “e2e”:“ng e2e” }

  5. 最后我包括 script.js 和 elements.js 在index.html(在deploy目录中)告诉浏览器有关自定义元素的信息。现在我的按钮和我的警报可以包含在index.html中。在此示例中,按钮显示为加载状态,并且在单击按钮时动态添加警报消息(带有计数器编号)。这是代码:

         自定义按钮测试页面      

        const button = document.querySelector('my-button');     const msgContainer = document.querySelector('#message-container');     button.addEventListener('action',(event)=> {       的console.log("action" emitted: ${event.detail});       button.setAttribute(“label”,“Show Next Alert Message!”);       msgContainer.innerHTML + = <my-alert message="Here is a message #${event.detail} created dynamically using ng elements!!!"></my-alert>;     });

这是我的链接 我的git repo

希望这会有所帮助!

谢谢。


1
2018-06-12 16:22





你好。 

如果我理解正确,你想 生成Web组件 (比方说吧 <my-component></my-component)然后使用一个简单的脚本标记来获取.js文件以初始化该组件并将其添加到您想要的任何html页面上。

在我的 GitHub存储库 我创建了一个简单的Todo List Component。该组件遵循Angular Elements Principles,并且我已经为webpack安装了一些文件管理库,以便将JS打包到一个JS文件中。

您可以检查此存储库,看看是否可以帮助您解决问题。只需克隆它然后运行 npm安装 其次是 npm run build:elements 如果有任何事情发生,请随时与我联系。

还要检查一下 指南。这家伙给了我很多帮助。

祝你好运


1
2018-06-13 14:20