2012-08-23 26 views
4

我是Backbone和Backbone.Marionette的新手。我成功創建了一個簡單的頁面,其中包含一個允許分頁(第一頁,上一頁,下一頁,最後一頁)的數據網格,快速搜索(每次按下某個鍵時觸發),選擇頁面上顯示的項目數10,所有的,...)如何創建一個可重用的backbone/backbone.marionette組件,例如數據網格?

現在,我有什麼工作,我試圖改善這一點,將這些功能作爲一種可重複使用的組件,但我不知道到底是要走的路。我不知道如何開始完善已經完成的工作。

例如,我希望能夠改變收集/模型由數據網格管理而無需重寫一切。這是我不確定如何做到這一點,這可能是由於缺乏知識。因此,您的意見和建議將得到進一步的肯定和歡迎。

// JST and HAML Assets is used for the templating pre-compilation 
Backbone.Marionette.Renderer.render = function(template, data) { 
    if (!JST[template]) { 
    throw "Template '" + template + "' not found!"; 
    } 
    return JST[template](data); 
}; 

window.MyApp = new Backbone.Marionette.Application(); 

MyApp.addRegions({ 
    content: ".content-box" 
}); 

MyApp.Datagrid = (function() { 
    var Datagrid, ItemPerPageView, Layout, PagerView, QuickSearchView, Theme, ThemeView, Themes, ThemesView; 

    Datagrid = {}; 

    Layout = Backbone.Marionette.Layout.extend({ 
    template: "layouts/grid", 
    regions: { 
     grid: "#grid", 
     quickSearch: "#quickSearch", 
     itemPerPage: "#itemPerPage", 
     pager: ".pager" 
    } 
    }); 

    Theme = Backbone.Model.extend(); 

    Themes = Backbone.ExtendedCollection.paginatedCollection.extend({ 
    url: "/themes", 
    model: Theme, 

    initialize: function() { 
     var _this = this; 

     MyApp.vent.on("quickSearch:term", function(term) { 
     _this.quickSearch(term); 
     }); 

     MyApp.vent.on("itemPerPage:count", function(count) { 
     _this.perPage(count); 
     }); 

     MyApp.vent.on("pager:previous", function() { 
     _this.previous(); 
     }); 

     MyApp.vent.on("pager:next", function() { 
     _this.next(); 
     }); 

     MyApp.vent.on("pager:first", function() { 
     _this.first(); 
     }); 

     MyApp.vent.on("pager:last", function() { 
     _this.last(); 
     }); 
    } 
    }); 

    ThemeView = Backbone.Marionette.ItemView.extend({ 
    tagName: "tr", 
    template: "theme", 
    model: Theme, 

    events: { 
     "click span": "edit", 
     "blur input": "save" 
    }, 

    edit: function(event) { 
     var id, span; 
     id = this.model.get("id"); 
     span = $("span", this.el).hide(); 
     $("input", this.el).show().focus().val(span.text()); 
    }, 

    save: function(event) { 
     var id, input, span; 
     id = this.model.get("id"); 
     span = $("span", this.el).show(); 
     input = $("input", this.el).hide(); 
     if (this.model.get("name") !== input.val()) { 
     this.model.set("name", input.val()); 
     this.model.save(); 
     } 
     span.text(this.model.get("name")); 
    } 
    }); 

    ThemesView = Backbone.Marionette.CompositeView.extend({ 
    template: "index", 
    model: Theme, 
    itemView: ThemeView, 
    collection: Themes, 
    itemViewContainer: "#themes", 

    serializeData: function() { 
     return this.data; 
    } 
    }); 

    QuickSearchView = Backbone.Marionette.View.extend({ 
    el: "#quickSearch", 

    events: { 
     "keyup input": "search" 
    }, 

    search: function(event) { 
     var searchTerm; 
     searchTerm = this.$("input").val().trim(); 
     MyApp.vent.trigger("quickSearch:term", searchTerm); 
    } 
    }); 

    ItemPerPageView = Backbone.Marionette.View.extend({ 
    el: "#itemPerPage", 

    events: { 
     "change select": "count" 
    }, 

    count: function(event) { 
     var count; 
     count = this.$("select").val(); 
     MyApp.vent.trigger("itemPerPage:count", count); 
    } 
    }); 

    PagerView = Backbone.Marionette.View.extend({ 
    el: ".pager", 

    events: { 
     "click #next": "next", 
     "click #previous": "previous", 
     "click #first": "first", 
     "click #last": "last" 
    }, 

    first: function(event) { 
     MyApp.vent.trigger("pager:first"); 
    }, 

    last: function(event) { 
     MyApp.vent.trigger("pager:last"); 
    }, 

    next: function(event) { 
     MyApp.vent.trigger("pager:next"); 
    }, 

    previous: function(event) { 
     MyApp.vent.trigger("pager:previous"); 
    } 
    }); 

    Datagrid.initializeLayout = function() { 
    var collection; 

    Datagrid.layout = new Layout(); 

    Datagrid.layout.on("show", function() { 
     MyApp.vent.trigger("layout:rendered"); 
    }); 

    MyApp.content.show(Datagrid.layout); 

    collection = new Themes(); 
    collection.fetch(); 

    collection.on("reset", function() { 
     return Datagrid.layout.grid.show(new ThemesView({ 
     collection: collection 
     })); 
    }); 
    }; 

    MyApp.vent.on("layout:rendered", function() { 
    var itemPerPageView, pagerView, quickSearchView; 

    quickSearchView = new QuickSearchView(); 
    Datagrid.layout.quickSearch.attachView(quickSearchView); 

    itemPerPageView = new ItemPerPageView(); 
    Datagrid.layout.itemPerPage.attachView(itemPerPageView); 

    pagerView = new PagerView(); 
    Datagrid.layout.pager.attachView(pagerView); 
    }); 

    return Datagrid; 
})(); 

MyApp.addInitializer(function() { 
    MyApp.Datagrid.initializeLayout(); 
}); 

$(document).ready(function() { 
    return MyApp.start(); 
}); 

編輯1:

根據所給出的答案和我自己的想法,我寫了一個解決方案的第一稿。我沒有成功寫出真正的可重用組件,但我有一個解決方案來鞏固我的代碼。有些部分需要重構和改進。還有一些我想在稍後的重構中解決的問題。

要添加一些情況下,應用程序編寫使用Rails作爲後端。所以有我的javascript文件夾結構

assets 
|--javascripts 
    |--application.js 
    |--admin 
     |--admin.js 
     |--admin.layout.js 
     |--subthemes 
     |--admin.subtheme.controller.js 
     |--admin.subtheme.view.js 
     |--themes 
     |--admin.theme.controller.js 
     |--admin.theme.view.js 
|--templates 
    |--admin 
     |--subthemes 
     |--index.hamlc 
     |--subtheme.hamlc 
     |--themes 
     |--index.hamlc 
     |--theme.hamlc 
    |--layouts 
     |--grid.hamlc 

首先,application.js啓動。從Rails的3.2資產管道將準備依賴預期:

//= require underscore 
//= require backbone 
//= require backbone.marionette 
//= require_tree ./lib/backbone 
//= require hamlcoffee 
//= require i18n 
//= require i18n/translations 
//= require_tree ../templates/ 
//= require_tree ./admin 
//= require_tree ./admin/theme 
//= require_tree ./admin/subtheme 

I18n.defaultLocale = "en"; 


Backbone.Marionette.Renderer.render = function(template, data) { 
    if (!JST[template]) { 
    throw "Template '" + template + "' not found!"; 
    } 
    return JST[template](data); 
}; 

$(document).ready(function() { 
    return MyApp.start(); 
}); 

現在,我們可以編寫管理部分開始:

var AdminRouter, TempView; 

// Create the application for admin part 
MyApp.Admin = new Backbone.Marionette.Application(); 

// Define a router to handle the grid collection type change 
AdminRouter = Backbone.Marionette.AppRouter.extend({ 
    initialize: function() { 
    var _this = this; 

    // Route quite generic to easily change the data in the grid 
    this.route(/^admin\/(.*?)$/, "changeCollection"); 

    // Manage event to handle the navigation on client side 
    MyApp.Admin.vent.on("admin:navigate", function(link) { 
     _this.navigate(link, { 
     trigger: true 
     }); 
    }); 
    }, 

    // Trigger an event to change the collection if one exist for the URL 
    changeCollection: function(collectionName) { 
    MyApp.Admin.vent.trigger("grid:collection:change", collectionName); 
    } 
}); 

// Side menu that allows changing the collection in the data grid 
SideMenuView = Backbone.Marionette.View.extend({ 
    el: ".side-menu", 

    events: { 
    "click a": "handleClick" 
    }, 

    // Prevent the normal behavior on the link click 
    handleClick: function(event) { 
    event.preventDefault(); 
    MyApp.Admin.vent.trigger("admin:navigate", $(event.target).attr("href")); 
    } 
}); 

// Add the initializer to the main application to prepare the admin part (grid) 
MyApp.addInitializer(function() { 
    new SideMenuView(); 
    new AdminRouter(); 
    Backbone.history.start({ 
    pushState: true 
    }); 
    MyApp.Admin.start(); 
}); 

然後我們就可以定義數據網格部分:

// This the grid layout module in the admin namespace 
MyApp.Admin.module("GridLayout", function(GridLayout, Admin, Backbone, Marionette, $, _) { 
    var ItemPageSelectorView, Layout, PagerView, QuickSearchView; 

    // The quick search view handle the related fields to do the quick search 
    QuickSearchView = Backbone.Marionette.View.extend({ 
    el: ".gridQuickSearch", 

    events: { 
     "keyup input": "search" 
    }, 

    // Get the field content and trigger an event with it 
    search: function(event) { 
     var searchTerm; 
     searchTerm = $(event.target).val().trim(); 
     $("input", this.$el).val(searchTerm); 
     Admin.vent.trigger("grid:quickSearch:term", searchTerm); 
    } 
    }); 

    // The item page selecto handle the choice of how many rows should be displayed per page 
    ItemPageSelectorView = Backbone.Marionette.View.extend({ 
    el: ".gridItemPageSelector", 

    events: { 
     "change select": "count" 
    }, 

    // Get the number of items per page that should be displayed 
    count: function(event) { 
     var count; 
     count = $(event.target).val(); 
     $("select", this.$el).val(count); 
     Admin.vent.trigger("grid:itemPageSelector:count", count); 
    } 
    }); 

    // The pager view manage the view components to change the page shown in the data grid 
    PagerView = Backbone.Marionette.View.extend({ 
    el: ".gridPager", 

    events: { 
     "click #next": "next", 
     "click #previous": "previous", 
     "click #first": "first", 
     "click #last": "last", 
     "click #page": "page" 
    }, 

    // 
    // The following functions triggers events to go to the right pages 
    // 
    first: function(event) { 
     Admin.vent.trigger("grid:pager:first"); 
    }, 

    previous: function(event) { 
     Admin.vent.trigger("grid:pager:previous"); 
    }, 

    page: function(event) { 
     Admin.vent.trigger("grid:pager:page"); 
    }, 

    next: function(event) { 
     Admin.vent.trigger("grid:pager:next"); 
    }, 

    last: function(event) { 
     Admin.vent.trigger("grid:pager:last"); 
    } 
    }); 

    // The grid layout with the regions to display the different part of the data grid 
    Layout = Backbone.Marionette.Layout.extend({ 
    template: "layouts/grid", 

    regions: { 
     gridTable: "#gridTable", 
     gridQuickSearch: ".gridQuickSearch", 
     gridItemPageSelector: ".gridItemPageSelector", 
     gridPager: ".gridPager" 
    } 
    }); 

    // Once the layout is rendered, the different views are attached to the right regions 
    Admin.vent.on("grid:layout:rendered", function() { 
    var itemPageSelectorView, pagerView, quickSearchView; 

    quickSearchView = new QuickSearchView(); 
    Admin.gridLayout.gridQuickSearch.attachView(quickSearchView); 

    itemPageSelectorView = new ItemPageSelectorView(); 
    Admin.gridLayout.gridItemPageSelector.attachView(itemPageSelectorView); 

    pagerView = new PagerView(); 
    Admin.gridLayout.gridPager.attachView(pagerView); 
    }); 

    // Initializer to do at the application start 
    GridLayout.addInitializer(function() { 
    Admin.addRegions({ 
     content: ".content-box" 
    }); 

    Admin.gridLayout = new Layout(); 

    // Trigger the rendered event when the grid layout is shown 
    Admin.gridLayout.on("show", function() { 
     Admin.vent.trigger("grid:layout:rendered"); 
    }); 

    // Manage the collection data change 
    Admin.vent.on("grid:collection:change", function(collectionName) { 
     // Close the previous view in the grid table region 
     Admin.gridLayout.gridTable.close(); 

     // Trigger an event to fetch the collection 
     Admin.vent.trigger("" + collectionName + ":collection:fetch"); 

     // Show the grid layout if not already done 
     if (!this.shown) { 
     this.shown = true; 
     Admin.content.show(Admin.gridLayout); 
     } 
    }); 
    }); 

    return GridLayout; 
}); 

我們完成了結構代碼。現在我們可以去一個控制器。例如,ThemeController:

MyApp.Admin.module("ThemeController", function(ThemeController, Admin, Backbone, Marionette, $, _) { 
    // Define the model to use in the collection 
    ThemeController.Theme = Backbone.Model.extend(); 

    // Define the collection with the related url on the server. The collection extends a paginated collection that has the methods to manage the quick search and the pagination 
    ThemeController.Themes = Backbone.ExtendedCollection.paginatedCollection.extend({ 
    url: "/admin/themes", 

    model: ThemeController.Theme, 

    initialize: function() { 
     var _this = this; 

     // 
     // The following functions handle the events for the quick search and pagination 
     // 

     Admin.vent.on("grid:quickSearch:term", function(term) { 
     _this.quickSearch(term); 
     }); 

     Admin.vent.on("grid:itemPageSelector:count", function(count) { 
     _this.perPage(count); 
     }); 

     Admin.vent.on("grid:pager:previous", function() { 
     _this.previous(); 
     }); 

     Admin.vent.on("grid:pager:next", function() { 
     _this.next(); 
     }); 

     Admin.vent.on("grid:pager:first", function() { 
     _this.first(); 
     }); 

     return MyApp.Admin.vent.on("grid:collection:fetched", function() { 
     Admin.gridLayout.gridTable.show(new Admin.ThemeView.Table({ 
      collection: _this 
     })); 
     }); 
    } 
    }); 

    // At the application initilization, we need to be sure this controller can 
    // handle the event to fetch the data from the server 
    Admin.addInitializer(function() { 
    Admin.vent.on("themes:collection:fetch", function() { 
     ThemeController.themes = new ThemeController.Themes(); 

     // Once the data are fetched from the server, trigger an event to display them 
     ThemeController.themes.fetch({ 
     success: function() { 
      Admin.vent.trigger("grid:collection:fetched"); 
     } 
     }); 
    }); 
    }); 
}); 

終於爲前一個控制器的看法:

MyApp.Admin.module("ThemeView", function(ThemeView, Admin, Backbone, Marionette, $, _) { 
    // The view to show one item in a row of the data grid 
    ThemeView.Item = Backbone.Marionette.ItemView.extend({ 
    tagName: "tr", 
    template: "admin/themes/theme", 
    model: Admin.ThemeController.Theme 
    }); 

    // The view to show the collection of item 
    ThemeView.Table = Backbone.Marionette.CompositeView.extend({ 
    template: "admin/themes/index", 
    model: Admin.ThemeController.Theme, 
    itemView: ThemeView.Item, 
    collection: Admin.ThemeController.Themes, 
    itemViewContainer: "#themes", 

    // ! I was force to add this to have data in the original format that is used by my templates ! 
    serializeData: function() { 
     return this.data; 
    } 
    }); 
}); 

備註:次主題控制器和視圖文件包含完全相同的一種代碼。只有模板和種類不同。

在HAML網格佈局通過滑軌資產管道編譯樣子:

.gridPager 
    %button#first= "<<" 
    %button#previous= "<" 
    %button#next= ">" 
    %button#last= ">>" 

%span.gridItemPageSelector= "Item per page" 
    %select 
    %option= 5 
    %option= 10 
    %option{"value" => -1}= "All" 

%span.gridQuickSearch= "Quick search:" 
    %input#gridSearchTerm{"type" => "text"} 

#gridTable 

%span.gridItemPageSelector= "Item per page" 
    %select 
    %option= 5 
    %option= 10 
    %option{"value" => -1}= "All" 

%span.gridQuickSearch= "Quick search:" 
    %input#gridSearchTerm{"type" => "text"} 

.gridPager 
    %button#first= "<<" 
    %button#previous= "<" 
    %button#next= ">" 
    %button#last= ">>" 

正如你可以看到,有相當多的重複。我想要在網格的頂部和底部進行快速搜索和分頁。目前,最簡單的方法就是複製代碼。稍後當我找到如何做到這一點時,我會改變它。

爲表模板,顯示了主題:

%table.table.table-striped 
    %thead 
    %tr 
     %th= "Id" 
     %th= "Name" 
    %tbody#themes 

很簡單,沒有什麼特別的話。在這個時候,頭文件被硬編碼了!

最後,項目視圖模板來顯示一個主題:

%td= this.id 
%td= this.name 

這個模板是真的很簡單。

我處於這種情況下工作得很好。例如,當我點擊其他鏈接來更改顯示的集合時,快速搜索字段和類似的東西不會重新初始化。爲此,我想添加一種狀態管理來跟蹤收集狀態,以及何時回到已經顯示的集合中,我想要像以前一樣顯示它。

我相信我的解決方案並不完美,可以重構很多。我也可能犯了很多「新手」的錯誤。所以隨時挑戰我的主張。我試圖學習和改進我的解決方案,並希望它能幫助別人做這樣的事情。

回答

2

嗯,我不是一個大專家,但是這就是我做的,用木偶和Requirejs:

一)我創建了一個通用的網格佈局wiew由我approuter與喜歡收集一些參數調用,COLS配置(我呈現了每個週期表頭)和行觀點:

showUsers: function(){ 

    require(['views/GridGen','collections/user_collection'], function(Grid, UserCollection){ 

     var Users = new UserCollection(); 

     App.grid = new Grid({collection: Users , 
          rowView: 'rowUser', 
          cols_config: App.tables.users}); 

     App.page.show(App.grid); 

    }); 
}, 

二)在我的網格佈局我呈現的各個部分等待昂秀活動:

var Grid = Backbone.Marionette.Layout.extend({ 

regions: { 
      top_controls: "#top_controls", 
      table_view: "#table_view", 
      pagination_controls: "#pagination_controls", 
      bottom_controls: "#bottom_controls", 
     }, 

onShow: function(){    
    this.renderTable(this.collection); 
}, 

renderTable: function(collection){ 

    collection.fetch({success:function(){ 

      require(['views/'+self.options.rowView+'.js'],function(iView){ 

        var vista = new View({collection: collection, itemView: iView, thead: self.options.cols_config}); 

        App.grid.table_view.show(vista); 

        self.renderPagination(collection); 

        collection.pager(); 
       }); 
      }}); 
} 

三)我一般表視圖採取的cols和ItemView控件呈現爲這樣的參數:

var View = Backbone.Marionette.CompositeView.extend({ 

initialize: function(){ 
     this.itemView = this.options.itemView;   
}, 
serializeData: function(){ 
     var data = Backbone.Marionette.ItemView.prototype.serializeData.apply(this, arguments); 
     data.thead = this.options.thead; 
     return data; 
}, 
appendHtml: function(collectionView, itemView, index){ 
     collectionView.$("tbody").append(itemView.el); 
}, 

這只是一個一般的想法,我不認爲這是做的最好的方式,但我沒有找到一個更好的解決方案,但希望給你至少一些提示:)

+0

感謝您的意見。我會嘗試類似的東西,看看它是否符合我的需求。 – Laurent

1

我強烈推薦backgrid component這是可擴展和可重用的開箱即用。

只在版本0.2.6 - 但良好的以下和其漂亮的光滑

相關問題