【问题标题】:Using connectedCallback() breaks the web component使用 connectedCallback() 会破坏 Web 组件
【发布时间】:2019-07-04 13:40:49
【问题描述】:

我正在学习 Web 组件,并正在构建一个动态列表来掌握它们。让它工作后,我读到最好使用connectedCallback 方法附加影子根。但是,尝试这样做后,我得到了一堆我无法修复的错误。 另外,我为标签设置简单属性的方式似乎有点啰嗦:有没有更简单的方法来获取属性并将其显示为标签?

这是我的工作示例:

const template = document.createElement('template');
template.innerHTML = `
<style>
    :host {
    display: block;
    font-family: sans-serif;
    text-align: center;
    }

    button {
    border: none;
    cursor: pointer;
    }

    ul {
    list-style: none;
    padding: 0;
    }
</style>
<h1>To dos</h1>

<lable id="lable1"></lable>
<select></select>
`;

class TodoApp extends HTMLElement {
    constructor() {
        super();

        this._shadowRoot = this.attachShadow({ 'mode': 'open' });
        this._shadowRoot.appendChild(template.content.cloneNode(true));
        this.$todoList = this._shadowRoot.querySelector('select'); 
        this.label1 = this._shadowRoot.getElementById('lable1')

    }

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

    attributeChangedCallback(name, oldValue, newValue) {
      this.label1.innerText = this.getAttribute('att1');
    }

    renderTodoList() {
        this.$todoList.innerHTML = '';

        this.todosArray.forEach((todoP) => {
            let $todoItem = document.createElement('option');
            $todoItem.text = todoP.text; 
            $todoItem.value = todoP.id; 
            this.$todoList.appendChild($todoItem);
        });
    }

    set todos(value) {
        this.todosArray = value;
        this.renderTodoList();
    }

}

window.customElements.define('to-do-app', TodoApp);

当我添加一个 connectedCallback() 方法并在那里创建影子 dom 时,我得到了一堆错误。 我的标记是:

<to-do-app att1="value 1 attribute"></to-do-app>

我试过了:

class TodoApp extends HTMLElement {
    constructor() {
        super();
    this.label1 = '';
    }

    connectedCallback() {
        this._shadowRoot = this.attachShadow({ 'mode': 'open' });
        this._shadowRoot.appendChild(template.content.cloneNode(true));
        this.$todoList = this._shadowRoot.querySelector('select'); 
        this.label1 = this._shadowRoot.getElementById('lable1')

    }

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

    attributeChangedCallback(name, oldValue, newValue) {
      this.label1.innerText = this.getAttribute('att1');
    }

但得到错误:

TypeError: can't assign to property "innerText" on "": not an object

【问题讨论】:

    标签: javascript web-component shadow-dom custom-element native-web-component


    【解决方案1】:

    我是 not sure at all it's best to define the Shadow DOM in connectedCallback()(除非你想使用 Shadow DOM polyfill。你从哪里读到的?

    无论如何,如果您的示例使用connectedCallback(),则错误是由于在connectedCallback() 之前调用了attributeChangedCallback()

    这就是为什么在调用attributeChangeCallback() 时尚未设置属性this.label1

    改为测试属性是否存在:

    attributeChangedCallback(name, oldValue, newValue) {
        if ( this.label1 )
            this.label1.innerText = this.getAttribute('att1');
    }
    

    并且,在渲染组件时,测试属性是否存在:

    connectedCallback() {
        //...
        this.label1 = this._shadowRoot.getElementById('lable1')
        if ( this.getAttribute( 'att1' ) )
            this.label1.innerText = this.getAttribute( 'att1' ) 
    }
    

    更新

    读取属性的最佳方式取决于您何时需要它。在您的用例中,因为当您在connectedCallback() 中需要它时,它已经在标记中,您可以使用this.getAttribute() 获取它。

    你分配它的值,也许你应该使用带有变量的template literal而不是&lt;template&gt;元素。

    let label = this.getAttribute( 'att1' )
    this.shadowRoot.innerHTML = `<label>${label}</label>`
    

    【讨论】:

    • 我在这里阅读了建议:dev.to/bennypowers/…
    • 对我的第二个问题有任何想法:读取标记中的属性并将其分配给文本标签的最简单方法是什么。它不是动态的,只需要在页面渲染时设置一次。我让它工作的方式似乎有点 OTT,因为我认为这是一个简单的操作。感谢以上帮助!
    猜你喜欢
    • 2023-03-05
    • 2023-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-18
    • 2020-04-29
    • 2020-05-12
    • 1970-01-01
    相关资源
    最近更新 更多