自定义元素:属性不会更新组件状态
custom element: attribute won't update component state
我是 Web 组件的新手,在 html 中编写组件并直接在 html 中添加属性时遇到问题。
问题是组件没有触发 set 属性 函数。
仅当我使用 javascript 设置属性或创建组件并将其添加到 DOM 时,set 属性 函数才有效。
我创建了一支笔来举例说明我的问题:
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>
setAttribute
接受两个参数,而不是你 将 JSON.parse(newVal)
分配给任何 this.setAttribute(attrName)
returns (我假设 undefined
).
this.setAttribute(attrName) = JSON.parse(newVal);
必须是
this.setAttribute(attrName, JSON.parse(newVal));
除此之外,请注意 { title: 'static value' }
无效 JSON。您不能使用单引号来引用键或值。必须是双引号。
接下来,在您的 attributeChangedCallback
中执行 this.setAttribute(attrName, JSON.parse(newVal))
没有意义,原因有二:
- 该属性已经在被设置为该属性的过程中(这就是为什么您的
attributeChangedCallback
正在被执行)
- 属性只能包含
String
个值。
相反,你想要做的是
this.article = JSON.parse(newVal);
这将触发您的 getter(这是您想要的,因为它才是真正更新您的组件的)。
我假设您的误解是由于您假设自定义属性自动与同名 属性 同步 - 但并非如此。
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>
@connexo 对属性发生了什么、如何操作它们以及如何提取它们的值给出了很好的解释。
我想通过稍微更改结构来添加他的解决方案。
Getters 和 setters: 使用 getters 和 setters 来操作元素的属性。这样你就可以通过使用news-article.article = { title: 'Breaking news' }
改变属性值和HTML来改变article
属性,并使用news-article.article
得到当前值article
属性。
因为您正在观察 article
属性,所以当您更改 article
属性值时,它会触发 attributeChangedCallback
。你应该把你的逻辑放在那里改变一切 但 属性的值。在您的情况下,更改阴影 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 的出色工作!
我是 Web 组件的新手,在 html 中编写组件并直接在 html 中添加属性时遇到问题。 问题是组件没有触发 set 属性 函数。
仅当我使用 javascript 设置属性或创建组件并将其添加到 DOM 时,set 属性 函数才有效。
我创建了一支笔来举例说明我的问题:
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>
setAttribute
接受两个参数,而不是你 将 JSON.parse(newVal)
分配给任何 this.setAttribute(attrName)
returns (我假设 undefined
).
this.setAttribute(attrName) = JSON.parse(newVal);
必须是
this.setAttribute(attrName, JSON.parse(newVal));
除此之外,请注意 { title: 'static value' }
无效 JSON。您不能使用单引号来引用键或值。必须是双引号。
接下来,在您的 attributeChangedCallback
中执行 this.setAttribute(attrName, JSON.parse(newVal))
没有意义,原因有二:
- 该属性已经在被设置为该属性的过程中(这就是为什么您的
attributeChangedCallback
正在被执行) - 属性只能包含
String
个值。
相反,你想要做的是
this.article = JSON.parse(newVal);
这将触发您的 getter(这是您想要的,因为它才是真正更新您的组件的)。
我假设您的误解是由于您假设自定义属性自动与同名 属性 同步 - 但并非如此。
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>
@connexo 对属性发生了什么、如何操作它们以及如何提取它们的值给出了很好的解释。
我想通过稍微更改结构来添加他的解决方案。
Getters 和 setters: 使用 getters 和 setters 来操作元素的属性。这样你就可以通过使用news-article.article = { title: 'Breaking news' }
改变属性值和HTML来改变article
属性,并使用news-article.article
得到当前值article
属性。
因为您正在观察 article
属性,所以当您更改 article
属性值时,它会触发 attributeChangedCallback
。你应该把你的逻辑放在那里改变一切 但 属性的值。在您的情况下,更改阴影 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 的出色工作!