TOP

Backboneでオーバーライドしようとして難儀したのでメモ

2016-11-07 17:29:53

BackboneのViewで、既存のViewをextendして同名のメソッドを定義してオーバーライドしようとした時にハマった時のメモ

View

// 継承元の親View(OyaView)
var OyaView = Backbone.View.extend({
    el: "body",

    events: {
        "click *[data-submit='SubmitForm']": "send_submit_data",
    },

    send_submit_data: {
        console.log("send_submit_data(Oya)");
    }
});

// 継承先の子View(KoView)
var KoView = OyaView.extend({
    el: "body",

    events: {
        "click *[data-submit='SubmitForm']": "send_submit_data",
    },

    send_submit_data: {
        console.log("send_submit_data(Ko)");
    }
});

var oyaView = new OyaView();
var koView = new KoView();

実行後

この後clickを発火させると、consoleの出力は以下のようになる

send_submit_data(Oya)
send_submit_data(Ko)

clickのイベントハンドラが2回セットしているので2回発火するのは理解出来るのだが、訳あってvar oyaView = new OyaView()の行は削除出来ない状況だったので、どうしようかって状況になった

色々試した記録(だめだったパターン)

名前空間を変えてしまう方法。new oyaView()を消して全て子Viewに任せるということであれば有効かもしれないが、親Viewを消せない条件なのでだめだった

_.onceを使ってオブジェクト生成部分をを一度しかコールしない方法。↑のコードは簡略化して書いていますが、requirejsを使ってたので、requirejsのoyaViewロード部分を_.onceにしてみたけどこれもだめだった

initializeで重複するイベントハンドラを削除してしまう事に

たぶんかなり無理矢理感があるけど、子Viewのイベントハンドラと親Viewのイベントハンドラを比較して、initialize内で上書きしてしまう事に

// 継承元の親View(OyaView)
var OyaView = Backbone.View.extend({
    el: "body",

    events: {
        "click *[data-submit='SubmitForm']": "send_submit_data",
    },

    send_submit_data: {
        console.log("send_submit_data(Oya)");
    }
});

// 継承先の子View(KoView)
var KoView = OyaView.extend({

    el: "body",

    events: {
        "click *[data-submit='SubmitForm']": "send_submit_data",
    },

    initialize: function () {
        var that = this;
        var events = $._data($("body").get(0)).events;                                  /* 1) bodyのイベントハンドラを取得 */

        _.each(events["click"], function(v){                                            /* 2) bodyにセットされてるclick関連のイベントハンドラを取得 */
            if("undefined" !== typeof that["events"]["click "+v.selector])              /* 3) イベントハンドラが重複する場合 */
            {
                if(v.handler.name === "bound "+that["events"]["click "+v.selector])     /* 4) "bound [メソッド名]" のような形でセットされてるので一致した場合 */
                {
                    $("body").off("click", v.selector);                                 /* 5) イベントハンドラを削除 */
                }
            }
        });
    },

    send_submit_data: {
        console.log("send_submit_data(Ko)");
    }
});

var oyaView = new OyaView();
var koView = new KoView();

実行後

この後clickを発火させると、consoleの出力は以下のようになる

send_submit_data(Ko)

子Viewのイベントハンドラがセットされるのはinitializeの後らしいので、initializeの時点で全く同じイベントハンドラがあった場合に削除してしまうという事で、疑似オーバーライドみたいな感じにした

なんかもっといい方法あるかもしれないけどこういう対処をしたよ、ってメモでした