Help us understand the problem. What is going on with this article?

Backbone.jsをTypeScriptから使用する

More than 3 years have passed since last update.

自分のウェブサイトで公開しているcirclecheckというウェブアプリにBackbone.jsを仕込んでみました。

Backbone.jsは機能毎に機能が分割された設計になっており、それぞれの説明を読んでいけば、迷う事は殆どありません。
ただ、DefinitelyTypedが公開されているわりには、TypeScriptからどの様に記述したら良いのかが分かりづらい箇所がありましたので、覚え書きを残す事にしました。

以下の様な interface を使用する Model, Collection, View を想定しています。

interface

interface ISAMPLE {
    idx: number;
    str_value: string;
    num_value: number;
}

Backbone.Model

class model_Sample extends Backbone.Model {

    constructor(attributes?: any, options?: any) {
        super(attributes, options);
    }
}

Backbone.Collection

class collection_Sample extends Backbone.Collection<model_Sample> {

    constructor(models?: model_Sample[] | Object[], options?: any) {
        // どちらの記述でも可。
        this.url = "/sample_url";
        this.url = function() { return ("/sample_url"); };
        super(models, options);

        this.on("add", this.evt_append);
        this.on("remove", this.evt_remove);
    }

    // Modelを一意に識別する方法を設定しておくと、add時に重複を弾く事が出来る。
    modelId(attributes: ISAMPLE) {
        return attributes.idx;
    }

    // 比較条件を設定しておけばコレクション内のモデルは常にこの並びとなる。
    comparator(compare: model_Sample, to?: model_Sample): number {
        if (compare.attributes.num_value > to.attributes.num_value) return 1;
        if (compare.attributes.num_value < to.attributes.num_value) return -1;
        return 0;
    }

    evt_append(m: model_Sample) {
        console.log("item append");
    }

    evt_remove(m: model_Sample) {
        console.log("item remove");
    }
}

Backbone.View

class view_Sample extends Backbone.View<model_Sample> {

    constructor(options?: Backbone.ViewOptions<model_Sample>) {
        // superの呼び出し前に設定しておく必要がある。
        this.el = "body";
        super(options);

        this.listenTo(this.model, "change", this.evt_model_change)
    }

    events(): Backbone.EventsHash {
        return {
            "click button#id_btn": this.evt_btn_click
        }
    }

    evt_model_change(m: model_Sample) {
        console.log("evt_model_change");
    }

    evt_btn_click() {
        console.log("evt_btn_click");
        this.render();
    }

    render() {
        return this;
    }
}

テストコード

index.html
<!DOCTYPE html>
<html lang="ja">

    <head>
        <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta name="author" content="MizunagiKB" />
    </head>

    <body>
        <div>
            <button id="id_btn">TEST</button>
        </div>
        <script type="text/javascript">
            window.onload = function()
            {
                main();
            }
        </script>

        <!-- jQuery http://jquery.com/ -->
        <script type="text/javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.0/jquery.js"></script>

        <!-- Bootstrap http://twitter.github.io/bootstrap/-->
        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/js/bootstrap.min.js"></script>

        <!-- Underscore.js http://jashkenas.github.io/underscore/ -->
        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

        <!-- Backbone.js http://backbonejs.org/ -->
        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.2.3/backbone-min.js"></script>

        <script type="text/javascript" src="sample.js"></script>

    </body>
</html>

sample.ts
interface ISAMPLE {
    idx: number;
    str_value: string;
    num_value: number;
}

class model_Sample extends Backbone.Model {

    constructor(attributes?: any, options?: any) {
        super(attributes, options);
    }
}

class collection_Sample extends Backbone.Collection<model_Sample> {

    constructor(models?: model_Sample[] | Object[], options?: any) {
        // どちらの記述でも可。
        this.url = "/sample_url";
        this.url = function() { return ("/sample_url"); };
        super(models, options);

        this.on("add", this.evt_append);
        this.on("remove", this.evt_remove);
    }

    // Modelを一意に識別する方法を設定しておくと、add時に重複を弾く事が出来る。
    modelId(attributes: ISAMPLE) {
        return attributes.idx;
    }

    // 比較条件を設定しておけばコレクション内のモデルは常にこの並びとなる。
    comparator(compare: model_Sample, to?: model_Sample): number {
        if (compare.attributes.num_value > to.attributes.num_value) return 1;
        if (compare.attributes.num_value < to.attributes.num_value) return -1;
        return 0;
    }

    evt_append(m: model_Sample) {
        console.log("item append");
    }

    evt_remove(m: model_Sample) {
        console.log("item remove");
    }
}

class view_Sample extends Backbone.View<model_Sample> {

    constructor(options?: Backbone.ViewOptions<model_Sample>) {
        // superの呼び出し前に設定しておく必要がある。
        this.el = "body";
        super(options);

        this.listenTo(this.model, "change", this.evt_model_change)
    }

    events(): Backbone.EventsHash {
        return {
            "click button#id_btn": this.evt_btn_click
        }
    }

    evt_model_change(m: model_Sample) {
        console.log("evt_model_change");
    }

    evt_btn_click() {
        console.log("evt_btn_click");
        this.render();
    }

    render() {
        return this;
    }
}

function main() {
    let value_1: ISAMPLE = { idx: 1, str_value: "A", num_value: 123 };
    let value_2: ISAMPLE = { idx: 2, str_value: "B", num_value: 456 };
    let value_3: ISAMPLE = { idx: 3, str_value: "C", num_value: 789 };

    let value_A: ISAMPLE = { idx: 1, str_value: "A", num_value: 123 };

    let m = new model_Sample();
    let c = new collection_Sample();
    let v = new view_Sample(
        {
            model: m
        }
    );

    // 同じモデルを与えてもchangeイベントは発生しない。(変更とみなされない)
    m.set(value_1);
    m.set(value_1);
    // attributesの変更をチェックしているので、この場合もchangeイベントは発生しない。
    m.set(value_A);
    // changeイベントが発生する。
    m.set(value_2);
    m.set(value_3);

    c.add(value_1);
    // 同じidxを持っている場合はaddイベントは発生しない。(追加されない)
    c.add(value_1);
    c.remove(value_1);
    // 存在しないものを削除しようとしてもremoveイベントは発生しない。
    c.remove(value_1);

    c.add(value_3);
    c.add(value_1);
    c.add(value_2);

    // comparatorが設定されているため、
    // collection内は(設定を解除しない限り)ソートされる。
    for (let i: number = 0; i < c.length; i++) {
        console.log(c.at(i))
    }

    // 配列を渡す場合は、JSON.stringifyで事前に文字列化しておくこと。
    c.fetch(
        {
            data: {
                param_1: "A",
                param_2: 1,
                param_3: JSON.stringify(["B", 2])
            }
        }
    );
}

おまけ

DefinitelyTypedのbackbone-global.d.tsについて

定義内で以下の様に書かれていますが、

    remove(model: TModel, options?: Silenceable): TModel;
    remove(models: TModel[], options?: Silenceable): TModel[];

addの記述と対応している方が妥当だと思われます。

    remove(model: {}|TModel, options?: Silenceable): TModel;
    remove(models: ({}|TModel)[], options?: Silenceable): TModel[];

こう記述されるはず…

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした