Как скомпилировать угловой элемент в веб-компонент с Webpack или угловым CLI? - PullRequest
0 голосов
/ 31 мая 2018

Я создал простой веб-компонент через Angular, используя учебник Паскаля Прехта , который вы можете увидеть работающим ЗДЕСЬ .Он автоматически волшебным образом компилируется в Stackblitz по ссылке, но не локально.

Моя конечная цель состоит в том, чтобы код для результирующего веб-компонента был локально размещен в отдельном файле. В конце концов,Я буду загружать его куда-нибудь и извлекать с помощью одного тега <script>, как обычные веб-компоненты raw-html / javascript.Я думаю, что вопрос говорит сам за себя, но вы можете прочитать подробности ниже, если хотите:


Подробности :

Чтобы обобщить мой код в ссылкевыше, у меня есть очень простой компонент:

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, {injector: this.injector});

  3. НаконецЯ прошу Angular скомпилировать этот компонент в main.ts:

    platformBrowserDynamic().bootstrapModule(AppModule);


Вот материал, который я прочитал / посмотрел полностью (среди десятковдругие ссылки - большинство из которых датированы, как и в оригинальном введении Angular Elements):

Веб-компоненты с Scratch от Tomek Sułkowski (он никогда не компилирует его отдельно)
Веб-компоненты с CLI (Та же проблема)
Веб-компоненты от Academind (И снова этот пареньтакже использует их в приложениях Angular)

Спасибо за любую помощь.

Ответы [ 4 ]

0 голосов
/ 13 июня 2018

Привет.

Если я правильно понимаю, вы хотите создать веб-компонент (скажем, <my-component></my-component), а затем с помощью простого сценариятег, чтобы получить файл .js, чтобы инициализировать этот компонент и добавить его на любую html-страницу, которую вы хотите.

В моем GitHub репозитории я создал простой Компонент списка Todo.Этот компонент следует принципам угловых элементов, а также я установил несколько библиотек управления файлами для веб-пакета, чтобы также упаковать JS в один файл JS.

Вы можете проверить этот репозиторий и посмотреть, поможет ли это вам.Просто клонируйте его и запустите npm install , а затем npm run build: elements Не стесняйтесь связаться со мной, если что-нибудь пойдет на юг.

Также проверьте это руководство.Этот парень мне очень помог.

Удачи

0 голосов
/ 12 июня 2018

Из того, что я прочитал, упаковка, специфичная для компонентов Angular Elements для легкого использования вне Angular, будет поставляться с Angular 7.

Теперь вы можете создать угловое приложение с помощью cli.

ng new YourAppName

Добавьте библиотеку угловых элементов с помощью:

ng add @angular/elements

Также будут добавлены все необходимые полифилы, как описано в официальной угловой документации .

Затем вы изменяетеAppModule не должен быть модулем начальной загрузки, а просто регистрировать пользовательские элементы.Вы удаляете начальную загрузку из NgModule и рекламируете компоненты как входные компоненты.Затем зарегистрируйте компоненты как пользовательские элементы в хуке ngDoBootstrap.Я сделал пользовательские элементы AppComponent и HelloComponent по умолчанию.Вот как выглядит мой модуль приложения:

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, например, такие элементы, как это:

<!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 вы получаете свернутые пакеты, которые теперь можно использовать и на других html-страницах, включив сценарии пакетов по мере их включения компилятором в файл index.html.

Я добавил свой пример в GitHub .В истории вы можете увидеть, что я изменил по сравнению с первоначальным приложением cli.

0 голосов
/ 12 июня 2018

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

Вот следующие шаги: 1. Добавьте ButtonComponent и AlertComponent в список entryComponent.В 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);
  }
}

Вот мой компонент кнопки:
import {
  Input,
  Component,
  ViewEncapsulation,
  EventEmitter,
  Output
} from '@angular/core';

@Component({
  selector: 'custom-button',
  template: `<button (click)="handleClick()">{{label}}</button>`,
  styles: [
    `
    button {
      border: solid 3px;
      padding: 8px 10px;
      background: #bada55;
      font-size: 20px;
    }
  `
  ],
  encapsulation: ViewEncapsulation.Native
})
export class ButtonComponent {
  @Input() label = 'default label';
  @Output() action = new EventEmitter<number>();
  private clicksCt = 0;

  handleClick() {
    this.clicksCt++;
    this.action.emit(this.clicksCt);
  }
}
Вот мой компонент оповещения:
import { Component, Input, OnInit } from '@angular/core';
@Component({
  selector: 'alert-message',
  template: '<div>Alert Message: {{message}}</div>',
  styles: [
    `
    div {
      border: 1px solid #885800;
      background-color: #ffcd3f;
      padding: 10px;
      color: red;
      margin:10px;
      font-family: Arial;

    }
    `]
})
export class AlertComponent {
  @Input () message: string;
}
Создание конфигураций в angular.json:
"build": {
  "builder": "@angular-devkit/build-angular:browser",
  "options": {
    "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"],
    "scripts": [
      {
        "input":
          "node_modules/document-register-element/build/document-register-element.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
    }
  }
},
"serve": {
  "builder": "@angular-devkit/build-angular:dev-server",
  "options": {
    "browserTarget": "angular6-elements:build"
  },
  "configurations": {
    "production": {
      "browserTarget": "angular6-elements:build:production"
    }
  }
},
"extract-i18n": {
  "builder": "@angular-devkit/build-angular:extract-i18n",
  "options": {
    "browserTarget": "angular6-elements:build"
  }
}
После сборки я объединяю runtime, polyfills, script js файлы в один файл сценария и экспортирую elements.js, который содержит пользовательские элементы (необязательно: gzip эти файлы), обслуживает его с помощью http-server deploy --gzip
"scripts": {
  "ng": "ng",
  "start": "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",
  "test": "ng test",
  "lint": "ng lint",
  "e2e": "ng e2e"
}
Наконец, я включаю script.js и elements.js в index.html (в каталоге deploy), чтобы сообщить браузеру о пользовательских элементах.Теперь my-button и my-alert могут быть включены в index.html.В этом примере кнопка отображается при загрузке, а оповещение добавляется динамически (с номером счетчика) при нажатии кнопки.Вот код:
    <!DOCTYPE html>
    <html lang="en">

    <head>
      <meta charset="UTF-8">
      <title>Custom Button Test Page</title>
      <script src="script.js"></script>
      <script src="elements.js"></script>
    </head>

    <body>
      <my-button label="Show Alert Message!"></my-button>

      <p></p>

      <div id="message-container"></div>

      <script>
        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>`;
        });

      </script>
    </body>

    </html>

Вот моя ссылка для моего репозитория git

Надеюсь, это поможет!

Спасибо.

0 голосов
/ 11 июня 2018
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

Это добавляет полифилл, необходимый для работы пользовательских элементов.

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() {
}
}

Теперь он загружен как собственный веб-компонент. До сих пор используется только в угловых проектах, но уже пригодный для работы с контентом dyanamic, подобным этому.

Надеюсь, это поможет вам запустить ваш код локально

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...