自定义元素:属性不会更新组件状态

时间:2019-08-18 10:22:24

标签: javascript html web-component

我是Web组件的新手,当我用html编写组件并直接在html中添加属性时遇到问题。 问题在于该组件不会触发set属性函数。

仅当我设置属性或使用javascript创建组件并将其添加到DOM时,set属性函数才有效。

我创建了一支笔来举例说明我的问题:

Go to Pen

window.addEventListener('load', () => {
  document.body.getElementsByTagName('news-article')[1].article = {
    title: 'dynamic value'
  };

  let element = document.createElement('news-article');
  element.article = {
    'title': 'dynamic element'
  };

  document.body.appendChild(element);
})

class NewsArticle extends HTMLElement {

  static get observedAttributes() {
    debugger
    return ['article'];
  }

  constructor() {
    debugger
    super();
    this.root = this.attachShadow({
      mode: 'open'
    });
  }

  set article(val) {
    debugger
    this.root.innerHTML = `
						<style>
							:host {
								display: block;
								border: 3px solid #000;
								padding:	15px;
							}

							h2 {
								text-transform: uppercase;
							}
						</style>


						<h2>${val.title}</h2>
					`;
  }

  get article() {
    debugger
    return this.getAttribute('article');
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    debugger
    this.setAttribute(attrName) = JSON.parse(newVal);
  }
}

window.customElements.define('news-article', NewsArticle);
<news-article article="{ title: 'static value' }"></news-article>
<news-article></news-article>

2 个答案:

答案 0 :(得分:2)

@connexo对属性发生了什么,如何操作它们以及如何提取它们的值进行了很好的解释。

我想通过稍微改变结构来补充他的解决方案。

字母和setter::使用字母和setter操纵元素上的属性。这样,您可以通过使用article更改属性值和HTML来更改news-article.article = { title: 'Breaking news' }属性,并使用news-article.article获取article属性的当前值。

因为您正在观察article属性,所以当您更改attributeChangedCallback属性值时,它将触发article。您应该放置逻辑,以更改所有属性值。根据您的情况,更改Shadow DOM的innerHTML

class NewsArticle extends HTMLElement {

  /**
   * Fire the attributeChangedCallback when the article
   * attribute has been changed.
   */
  static get observedAttributes() {
    return ['article'];
  }

  constructor() {
    super();
    this.attachShadow({
      mode: 'open'
    });
  }

  /** 
   * Set the article attribute value.
   *
   * This will fire the attributeChangedCallback because
   * 'article' is in the observedAttributes array.
   */
  set article(val) {
    this.setAttribute('article', JSON.stringify(val));
  }

  /** 
   * Gets the current article attribute value.
   */
  get article() {
    return JSON.parse(this.getAttribute('article'));
  }

  /** 
   * Do something when an attribute is changed.
   *
   * In this case change the innerHTML of the shadowRoot
   * when the 'article' attribute has changed.
   */
  attributeChangedCallback(attrName, oldVal, newVal) {
    if (attrName === 'article') {
      const { title } = JSON.parse(newVal);
      this.shadowRoot.innerHTML = `
        <style>
          :host {
            display: block;
            border: 3px solid #000;
            padding: 15px;
          }

          h2 {
            text-transform: uppercase;
          }
         </style>
         <h2>${title}</h2>`;
    }
  }

}

功劳不错,应该去@connexo!

答案 1 :(得分:1)

setAttribute接受两个参数,相反,您将分配 JSON.parse(newVal)到任何this.setAttribute(attrName)返回的值(我假设为undefined)。

this.setAttribute(attrName) = JSON.parse(newVal);

必须

this.setAttribute(attrName, JSON.parse(newVal));

最重要的是,请注意{ title: 'static value' }不是有效的JSON。您不能使用单引号将键或值引起来。它必须是双引号。

接下来,出于两个原因,在您的this.setAttribute(attrName, JSON.parse(newVal))中进行attributeChangedCallback毫无意义。

  1. 该属性已经处于设置状态(这就是执行attributeChangedCallback的原因)
  2. 属性只能包含String个值。

相反,您要做的是

this.article = JSON.parse(newVal);

这将触发您的吸气剂(这正是您想要的,因为那是真正更新您的组件的东西)。

我认为您的误解是由于您假设自定义属性会自动与sanme-name属性进行同步,而情况并非如此

window.addEventListener('load', () => {
  document.body.getElementsByTagName('news-article')[1].article = {
    title: 'dynamic value'
  };

  let element = document.createElement('news-article');
  element.article = {
    'title': 'dynamic element'
  };

  document.body.appendChild(element);
})

class NewsArticle extends HTMLElement {

  static get observedAttributes() {
    return ['article'];
  }

  constructor() {
    super();
    this.attachShadow({
      mode: 'open'
    });
  }

  set article(val) {
    this.shadowRoot.innerHTML = `
<style>
  :host {
    display: block;
    border: 3px solid #000;
    padding:	15px;
  }

  h2 {
    text-transform: uppercase;
  }
</style>


<h2>${val.title}</h2>`;
					
  }

  get article() {
    return this.getAttribute('article');
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    this.article = JSON.parse(newVal);
  }
}

window.customElements.define('news-article', NewsArticle);
<news-article article='{ "title": "static value" }'></news-article>
<news-article></news-article>
<news-article article='{ "title": "static value" }'></news-article>