事件触发时本地 DOM 元素上的 Polymer 2 TypeError

Polymer 2 TypeError on Local DOM element when Event triggered

我有一个接收用户输入的搜索输入字段和一个响应用户输入的可变状态按钮。请注意搜索输入中的 on-input 事件。

<dom-module id="app-search">
    <template>
        
        <input type="search" id="searchInput" on-input="_onInput" />

        <!-- NOTE: button is set to 'disabled' when there is no value of search field -->
        <paper-button disabled>Done</paper-button>

    </template>
</dom-module>

在 Polymer ready() 定义中,我得到了 paper-button 元素的句柄(ready() 函数在一切之后被调用,包括在元素及其属性被初始化之后,所以现在是查询本地 DOM).

的好时机
ready() {
    super.ready(); // must call this for Polymer to work

    // get handle to paper-button element
    this._doneBtn = Polymer.dom(this.root).querySelector('paper-button');
}

(顺便说一句,我知道使用 this.$ 可以用作 Polymer.dom(this.root).querySelector() 的语法快捷方式,但该语法似乎只有效对于本地 DOM 中具有 id 的目标元素,例如:this.$.searchInput 将 return 指向带有 id="searchInput" 的元素的句柄。有谁知道 shorthand 无需键入 Polymer.dom(this.root)... 即可定位非 ID 常规元素?)

我有一个功能可以检测搜索字段中的 input 事件。如果搜索字段中有值,请启用该按钮。

_onInput() {
    // if search input has value
    if (Boolean(this.$.searchInput.value)) {
        // remove disabled attr from button
        this._doneBtn.removeAttribute('disabled');
    } else {
        this._disableBtn();
    }
}

_disableBtn() {
    this._doneBtn.setAttribute('disabled', true);
}

到现在为止,当用户开始输入时,该按钮会启用;当没有值时,按钮将被禁用。

但是,按照惯例,用户也可以通过单击搜索输入右侧显示的小 'x' 来删除输入值。开发人员可以通过将 search 事件附加到输入元素来检测该事件:

ready() {
    super.ready(); // must call this for Polymer to work

    // get handle to paper-button element
    this._doneBtn = Polymer.dom(this.root).querySelector('paper-button');

    // attach 'search' event listener to search input field
    // call the element's '_disableBtn' function
    this.$.searchInput.addEventListener('search', this._disableBtn);
}

问题是当我点击搜索字段有值时出现的'x'触发事件,this._disableBtn函数触发,但是 this._doneBtn 在函数 return 中未定义 : Uncaught TypeError: Cannot read property 'setAttribute' of undefined.

假设这可能与不正确的类型定义有关,我尝试在 Polymer 属性 getter:

中声明 _doneBtn 属性
static get properties() {
    return {
        _doneBtn: Object // also tried 'String'
    }
}

我也尝试再次从 _disabledBtn 函数内部查询 DOM 并尝试重新声明 属性 但我仍然得到相同的错误:

_disableBtn() {
    if (!this._doneBtn) {
        this._doneBtn = Polymer.dom(this.root).querySelector('paper-button');
    }
    this._doneBtn.setAttribute('disabled', true);
}

谁能理解这里发生了什么?这似乎与事件监听器有关。也许 DOM 在解析之前没有完全呈现,尽管在 ready() 调用中切换声明的顺序没有什么区别?它也可能与 this.

有关

有趣的是,当我在 _disableBtnconsole.log(this) 时,控制台 return 有两个不同的 this 实例,一个用于宿主元素 (<app-search>)一个用于触发事件的目标元素:two elements point to 'this'。另外值得注意的是 this 的打印顺序。

我希望比我聪明的人可以帮助解决这里发生的问题。

我的直觉是你没有正确使用 "this"。 "this" 表达式是违反直觉的,因为我们倾向于自动将它与词法范围或使用 "this" 的位置相关联。 las,情况并非如此,因为 "this" 绑定不是在 JIT "compile time" 或 "runtime" 中确定的,而是在 "call time" 中确定的。

this 表示调用它的上下文。因此,只需将一行代码从一个函数移动到另一个函数,"this" 就可以引用其他内容。并且其他东西可能没有附加 "setAttribute" 函数,因此出现 "of undefined" 错误消息。

解决这个(没有双关语意)现实的一种方法是使用 .bind([我们希望 "this" 在调用该函数时具有的值])配置被调用函数。

例如,不要编写以下代码段:

} else {
    this._disableBtn();
}

你会写

} else {
    this._disableBtn().bind(this);
}

你会确保你在 _disableBtn() 函数中使用的 "this" 指向与 "else" 块中的 "this" 相同的目标。

基本上,"this" 值取决于调用它所使用的函数的原始调用站点上下文(我不确定这是否清楚......但如果不清楚,请慢慢重读'在第一次飞行时没有意义)。要在被调用函数中硬编码一个"this"的值,可以在调用函数时加上.bind("this"你想传递给被调用函数的值)如:

function aFunction() {
   myfunction(parameter).bind("required value of 'this' in myFunction").
}

希望对您有所帮助。

杰克

看完@softjake的回复,问题解决了。

首先,让我们回顾一下在 ready() 函数中添加的 addEventListener() 设置:

ready() {
    super.ready();

    // handle to toggle button element
    this._doneBtn = Polymer.dom(this.root).querySelector('paper-button');

    // previous method === no worky
    //this.$.searchInput.addEventListener('search', this._disableBtn);

    // changed it to:
    this.$.searchInput.addEventListener('search', this._disableBtn.bind(this._doneBtn));
}

这里的关键是我使用 .bind(this._doneBtn) 来确保 this,在 _disableBtn 函数的范围内指的是 this._doneBtn 而不是其他一些this(如父元素或 document/window)。

最后,稍微调整_disableBtn函数:

_disableBtn() {
    // previous (also no worky)
    //this._doneBtn.setAttribute('disabled', true);

    // changed to:
    this.setAttribute('disabled', true);
}

因为this已经引用了this._doneBtn,我们可以简单地使用this.setAttribute(...).