EN
sinitsyn-alex
sinitsyn-alex
27 августа 2017

Всем доброго времени суток.
Появилась необходимость доопределить js для блока Attach.
А именно, при выборе картинки рисовать preview аватара.
Код:

/* desktop.blocks/attach/attach.js */
modules.define('attach', function(provide, Attach) {
    provide(Attach.decl({ modName: 'type', modVal: 'avatar' }, {
        onSetMod: {
            'js': {
                'inited': function() {
                    this.__base.apply(this, arguments);
                    console.log('attach_type_avatar inited...');
                }
            }
        },
        _updateFileElem: function() {
            this.__base.apply(this, arguments);
            console.log('_updateFileElem');
            //Do something...
        },
        _clear: function() {
            this.__base.apply(this, arguments);
            console.log('_clear');
            //Do something...
        }
    }));
});

Методы срабатывают,
"Супер-колл" работает,
Функциональность блока не ломается,
только после выбора файла, всплывает ошибка:

Uncaught TypeError: Cannot read property '_emitChange' of undefined
at Object._onChange (profile_settings.min.js:7695)
at n.fn.init.<anonymous> (profile_settings.min.js:1643)
at Function._liveClassTrigger (profile_settings.min.js:1618)
at HTMLBodyElement.f (jquery.min.js:2)
at HTMLBodyElement.dispatch (jquery.min.js:3)
at HTMLBodyElement.r.handle (jquery.min.js:3)

Конкретно в этом месте:

_onChange : function() {
        this.elem('no-file').detach();
        this.getVal()?
            this
                ._updateFileElem()
                ._emitChange() :
            this._clear();
}

Как побороть ошибку? Доопределяю кнопки таким же образом - нет никаких ошибок.

belozer
#belozer
28 августа 2017

для начала не desktop.blocks/attach/attach.js
а desktop.blocks/attach/_type/attach_type_avatar.js

Но проблема в том, что методы теряют свой контекст. Это точно весь код?

sinitsyn-alex
#sinitsyn-alex
28 августа 2017

@belozer. С путем ошибся, но не суть, в сборку попадает. Да это весь код

belozer
#belozer
28 августа 2017

Хмм.. Хорошо бы ещё узнать версию bem-components и bem-core.

В любом случае попробуй установить дебаггер в _onChange и посмотри на что ссылается this и попробуй прямо из дебаггера вызвать методы. Т.к. это странно.

П.Н.
А шаблоны трогал? Если да, хорошо бы шаблон тоже приложить (на всякий случай).

sinitsyn-alex
#sinitsyn-alex
28 августа 2017

@belozer. bem-components - 2.5.0, bem-core - 2.9.0. Шаблоны не трогал.

/* attach.bemhtml */
block('attach')(
    def()(function() { return applyNext({ _attach : this.ctx }); }),

    tag()('span'),

    js()(true),

    content()(
        function() {
            var ctx = this.ctx,
                button = ctx.button;

            this.isSimple(button) && (button = {
                block : 'button',
                tag : 'span',
                text : button
            });

            var attachMods = this.mods,
                buttonMods = button.mods || (button.mods = {});
            ['size', 'theme', 'disabled', 'focused'].forEach(function(mod) {
                buttonMods[mod] || (buttonMods[mod] = attachMods[mod]);
            });

            return [
                button,
                {
                    elem : 'no-file',
                    content : this.ctx.noFileText
                }
            ];
        }
    )
);

/* attach.bh.js */
module.exports = function(bh) {

    bh.match('attach', function(ctx, json) {
        ctx
            .tParam('_attach', json)
            .tag('span')
            .js(true);

        var button = json.button;
        typeof button === 'object' || (button = {
            block : 'button',
            tag : 'span',
            text : button
        });

        var attachMods = ctx.mods(),
            buttonMods = button.mods || (button.mods = {});
        ['size', 'theme', 'disabled', 'focused'].forEach(function(mod) {
            buttonMods[mod] || (buttonMods[mod] = attachMods[mod]);
        });

        ctx.content([
            button,
            {
                elem : 'no-file',
                content : json.noFileText
            }
        ], true);
    });
belozer
#belozer
28 августа 2017

Останови дебаггер в _onChange и посмотри что возвращает this._updateFileElem()

sinitsyn-alex
#sinitsyn-alex
28 августа 2017

@belozer. "Дебажнул".
this._updateFileElem() - возвращает undefined.
Поэтому и undefined._emitChange() - выдает ошибку.
This - в _onChange указывает на блок Attach

sinitsyn-alex
#sinitsyn-alex
30 августа 2017

@belozer. Очень странно... В первом обращении там везде ссылка на attach, потом на undefined. Вы сможете повторить мой код у себя на машине?

sinitsyn-alex
#sinitsyn-alex
30 августа 2017

@belozer. Убираю метод _updateFileElem из уже доопределенного атача - ошибка пропадает.
Остальные методы доопределенные не создают ошибок. Как я понял ошибка в библиотеке. Прошу помочь(

belozer
#belozer
30 августа 2017

@sinitsyn-alex я слепой.... У тебя доопределён метод, но возвращаемое значение изменено.
В attach возвращается this, а у тебя изменено на void (ничего не возвращается).

Правильней будет так

_updateFileElem: function() {
    // Вызов базового метода
    this.__base.apply(this, arguments);
    console.log('_updateFileElem');

    // возвращаем Attach
    return this;
},

Можно ещё так

_updateFileElem: function() {
    // Вызов базового метода и сохранение его значения
    const base = this.__base.apply(this, arguments);
    console.log('_updateFileElem');

    // возвращаем значение базового метода
    return base;
},
sinitsyn-alex
#sinitsyn-alex
31 августа 2017

@belozer. Я очень благодарен Вам за решение моей проблемы, спасибо!

sinitsyn-alex
#sinitsyn-alex
31 августа 2017

@belozer. Я правильно понял, что необходимо в каждом, доопределенном методе возвращать this?
Хотя вот метод _clear я тоже доопределил и не возвратил this, а в оригинальном методе this возвращается, но ошибки ведь нет)

belozer
#belozer
31 августа 2017

@sinitsyn-alex всё зависит от того что хочется решить переопределением метода.
this возвращать не обязательно, если его не возвращает базовый метод.

Хотя вот метод _clear я тоже доопределил и не возвратил this

Возвращать this нужно для работы chain синтаксиса.
https://github.com/bem/bem-components/blob/v2.5.0/common.blocks/attach/attach.js#L64

this
    ._updateFileElem() // возвращаем this
    ._emitChange() : // вызываем метод у возвращённого значения, т.е. this
this._clear(); // здесь вызов делается напрямую

вот что будет при попытке использовать переопределённый _clear

this
    ._clear() // после переопределения _clear стал возвращать undefined, вместо this
    ._emitChange(); // ошибка
sinitsyn-alex
#sinitsyn-alex
2 сентября 2017

@belozer Теперь всё стало на свои места. Спасибо!