2013/04/25

初次玩 Backbone.js: 多種 Template 的實作方式

假設我想設計一個公司資料,model 為單一員工資料,包含 id 與 name。collection 為部門資料,由多個員工所組成。而 view 用來顯示部門資料之用。
// Models
company.models.Employee = Backbone.Model.extend({
    defaults: {
        id: undefined,
        name: undefined
    }
});

// Collections
company.collections.Department = Backbone.Collection.extend({
    model: company.models.Employee
});

// Views
company.views.EmployeeList = Backbone.View.extend({
    el: "#container",
 
    render: function() {
        ...
    }
});

// Create base data
var c = new company.collections.Department(
        [{id:1, name: "Star Willard"},
         {id:2, name: "Rhona Eggleston"},
         {id:3, name: "Cassi Chowdhury"},
         {id:4, name: "Leigh Nilson"},
         {id:5, name: "Niesha Auger"}
        ]);

// Create view
var v = new company.views.EmployeeList({collection: c});
v.render();


以下就來探討 render function 該怎麼把 collection 套用到 template 並呈現在 UI 上


方法一:土法煉鋼,串成 string


這種方法當然是很差啦,程式可讀性下降,也很難維護

Render function in app.js
render: function() {
    var that = this;
    this.collection.each(function(m) {
        var html = "<div>" + 
                   "" + m.get("id") + " " + 
                   "" + m.get("name") + "" + 
                   "</div>";
        that.$(".list").append(html);
    });
    return this;
}


方法二:寫在 stript 內


利用 underscore.js template engine
比前一種好一點點,但還是有缺點,沒有把 template 切出去,一方面 IDE 的 syntax 會失效

in index.html
<script id="template-item" type="text/template">
    <div>
        <b><%= id %></b>
        <u><%= name %></u>
    </div>
</script>
Render function in app.js
template: _.template($("#template-item").html()),
  
render: function() {
    var that = this;
    this.collection.each(function(m) {
     that.$(".list").append(that.template(m.toJSON()));
    });
    return this;
}


方法三:從獨立的 html 檔案中讀取


把 template 寫在個別的 html 檔,利用 ajax 的方式去 load。
程式碼較好管理,但每讀取一次 template 就要多發一個 http request

tpl/template-item.html
<%= id %> <%= name %>

Add the template manager to app.js
其實只是把 get request 包起來啦
company.utils.TemplateManager = {
    load: function(name, callback) {
        $.get('tpl/' + name + '.html', function(data) {
            callback(data);
        });
    }
};

Render function in app.js
render: function() {
    var that = this;
    company.utils.TemplateManager.load('template-item', function(data) {
        var tpl = _.template(data);
        that.collection.each(function(m) {
            that.$(".list").append(tpl(m.toJSON()));
        });
    });
    return this;
}


方法四:Asynchronously load template


與其說是非同步,正確來說應該是預先載入(preload),之後使用時就可以直接從 local variable 取出,加快反應速度,也可以省去從 server 抓取相同 template 的功耗

TemplateLoader in app.js
company.utils.TemplateManager = {
    templates: {},
    
    load: function(names) {
        var that = this;
        $.each(names, function(index, name) {
            $.get('tpl/' + name + '.html', function(data) {
                that.templates[name] = data;
            });
        });
    },
    
    get: function(name, callback) {
        return this.templates[name];
    }
};

Render function in app.js
render3: function() {
    var that = this;
    var tpl = _.template(company.utils.TemplateManager.get('template-item'));
    this.collection.each(function(m) {
        that.$(".list").append(tpl(m.toJSON()));
    });
}

in app.js
Preload the template before render the view.
company.utils.TemplateManager.load(['template-item', 'other-template']);
var v = new company.views.EmployeeList({collection: c});
v.render();

如果要保證全部的 template 都載入才進行之後的事情,可以使用 jquery.when()
load: function(names, callback) {
    var deferreds = [];
    var that = this;
    $.each(names, function(index, name) {
        deferreds.push($.get('tpl/' + name + '.html', function(data) {
            that.templates[name] = data;
        }));
    });
    $.when.apply(null, deferreds).done(callback);
}


參考資料


沒有留言:

張貼留言