我无法让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),并且不起作用。
为什么哦为什么?
答案 0 :(得分:0)
自定义元素有两种类型:
自治自定义元素,它们是extend HTMLElement
自定义内置元素,这些类扩展了
特定类型的元素,例如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,
并以这种方式呈现(用于比较的原始“花式按钮”):
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)
}
}