Чтение данных из веб-компонента Angular Elements в простом JavaScript

Я создаю несколько WebComponent, которые буду использовать в разных библиотеках / фреймворках, но в основном с простой ванилью JavaScript. Я подумываю об использовании для этого Angular Elements и создал образец из нескольких документов.

Он отлично работает, со всеми встроенными естественными особенностями Angular. Но я борюсь с чтением данных из веб-компонента из простого JavaScript, что-то вроде var result = myComponent.value.

  • Обратите внимание, что эти данные, которые я читаю, не являются примитивными или однозначными. Это как объект со множеством деталей из сложного WebComponent
  • Я бы хотел что-то вроде readonly property
  • В стандартных Angular документах по компонентам для этой цели всегда используется @Output() с EventEmitter. Но поскольку я буду использовать компонент как WebComponent вне Angular мира, я не могу (и не хочу) использовать этот подход. Кроме того, я не хочу уведомлять о каждом изменении значения, а просто считываю значение, когда это необходимо.

Если я напишу стандартный WebComponent простым JavaScript, я могу сделать это

class MyComponent extends HTMLElement {

    constructor() {
        super();
        this.name = 'Guest';
        this.count = 0;
    }

    // more code...

    // THIS result PROPERTY IS USED TO READ DATA FROM THE WEB COMPONENT
    get result() {
        return { name: this.name, count: this.count }; // <<== to read data
    }

    connectedCallback() {
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.innerHTML = `
            <style>
                styles....
            </style>
            <h1>MyComponent</h1>
            More HTML...
        `;
    }
}

customElements.define('my-component', MyComponent);

// Then I can use <my-component></my-component> in my application HTML
// And in the JavaScript code, I can read data from my-component as

const result = document.querySelector('my-component').result;
// which would be something like { name: 'Guest', count: 0 }

Есть ли способ сделать это в AngularElements?


person Arghya C    schedule 16.06.2020    source источник
comment
Можете ли вы использовать простой JS Object.defineProperty?   -  person Danny '365CSI' Engelman    schedule 16.06.2020
comment
@ Danny'365CSI'Engelman Не могли бы вы объяснить, как Object.defineProperty() поможет в этом сценарии?   -  person Arghya C    schedule 16.06.2020
comment
Из вашего кода NG определите свойство только с геттером, возвращающим объект, тогда поведение будет точно таким же, как у геттера result в вашем ванильном коде выше.   -  person Danny '365CSI' Engelman    schedule 16.06.2020


Ответы (1)


Хорошо, у меня есть 2 обходных пути, чтобы справиться с этим! Они больше похожи на хаки, так как (1) они не выполняются стандартным Angular способом и (2) могут сломаться в будущем из-за изменений реализации в Angular. Ниже приведены 2 способа получить output property для WebComponent, построенного с Angular Elements

(Я использую "@angular/core": "~9.1.11" & "@angular/elements": "^9.1.11")

  1. Мой коллега указал, что createCustomElement предоставляет только входные данные как свойства. См. Код // The component import { Component, OnInit, ViewEncapsulation, Input, ElementRef } from '@angular/core'; @Component({ // selector, templateUrl & styleUrls encapsulation: ViewEncapsulation.ShadowDom, }) export class MyComponent implements OnInit { constructor( private element: ElementRef, ) { // THIS IS APPROACH 2 => we are defining a 'result2' property on the HTMLElement Object.defineProperty(this.element.nativeElement, 'result2', { get: () => { return { name: this.name, count: this.count }; }, enumerable: true, }); } public name = 'World'; public count = 0; // More code for component functionalities and state management... // Just note that this does NOT work i.e. cannot read it from outside public get result0(): ElementMetadata { return { name: this.name, count: this.count }; } public set result0(value) { console.log(`result value set to ${value}`); } // THIS IS APPROACH 1 => we are marking 'result1' with a getter, as @Input @Input() public get result1(): ElementMetadata { return { name: this.name, count: this.count }; } } // The module (no changes made specifically for this) export class AppModule { constructor(private injector: Injector) { } ngDoBootstrap() { const custElement = createCustomElement( MyComponent, { injector: this.injector }, ); customElements.define('my-component', custElement); } }
    person Arghya C    schedule 17.06.2020