如果我创建两个自定义元素并在另一个元素中创建其中一个,则属性不会对第一个元素的新子元素起作用。
class Bar extends HTMLElement {
constructor() {
super();
const val = this.getAttribute('val') || 'no value';
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
wrapper.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
shadow.appendChild(wrapper);
}
}
customElements.define('x-bar', Bar);
class Foo extends HTMLElement {
constructor() {
super();
const loop = this.getAttribute('loop') || 10;
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
for(let i=0; i<loop; i++){
const b = document.createElement('x-bar');
b.setAttribute('val', `value #${i}`);
wrapper.appendChild(b);
}
shadow.appendChild(wrapper);
}
}
customElements.define('x-foo', Foo);
&#13;
<x-foo loop='3'></x-foo>
&#13;
我希望我的输出是
值#0
值#1
值#2
因为我已经像这样b.setAttribute('val', value #${i});
但我得到3x no value
有关原因的任何输入?和/或如何解决它,谢谢!
答案 0 :(得分:2)
在之后构造函数被调用之前,您才设置属性;记下日志记录:
class Bar extends HTMLElement {
constructor() {
super();
const val = this.getAttribute('val') || 'no value';
console.log("In constructor, val = " + val);
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
wrapper.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
shadow.appendChild(wrapper);
}
}
customElements.define('x-bar', Bar);
class Foo extends HTMLElement {
constructor() {
super();
const loop = this.getAttribute('loop') || 10;
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
for(let i=0; i<loop; i++){
console.log("Constructing...");
const b = document.createElement('x-bar');
console.log("Setting attribute");
b.setAttribute('val', `value #${i}`);
wrapper.appendChild(b);
}
shadow.appendChild(wrapper);
}
}
customElements.define('x-foo', Foo);
&#13;
<x-foo loop='3'></x-foo>
&#13;
您需要将渲染逻辑移出构造函数,以便将构造后的属性设置为考虑因素。也许通过覆盖setAttribute
:
class Bar extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
this.wrapper = document.createElement('div');
this.render(); // Though calling methods from the constructor isn't ideal
shadow.appendChild(this.wrapper);
}
setAttribute(name, value) {
super.setAttribute(name, value);
if (name === "val") {
this.render();
}
}
render() {
const val = this.getAttribute('val') || 'no value';
this.wrapper.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
}
}
customElements.define('x-bar', Bar);
class Foo extends HTMLElement {
constructor() {
super();
const loop = this.getAttribute('loop') || 10;
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
for(let i=0; i<loop; i++){
console.log("Constructing...");
const b = document.createElement('x-bar');
console.log("Setting attribute");
b.setAttribute('val', `value #${i}`);
wrapper.appendChild(b);
}
shadow.appendChild(wrapper);
}
}
customElements.define('x-foo', Foo);
&#13;
<x-foo loop='3'></x-foo>
&#13;
从构造函数中调用方法并不理想,但是,您可能想稍微摆弄一下
答案 1 :(得分:2)
大多数人都不知道Web组件的构造函数的规则:
以下是官方文档:
https://w3c.github.io/webcomponents/spec/custom/#custom-element-conformance
摘要为:
您的构造函数代码:
通常,构造函数应该用于设置初始状态和默认值,以及设置事件侦听器和可能的阴影根。
总的来说,我同意@ T.J。克劳德,但我会对typealias LoginHandler = (_ msg: String) -> Void
对象进行一次小修改:
Bar
这使用了class Bar extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
}
static get observedAttributes() {
// Indicate that we want to be notified
// when the `val` attribute is changed
return ['val'];
}
connectedCallback() {
// Render the initial value
// when this element is placed into the DOM
render(this.getAttribute('val'));
}
attributeChangedCallback(attrName, oldVal, newVal) {
if (oldVal != newVal) {
// If the value for the `val` attribute has changed
// then re-render this element
render(newVal);
}
}
render(val = 'no value') {
this.shadowRoot.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
}
}
customElements.define('x-bar', Bar);
和attributeChangedCallback
的标准。在覆盖observedAttributes
功能的同时,它不是未来的证明。如果setAttribute
的API要在将来更改,那么您需要记住修复组件。使用许多组件执行此操作会增加很多开发人员的债务。
setAttribute
第二种方法没有重新渲染整个DOM,只需将修改后的属性值插入class Bar extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
// Render the blank DOM
this.shadowRoot.innerHTML = '<div class="bar"><span>no value</span><div>';
this._span = this.shadowRoot.querySelector('span');
}
static get observedAttributes() {
// Indicate that we want to be notified
// when the `val` attribute is changed
return ['val'];
}
attributeChangedCallback(attrName, oldVal, newVal) {
if (oldVal != newVal) {
// If the value for the `val` attribute has changed
// then insert the value into the `<span>`
this._span.textContent = newVal || 'no value';
// OR: this._span.innerHTML = newVal || 'no value';
// But make sure someone has not tried to hit you
// with a script attack.
}
}
get val() {
return this._span.textContent;
}
set val(newVal) {
if (newVal == null || newVal === false || newVal === '') {
this.removeAttribute('val');
}
else {
this.setAttribute('val', newVal);
}
}
}
customElements.define('x-bar', Bar);
标记即可。
它还提供了属性,因此您可以通过属性以及通过JavaScript设置值:
<span>