扩展自定义元素无法在Angular 2 +

时间:2019-03-21 19:10:00

标签: javascript angular

我无法让Angular(2+,而不是AngularJS)与我的扩展自定义元素一起玩,它的定义如下:

  class FancyButton extends HTMLButtonElement {
    connectedCallback() {
      this.innerText = `I'm a fancy-button!`;
      this.style.backgroundColor = 'tomato';
    }
  }

  customElements.define("fancy-button", FancyButton, {
    extends: "button"
  });

并按如下方式使用:

<button is="fancy-button">Fancy button here</button>

根据此Google Developer资源,该定义完全符合网络标准: https://developers.google.com/web/fundamentals/web-components/customelements#extend

它在普通的Web设置和React中都能正常工作,但Angular会忽略它并显示一个标准按钮,显然会忽略 is =“ fancy-button” 属性。

这里是stackblitz,显示了此操作。 一个精美的按钮不在Angular范围(index.html)内,并且工作正常。 另一个按钮在Angular范围内(app.component.html),并且不起作用。

为什么哦为什么?

2 个答案:

答案 0 :(得分:0)

自定义元素有两种类型:

  1. 自治自定义元素,它们是extend HTMLElement

    的类
  2. 自定义内置元素,这些类扩展了 特定类型的元素,例如extend HTMLButtonElement

Angular不支持自定义内置元素(更多详细信息) 在下面)。 Safari仍不支持它们(自 2020年9月:https://caniuse.com/#search=custom%20elements )。

您的示例是“自定义内置元素”。解决方法 对我而言,工作是将任何自定义内置元素重写为自治元素 自定义元素环绕内置元素。这给了我 我想要的封装,它为我提供了一种自定义内置函数的方法,并且可以与Angular和Safari一起使用。

对于上面的示例,翻译为:

class FancyButtonToo extends HTMLElement {
  connectedCallback() {
    const buttonElement = document.createElement('button');
    this.appendChild(buttonElement);
    buttonElement.innerText = "Fancy button #2 here!";
    buttonElement.style.backgroundColor = 'tomato';
  }
}

customElements.define("fancy-button-too", FancyButtonToo);

(该项目还需要添加schemas: [ CUSTOM_ELEMENT_SCHEMAS]app.module.ts)。完整代码在这里:stackblitz, 并以这种方式呈现(用于比较的原始“花式按钮”):

enter image description here

其他信息

问:我们确定Angular无法支持自定义内置元素(例如 相对于某些我们不知道的模糊配置?)

A:我们可以肯定:位于https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-autonomous-example的规格文件 详细介绍了自治自定义元素之间的差异 和自定义的内置元素。一个重要的区别是 元素的程序化构造:

// Built-in Elements and Autonomous Custom Elements are created like this:
el = createElement(name);
// examples
el = createElement("button"); // normal built-in button
el = createElement("fancy-text"); // a custom element

// Customized Built-in Elements are created like this:
el = createElement(built-in-name, { is: custom-built-in-name });
// example
el = createElement("button", { is: "fancy-button" });

相关的Angular模板渲染代码位于 https://github.com/angular/angular/blob/master/packages/platform-browser/src/dom/dom_renderer.ts 从Angular当前版本开始,您会发现:

class DefaultDomRenderer2 implements Renderer2 {
  /* ... */
  createElement(name: string, namespace?: string): any {
    if (namespace) {
      return document.createElementNS(NAMESPACE_URIS[namespace] || namespace, name);
    }
    return document.createElement(name);
  }
  /* ... */
}

Angular渲染器当前没有额外的代码 需要将第二个参数传递给createElement();这不可以 创建自定义内置元素。

答案 1 :(得分:0)


class DefaultDomRenderer2 implements Renderer2 {
  // ...
  create(name: string, namespaceOrOptions?: string | ElementCreationOptions) {
    if (namespaceOrOptions && typeof namespaceOrOptions === 'string') {
      // In cases where Ivy (not ViewEngine) is giving us the actual namespace, the look up by key
      // will result in undefined, so we just return the namespace here.
      return document.createElementNS(NAMESPACE_URIS[namespaceOrOptions] || namespaceOrOptions, name);
    }
    if (namespaceOrOptions && namespaceOrOptions.hasOwnProperty('is')) {
      return document.createElement(name, namespaceOrOptions as ElementCreationOptions);
    }
    return document.createElement(name)
  }
}