所以这是我的第一个Backbone项目,我想知道我是否以最好的方式做事。我的应用程序基本上有两种状态,一种显示搜索框,另一种显示一个搜索框,下面有一个表格。我的路由器有搜索路径和只有搜索视图的初始登录页面。当用户键入查询时,路由器将导航到搜索路径,并将表视图添加到页面中。这是我的路由器:
app.Router = Backbone.Router.extend({
routes: {
'': 'index',
'search/coords=:address&age=:age&rad=:rad': 'search'
},
search: function(address, age, rad){
app.statusView || (app.statusView = new app.StatusView());
app.searchView || (app.searchView = new app.SearchView());
app.trigger('status:loading');
app.Practices.fetch({
reset: false,
success: function() {
app.searchView.setElement($('#search-box')).render();
var searchQuery = new app.SearchQueryModel({age: age, coords: address.split(","), radius: rad});
if (!app.tableView){
app.tableView = new app.TableView({model: searchQuery});
} else {
app.tableView.model = searchQuery;
app.tableView.refresh();
};
}
});
app.trigger('status:clear');
},
index: function() {
app.statusView = new app.StatusView();
app.searchView = new app.SearchView();
app.footerView = new app.FooterView();
app.searchView.setElement($('#search-box')).render();
}
});
正如您所看到的,我的视图在索引路由中被实例化,然后在您搜索时使用相同的视图,除非用户直接进入搜索页面,在这种情况下视图将在那里实例化。如果这不是非常不理想的话,我会感到惊讶,因为检查视图是否已存在于搜索路径中似乎很笨拙。有没有更好的做事方式?
答案 0 :(得分:0)
首先欢迎Backbone。这是一个可爱的框架,可以让你做出像你喜欢的美丽或丑陋的东西。根据良好实践,您的问题是关于视图实例化的位置。当然,通过处理url路由和视图实例来违反 Demeter法似乎有点不对。
但是观点必须从某个地方开始?如果不是路由器那么?
所以我有两个回复:
如果您的应用很简单而且您只想玩骨干,那么您可能会很好。很多人让单页应用程序框架使其他简单的应用程序复杂化。我不是想偷懒,但现在你拥有它是Backbone中自然的初学者选择。如果是这种情况,请停在这里。
如果您想使用骨干的全部功能来定制框架,请继续阅读。
所以我的设置旨在使用一些样板函数启动一个新项目,并只创建一些特定于新应用程序的类。路径处理和所有这类事情对我来说似乎很低级,它应该只是我不想经常看的某些配置的一部分。结果是我的路由器看起来像这样:
define([
'autorouter'
], function(AutoRouter){
var AppRouter = AutoRouter.extend({
autoRoutes: {
":page" : "routeDirect",
":page/:object" : "routeDirect",
":page/:object/:action" : "routeDirect",
"": "routeDirect"
}
});
return AppRouter;
});
然后,对于每个新项目,我都有一个文件,我保留非默认路由,例如:
define(function(require){
return {
"schedule" : require('screens/schedule')
, "logout" : require('screens/logout')
, "login" : require('screens/login')
, "create" : require('screens/create')
, "upload" : require('screens/upload')
, "select" : require('screens/selection')
, "inventory" : require('screens/inventory')
, "describe" : require('screens/description')
}
});
我将每个屏幕放入其自己的文件中(使用requirejs进行多文件依赖关系管理)。额外的变量会传递给screen
。
每个screen
都是特定用户体验的大脑,负责加载视图,并在屏幕处于活动状态时处理某些事件。
如果这看起来像一个有趣的设置,那么我就是这样做的:
对于路由器本身,我使用了一个样板类,我从Derick Bailey借来了一些稍作修改:
define([
'jquery', 'underscore', 'backbone'],
function($, _, Backbone) {
var AutoRouter = Backbone.Router.extend({
constructor: function(options){
Backbone.Router.prototype.constructor.call(this, options);
var that = this;
that.app = options.app;
if (this.autoRoutes){
that.processAutoRoutes(options.app, that.autoRoutes);
}
},
processAutoRoutes: function(app, autoRoutes){
var method, methodName;
var route, routesLength;
var routes = [];
var router = this;
for(route in autoRoutes){
routes.unshift([route, autoRoutes[route]]);
}
routesLength = routes.length;
for (var i = 0; i < routesLength; i++){
route = routes[i][0];
methodName = routes[i][1];
method = app[methodName];
router.route(route, methodName, method);
}
}
});
return AutoRouter;
});
我永远不必看它,但我确实需要将它传递给app实例。例如:
this.appRouter = new AppRouter({app : this});
最后我的路线指示功能:
define(function(require){
var pathParser = function(path){
return Array.prototype.slice.call(path);
}
var pathApply = function(path, routes, context){
var pathArray = pathParser(path);
var primary = pathArray[0];
if (routes.hasOwnProperty(primary)){
routes[primary].apply(context, pathArray.slice(1));
} else {
routes["default"].apply(context, pathArray.slice(1));
}
}
return function(path){
//NOTE PLEASE that this references AutoRouter
//Which has an app property
var oApp = this.app;
var pathRoutes = _.extend(require('urls'), {
"default" : require('screens/default')
});
pathApply(arguments, pathRoutes, oApp);
};
});
那么,我做得更好吗?好吧,如果你只用一两个屏幕做一些非常简单的事情,那么你当然不希望从头开始构建这种设置。但是如果你像我一样,并且你希望能够快速生成新项目,那么拥有一些类似上面两个类的样板允许一个JSON对象告诉应用程序我应该将哪些路由发送到哪些屏幕。然后我可以在适当的位置拥有所有逻辑,允许分离关注点。这就是我认为Backbone非常愉快的原因。
答案 1 :(得分:0)
我对您的问题的理解是,每次进行搜索时都会触发一条路线。
如果您正在这样做,那么使用视图事件哈希(用于捕获和处理视图中发生的事件)进行搜索。不要使用路由。在视图中定义事件哈希并使用回调来处理搜索。
var myAppEventBus = _.extend({},Backbone.Events);
var myAppController = {
function : search(options) {
// create an instance of the collection and do a fetch call passing the
// search parameters to it.
var searchResultsCollection = new SearchResultsCollection();
// pass search criteria, the success and error callbacks to the fetch
// method.
var that = this;
searchResultsCollection.fetch(
{
data:that.options,
success : function() {
// Pass the fetched collection object in the trigger call so that
// it can be
// received at the event handler call back
var options = {
"searchResultsCollection" : that.searchResultsCollection;
};
myAppEventBus.trigger("search_event_triggered",options);
},
error : function() {
// do the error handling here.
}
}
);
}
};
// Application Router.
var MyAppRouter = Backbone.Router.extend({
routes : {
'search/coords=:address&age=:age&rad=:rad': 'search'
},
search : function(searchParams) {
// Fetch the query parameters and pass it to the view.
var routeSearchExists = false;
var searchOptions = {};
var options = {};
if(searchParams) {
routeSearchExists = true;
// If search params exist split and set them accordingly in
// the searchOptions object.
options.searchOptions = searchOptions;
}
// Create and render the search view. Pass the searchOptions
var searchView = new SearchView(options);
searchView.render();
// Create and render an instance of the search results view.
var searchResultsView = new SearchResultsView();
searchResultsView.render();
// If there are search parameters from the route, then do a search.
if(routeSearchExists) {
searchView.search();
}
}
});
// The main view that contains the search component and a container(eg: div)
// for the search results.
var SearchView = Backbone.View.extend({
el : "#root_container",
searchOptions : null,
initialize : function(options) {
// Intialize data required for rendering the view here.
// When the user searches for data thru routes, it comes down in the
// options hash which can then be passed on to the controller.
if(options.searchOptions) {
this.searchOptions = options.searchOptions;
}
},
events : {
"search #search_lnk":"initSearch"
},
initSearch : function(event) {
event.preventDefault();
var searchOptions = {};
// Fetch the search fields from the form and build the search options.
myAppController.search(searchOptions);
},
search : function() {
if(this.searchOptions) {
myAppController.search(searchOptions);
}
}
});
// The view to display the search results.
var SearchResultsView = Backbone.View.extend({
searchResultsCollection : null;
initialize : function(options) {
// Handling the triggered search event.
myAppEventBus.on("search_event_triggered",this.render,this);
},
render : function(options) {
//search results collection is passed as a property in options object.
if(options.searchResultsCollection)
//Render your view.
else
// Do it the default way of rendering.
}
});
答案 2 :(得分:0)
让我们说它不坏,但还有一种更好的方法。
至于现在,你的路由器负责与app astatus的连接URL以及视图和模型控制。第二个可能与路由器分离,因此您需要控制器抽象,但Backbone不会从框中提供“控制器”。
但这不是问题,您可以使用plugin或查看Marionette.js
中的Controller实现这里的主要想法是正确分配应用程序部分之间的职责:
1)路由器 - 保留路由并使用控制器操作挂接URL
2)控制器 - 管理视图和模型(创建,删除,获取等)
3)查看 - 收听模型和DOM事件并呈现数据
4)模型 - 提供实际数据并处理数据。