Polymer 2 TypeError в локальном элементе DOM при срабатывании события - PullRequest
0 голосов
/ 09 мая 2018

У меня есть поле ввода для поиска ввода пользователя и кнопка с переменным состоянием, которая реагирует на ввод пользователя. Обратите внимание на событие 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() вызывается после всего, в том числе после инициализации элемента и его свойств, поэтому самое время запросить местный ДОМ).

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 вернет дескриптор элемента с id="searchInput". Кто-нибудь знает сокращение для нацеливания на обычные элементы без идентификатора без необходимости набирать Polymer.dom(this.root)...?)

У меня есть функция, которая обнаруживает 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);
}

До сих пор это работало так, что, когда пользователь начинает печатать, кнопка становится активной; когда значение отсутствует, кнопка отключается.

Однако, по соглашению, пользователи также могут удалить введенное значение, щелкнув маленькую букву «х», которая появляется справа от входных данных поиска. Разработчики могут обнаружить это событие, прикрепив событие 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 внутри функции возвращает неопределенное значение : Uncaught TypeError: Невозможно прочитать свойство 'setAttribute' из неопределенного .

Предполагая, что это может быть связано с неправильным определением типа, я попытался объявить свойство _doneBtn в получателе свойств Polymer:

static get properties() {
    return {
        _doneBtn: Object // also tried 'String'
    }
}

Я также попытался снова запросить DOM из функции _disabledBtn и попытаться повторно объявить свойство, но я все еще получаю ту же ошибку:

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

Кто-нибудь может понять, что здесь происходит? Похоже, это как-то связано со слушателем событий. Возможно, DOM не полностью обработан до его анализа, хотя переключение порядка объявлений в вызове ready() не имеет значения? Это может также иметь отношение к this.

Интересно, что когда я console.log(this) внутри _disableBtn, консоль возвращает два разных экземпляра this, один для элемента host (<app-search>) и один для целевого элемента, вызвавшего событие: two элементы указывают на «это» . Также примечателен порядок печати this.

Я надеюсь, что кто-то умнее меня сможет помочь решить, что здесь происходит. Спасибо.

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

Прочитав ответ @ softjake, я смог решить проблему.

Сначала давайте вернемся к настройке addEventListener(), которая была добавлена ​​в функцию ready():

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 (например, родительский элемент или документ /window).

Наконец, слегка отрегулируйте функцию _disableBtn:

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

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

Поскольку this уже относится к this._doneBtn, мы можем просто использовать this.setAttribute(...).

0 голосов
/ 10 мая 2018

У меня такое чувство, что вы не правильно используете "это". Выражение «это» нелогично, поскольку мы склонны автоматически связывать его с лексической областью или местом, где используется «это». Увы, это не тот случай, так как привязка «this» определяется не во время JIT «время компиляции» или «время выполнения», а во время «вызова».

Это представляет контекст, из которого это было вызвано. Таким образом, просто перемещая строку кода из одной функции в другую, «this» может ссылаться на что-то другое. И что-то еще может не иметь прикрепленной функции setAttribute, таким образом, сообщение об ошибке «undefined».

Один из способов обойти эту (без каламбура) реальность - настроить вызываемую функцию с помощью .bind ([значение, которое мы хотим, чтобы "this" имело значение при вызове этой функции]).

Например, вместо написания следующего фрагмента:

} else {
    this._disableBtn();
}

вы бы написали

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

Вы бы убедились, что «this», которое вы используете в функции _disableBtn (), указывает на ту же цель, что и «this» в блоке «else».

По сути, значение "this" зависит от исходного контекста сайта вызова, в котором была вызвана функция, в которой он используется (я не уверен, если это понятно ... но перечитайте его медленно, если оно не имеет смысла для первый полет). Чтобы жестко закодировать значение «this» в вызываемой функции, вы можете добавить .bind («this» значение, которое вы хотите передать вызываемой функции) при вызове функции, например:

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

Надеюсь, это поможет.

Jake

...