自定义元素未设置/获取属性

时间:2018-05-24 11:52:38

标签: javascript nested attributes custom-element

如果我创建两个自定义元素并在另一个元素中创建其中一个,则属性不会对第一个元素的新子元素起作用。



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;
&#13;
&#13;

我希望我的输出是

  

值#0

     

值#1

     

值#2

因为我已经像这样b.setAttribute('val', value #${i});

设置了attr

但我得到3x no value

有关原因的任何输入?和/或如何解决它,谢谢!

2 个答案:

答案 0 :(得分:2)

之后构造函数被调用之前,您才设置属性;记下日志记录:

&#13;
&#13;
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;
&#13;
&#13;

您需要将渲染逻辑移出构造函数,以便将构造后的属性设置为考虑因素。也许通过覆盖setAttribute

&#13;
&#13;
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;
&#13;
&#13;

从构造函数中调用方法并不理想,但是,您可能想稍微摆弄一下

答案 1 :(得分:2)

大多数人都不知道Web组件的构造函数的规则:

以下是官方文档:

https://w3c.github.io/webcomponents/spec/custom/#custom-element-conformance

摘要为:

您的构造函数代码:

  • 必须对构造函数中的第一个语句进行super()的无参数调用。
  • 在构造函数中的任何位置都不能有return语句。
  • 不得调用document.write()或document.open()。
  • 不得检查元素的属性。
  • 不得更改或添加任何属性或子项。

通常,构造函数应该用于设置初始状态和默认值,以及设置事件侦听器和可能的阴影根。

总的来说,我同意@ 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>