用纯JavaScript从Angular元素Web组件中读取数据

3
我正在构建一些WebComponent,我将在不同的库/框架中使用它们,但大多数是使用原生的JavaScript。我正在考虑使用Angular Elements 来实现这个目标,并根据几个文档构建了一个示例。
它可以正常工作,并包含所有自然的Angular好处。但是,我遇到的问题是,如何从普通的JavaScript中读取Web组件中的数据,类似于var result = myComponent.value
  • 请注意,我要读取的数据不是原始数据或单个值。它类似于从复杂的WebComponent中获取许多详细信息的对象。
  • 我希望像readonly property那样做。
  • 标准的Angular组件文档总是使用@Output()EventEmitter来实现此目的。但由于我将在Angular之外的WebComponent中使用该组件,因此我不能(也不想)使用这种方法。另外,我不想在每次更改值时都进行通知,而只是在需要时读取该值。
如果我用普通的JavaScript编写标准的WebComponent,我可以这样做。
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 中实现吗?

你能使用纯JS的Object.defineProperty吗? - Danny '365CSI' Engelman
@Danny'365CSI'Engelman,您能解释一下Object.defineProperty()在这种情况下如何有所帮助吗? - Arghya C
1
从你的NG代码中定义一个只有getter返回对象的属性,行为与上面你原始代码中的“result” getter完全相同。 - Danny '365CSI' Engelman
1个回答

0

好的,我现在有两种解决方法来处理它!它们更像是hack,因为(1)它们不是按照标准的Angular方式完成的,(2)可能会因为Angular内部实现的更改而在未来出现问题。以下是我可以通过这两种方式实现使用Angular Elements构建的WebComponentoutput property的方法。

(我正在使用"@angular/core": "~9.1.11""@angular/elements": "^9.1.11")

我的同事指出,createCustomElement 只将输入作为属性公开。请参见代码here。因此,hack 是将属性标记为 Input(),然后像普通属性一样从外部读取它!这非常不直观,也没有遵循任何标准语义。有趣的是,如果我只有一个使用 @Input() 装饰的属性的 getterAngular 不会抱怨。
如评论中 Danny '365CSI' Engelman 所指出的那样,更好的方法是通过使用Object.defineProperty 在本机 HTML 元素上定义一个新属性。
请注意,与选项(1)不同,更好的方法可能是增强/扩展 Angular 中的逻辑,以包括组件类中的其他属性作为元素属性,也许可以使用新的自定义装饰器,例如 @outputProperty

使用上述两种方法的示例代码实现

// 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);
    }
}

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