2

我有一个相当复杂的 Backbone 应用程序,我不确定如何组织视图/模板。该应用程序是一个基于 Web 的电子邮件客户端。我真的很难理解如何制作这个侧边栏。

应用程序侧边栏与您在 Apple Mail/Outlook 中看到的非常相似。它基本上是一个文件夹浏览器。此视图存在于每个页面上。

我有两个主要问题:

如何将集合数据放入侧边栏视图?

从技术上讲,侧边栏上呈现了三个“集合” - 帐户 => 邮箱和标签。所以一些示例代码看起来像这样:

<div class="sidebar">
  <div class="sidebar-title">Mailboxes</div>

  <div class="sidebar-subtitle">Gmail</div>
  <div class="mailboxes" data-account-id="1">
    <ul>
      <li><a href="...">Inbox</a></li>
      <li><a href="...">Sent</a></li>
      ...
    </ul>
  </div>

  <div class="sidebar-subtitle">Yahoo</div>
  <div class="mailboxes" data-account-id="2">
    <ul>
      <li><a href="...">Inbox</a></li>
      <li><a href="...">Sent</a></li>
      ...
    </ul>
  </div>

  <div class="sidebar-title">Labels</div>
  <div class="sidebar-labels">
    <ul>
      <li>Home</li>
      <li>Todo</li>
    </ul>
  </div>
</div>

所以理论上我需要做这样的事情,不是吗?

<div class="sidebar">
  <div class="sidebar-title">Mailboxes</div>

  <% for account in @accounts.models: %>
    <div class="sidebar-subtitle"><%= account.get('name') %></div>
    <div class="mailboxes" data-account-id="<%= account.get('id') %>">
      <ul>
        <% for mailbox in account.mailboxes.models: %>
          <li><a href="..."><%= mailbox.get('name') %></a></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class="sidebar-title">Labels</div>
  <div class="sidebar-labels">
    <ul>
      <% for label in @labels: %>
        <li><%= label.get('name') %></li>
      <% end %>
    </ul>
  </div>
</div>

问题是我不能同时通过@accounts路由器@labels。我意识到我可以使用@options,但这似乎很混乱且不可靠。此侧边栏需要存在于每个页面上。在 Backbone 堆栈上应该是什么样子?我认为它不应该有自己的路由器,因为它不是真正的“页面”。

我会把事情分解成更小的视图吗?

每个邮箱都应该有自己的视图吗?每个邮箱都应该有自己的视图吗?每个标签都有自己的看法?我想监听事件,例如新消息,并更新邮箱未读计数(例如)。但是我该怎么做呢?如何嵌套视图并有效地处理所有对象的创建/获取?如果我要将事物分解为更小的视图,我的侧边栏视图应该是什么样的?

对不起,如果我在胡说八道。我已经看了好几天了,似乎找不到一个好的解决方案。

4

1 回答 1

6

使用options是完全合法的并且是 Backbone-y。所以我不会害怕使用它,尽管还有很多其他方法可以解决这个问题。是的。我认为侧边栏应该在路由器调用的更大页面视图中初始化。

我也觉得你的邮箱应该是自己的子视图。听起来无论邮件是 yahoo 还是 google,每个邮箱都将具有相同的功能(例如最小化),因此创建该视图类并多次重用它是很有意义的。

那么这个邮箱视图应该采取什么措施呢?(关于收集)

同样,你有选择。放入 1 个包含所有内容的大型集合,或放入代表来自某种帐户的邮件的每个集合...

如果您的集合已经是独立的(例如 1 个用于 Google 邮件,1 个用于 Yahoo 邮件),那么创建子视图并将适当的集合传递给每个子视图将很容易。即使它不是单独的,我也可能会将集合过滤为仅需要的模型,然后再将其作为集合传递。这就是我倾向于的。我假设在每个邮箱视图中,永远不会出现邮箱需要访问与特定帐户无关的任何邮件模型的情况。所以传递任何你不需要的东西是没有意义的。

至于标签,我不确定。我想这取决于你的标签是什么以及如何形成的。例如,您是否有自己的标签模型,它基本上是与邮件 model.id 关联的标签标签?还是标签是您正在采摘的邮件模型的属性?

更新示例代码 - 专注于通过视图层次结构传递数据

所以我在这里基本上概述了一种非常常见的在父视图中制作子视图的方法,以及如何通过视图层次结构传递不同的数据(阅读:在你的案例中为集合)。

[options]您可以通过简单地重复该模式,利用View 构造函数的参数并通过和传递内容来为更多的子视图执行此操作(例如,每个邮件模型都有自己的代表性子视图作为邮箱视图中的列表项等)在里面接受它。

在这段代码的许多部分,我采取了一种冗长的方法,以便更清楚地了解哪些数据在哪里传递。我稍微更改了您的邮箱模板,只是为了说明如何添加更多子视图,但您当然可以将其更改回来。您对 MailboxView 做什么取决于您,您的代码(取决于您的目标)应该反映这一点。

因此,事不宜迟,这里有一些示例代码供您仔细研究和思考。我把它搅在一起,所以可能会有错误。我实际上并没有看到它是否会执行。

通常在视图中,我们可能会将 view.el 定义为模板中最外层的元素,并且只包含内部部分,但为了与您提供的模板保持一致并减少代码中可能令人困惑的额外内容的数量,我大部分情况下模板都保持原样。

// Your templates
<script id="sidebar" type="text/template">

    <div class="sidebar">
    <div class="sidebar-title">Mailboxes</div>
        // Mailbox here
        // Mailbox here
        // Labels  here
    </div>

</script>

// I altered this template slightly just to illustrate a point on appending the model
// view data into your mailbox view. You might not want this but it's just for
// demonstration purposes
<script id="mailbox" type="text/template">
    <div class="sidebar-subtitle">Gmail</div>
    <div class="mailboxes" data-account-id="1">
        <ul class="inbox"></ul>  // Changed this
        <ul class="sent"></ul>
    </div>
</script>

<script id="labelBox" type="text/template">
    <div class="sidebar-title">Labels</div>
        <div class="sidebar-labels">
            <ul>
                <li>Home</li>
                <li>Todo</li>
            </ul>
        </div>
    </div>
</script>

您的应用程序大视图,包含所有内容的主视图。

AppView = Backbone.View.extend({
    initialize: function() {
        // You might not instantiate your collections here but it is a good starting
        // point for this demo. We'll be passing these through children view and 
        // utilizing them in the appropriate views.

        // Let's assume these collections we instantiate already have models inside them.

        // Your 3 Collections
        this.gmail = new MailCollection();
        this.yahoo = new MailCollection();
        this.labels = new LabelCollection();

    },
    render: function() {
        this.$el.html();

        // We pass each collection into the SidebarView [options] hash. We can "pick-up"
        // these collections in the sidebar view via this.options.xxx

        // Render the sidebar
        var sidebar = new SidebarView({
            'gmail':this.gmail,
            'yahoo':this.yahoo,
            'labels':this.labels
        });

        // Render the sidebar and place it in the AppView... anywhere really
        // but for simplicity I just append it to the AppView $el
        this.$el.append(sidebar.render().el);

        return this;
    }
});

您的侧边栏视图:

SidebarView = Backbone.View.extend({
    template: _.template($('#sidebar').html()),
    initialize: function() {

        // We passed the 3 collections into Sidebar view and can access
        // them through the options. We could have instantiated them here
        // but I passed them into the side to illustrate that using
        // options for this kind of thing is a-okay.

        this.gmail = this.options.gmail,
        this.yahoo = this.options.yahoo,
        this.labels = this.options.labels

        // This is an array of sub-view mailboxes so when we close this view,
        // it's easy to loop through the subviews and close both mailboxes
        this.mailboxes = [];
    },
    render: function() {
        // We render the sidebarView using the template
        this.$el.html(this.template());

        // We generate the sub-view mailbox views (gmail and yahoo)
        var gmailView = new MailboxView({
            'collection': this.gmail // We pass in only the gmail collection
        });
        var yahooView = new MailboxView({
            'collection': this.yahoo // Pass in the yahoo mail collection
        });
        var labelView = new LabelboxView({
            'collection': this.labels // Pass in the labels collection
        });

        // We push the views into our array
        this.mailboxes.push(gmailView);
        this.mailboxes.push(yahooView);
        this.mailboxes.push(labelView);

        // Render each view and attach it to this sidebar view
        this.$el.append(gmailView.render().el);
        this.$el.append(yahooView.render().el);
        this.$el.append(labelView.render().el);

        return this;
    },
    onClose: function() {
        // Sample code of how we close out child views. When this parent view closes,
        // it automatically cleans up the child views.

        _.each(this.mailboxes, function(view) {
            view.close(); // A special function that I use to close views
        });
    }
});

有关我使用的方法和方法的更多详细信息,请参阅Zombie View Cleanup 。一旦您开始在应用程序中创建大量视图/子视图关系,这将特别有用。onClose()close()

您的邮箱视图:

MailboxView = Backbone.View.extend({
    template: _.template($('#mailbox').html()),
    initialize: function() {
        // When we pass something in as 'collection' backbone automatically
        // attaches it as a property of the view so you can use the collection
        // as this.collection rather than this.options.collection for
        // convenience
    },
    render: function() {
        this.$el.html(this.template());

        this.loadMail();

        return this;
    },
    loadMail: function() {
        // Some code that loops through your collection and adds the mail to an
        // appropriate DOM element. Would make sense to make Inbox a ul and each
        // mail an li so I altered your template to demonstrate this

        // Let's assume a mail model has the attr 'author' and 'body' in this
        // simple example

        // This will loop through your collection and use a mail template to
        // populate your list with the appropriate data.
        this.collection.each(function(mail) {
            this.$('ul').append(_.template('<li><%=author%><%=body%></li>', mail.toJSON()))
        });

        // Alternatively, you could (and probably should) make each mail model
        // represented by ANOTHER sub-view, a mail subview that has all the
        // functionality that mail views usually have.
        // To accomplish this you just repeat the prior demonstrated cycle
        // of creating child view inside child view, passing in the appropriate
        // data that you need, creating the view, and attaching it to the parent
        // where you would like.
    }
});

您的标签视图:

LabelboxView = Backbone.View.extend({
    template: _.template($('#labelBox').html()),
    initialize: function() {
        // I think you get the gist from above, you'd do the same thing as each
        // mailbox view except deal with the unique aspects of labels, whatever
        // these may be (and do)
    },
    render: function() {
        this.$el.html(this.template());
        return this;
    }
});  
于 2012-09-10T00:56:44.167 回答