【问题标题】:How to bind a Backbone.View to a 'single' DOM element in a list of similar elements in the DOM如何将 Backbone.View 绑定到 DOM 中类似元素列表中的“单个”DOM 元素
【发布时间】:2011-07-19 17:10:32
【问题描述】:

我的页面结构如下:

<ul class="listOfPosts">
   <li class="post WCPost" data-postId="1">
      <div class="checkbox"><input type="checkbox" class="wcCheckbox"/></div>
      <div class="PostContainer>
       <!-- some other layout/content here -->
       <ul class="listOfLabels">
          <li class="label"> Label 1</li>
          <li class="label"> Label 2</li>
       </ul>
      </div>
   </li>
   <li class="post WCPost" data-postId="2">...</li>       
   <li class="post WCPost" data-postId="3">...</li>
   <li class="post WCPost" data-postId="4">...</li>
   ...
</ul>

这是过于简单的视图:

  var MyView = Backbone.View.extend({
    el:$('.listOfPosts'),

    initialize: function() {
         _.bindAll(this,"postClicked");
    },

    events: {
        "click .wcCheckbox":"postClicked"
    },

    postClicked: function() {
         alert("Got a a click in the backbone!");
        }
  });

问题:我想知道被点击的帖子的数据ID。使用简单的 JQuery,我可以做到以下几点:

$('.wcCheckbox').live('click' function() {
    alert("Data id of post = "+$(this).parents('.WCPost').data('data-postId'));
}

现在我知道 Backbone 会进行事件委托,因此它需要 el 属性中的 DOM 引用。如果我给它 .listOfPosts ,那么事件似乎会完美触发,但要检查“哪些”帖子,我必须继续遍历 DOM 并挑选出被选中的元素!!!在我看来,这将是相当昂贵的!我如何在 Backbone 中为每个单独的帖子执行此操作?即,如何将视图绑定到每个单独的 li.WCPost?

注意:我没有使用普通的 jQuery,因为我需要编写的代码最好使用 Backbone 的模块化设计完成,但由于我不是 Backbone 的专业人士(但;)有点困惑......

【问题讨论】:

    标签: javascript jquery backbone.js


    【解决方案1】:

    据我所知,在 postClicked() 函数中使用 $(event.target).data('postId') 之类的东西是执行此类操作的正常方法。

    事件的广泛使用起初可能看起来不寻常,但如果使用得当,这是改进代码组织的好方法。在大多数情况下,您确实可以从事件中获取您想要的所有数据,尤其是如果您有 jQuery。 (请注意,传递给postClicked 函数的事件是一个常规的jQuery 事件对象,可以应用everything you can find about it。Backbone.js uses jQuery 的delegate() 函数绑定事件。)

    * * *

    但是,您仍然可以在视图的initialize() 方法中自行绑定事件。

    initialize: function() {
         // Custom binding code:
         this.$('.wcCheckbox').live('click' function() {
             alert("Data id of post = "+$(this).parents('.WCPost').data('data-postId'));
         }
         // No need for this anymore:
         // _.bindAll(this,"postClicked");
    },
    

    this.$(&lt;selector&gt;) 是一个自动将 jQuery 选择器范围限定为视图的函数,相当于 $(&lt;selector&gt;, this.el)。)

    * * *

    另一种解决方案(最终可能是最自然的,但首先需要做一些工作)是创建一个 PostView 类,并将其用于单个帖子和绑定复选框单击事件。

    要显示帖子,请在帖子列表视图中浏览Posts 集合,为每个Post 实例创建一个视图,并将其附加到.listOfPosts 元素。

    您不再需要解决访问帖子 ID 的问题,因为您将在 PostView 上绑定复选框单击事件。因此,在处理函数中可以很容易地找到帖子的 ID — 通过相关模型 (this.model.get('id')) 或通过视图的元素 ($(this.el).data('postId'))。

    * * * 更新

    现在我独立于 Backbone 生成了我的帖子的 DOM,如何像我在问题中提到的那样“改造”每个帖子的视图?

    我不想仅仅为了适应 Backbone 就重构大量客户端代码。但是如何将视图绑定到每个 li??

    如果您决定使用 MVC 和面向对象的 JavaScript,则不应直接为您的帖子管理 DOM 元素:您创建 PostView 实例,然后它们反过来创建相应的元素,就像在 todos.js 中一样,并用渲染的模板填充它们。 (如果您不想自动创建元素,Backbone 允许您手动将新创建的视图绑定到元素,方法是在子类化时指定 el 属性。这样,您可以将视图绑定到现有元素,例如,在初始页面加载。)任何与特定帖子相关的 DOM 修改都应在 PostView 类中进行。

    OOP 方法相对于基于 DOM 的一些优势:

    • 视图比 DOM 元素更适合存储数据。

      请注意,在您当前的方法中,您广泛使用了data 属性,例如,存储帖子的 ID。但是,如果您正在操作视图,则不需要这样做:每个 PostView 已经知道其相关模型,因此您可以通过例如 view.model.get('postId') 以及任何其他帖子轻松获取帖子 ID你想要的数据。

      您还可以存储不属于 Post 模型的视图特定数据:例如,动画和/或显示状态(展开、选中等)。

    • 视图更适合定义元素的行为。

      例如,如果您想为每个帖子指定一个复选框单击事件处理程序,您可以将此代码放入您的 PostView 类中。这样,事件处理程序知道所有发布数据,因为视图可以访问它。此外,代码结构更好——处理特定帖子,属于PostView,不会妨碍您。

      另一个例子是在视图上定义render() 函数的约定。模板函数或模板字符串存储在视图属性template 中,render() 呈现该模板并用生成的 HTML 填充视图元素:

      render: function() {
          $(this.el).html(this.template({
              modelData: this.model.toJSON()
          }));
      },
      
    • 操作 DOM 可能比操作对象慢。

    然而,基于 DOM 的方法(您现在似乎一直在使用)有其优点,尤其是对于不太复杂的应用程序。另见:Object Oriented Javascript vs. Pure jQuery and .data storage

    有没有办法将视图绑定到不是由主干生成的当前和未来的 DOM 元素?

    这是否意味着骨干需要从一开始就使用并且很难“改造”?

    如上所示,您不需要将视图绑定到 future DOM 元素(使用视图来操作它们)。

    关于在页面初始化时绑定到页面上已经存在的 DOM 元素,我不知道这样做的“正确”方法。我目前的做法是清除现有元素并从头开始创建视图,这将自动创建相应的 DOM 元素。

    这意味着浏览器在页面初始化时需要做一些额外的工作。对我来说,OOP 的优势证明了这一点。从用户的角度来看,我想这也很好:在初始初始化之后,一切都会更快,因为在用户执行诸如注销之类的操作之前不需要重新加载页面。

    (顺便说一句,我想我应该澄清一下使用 MVC 方法的要点,至少对我来说:创建一个客户端应用程序,本质上,它自己完成大部分工作,使用后端提供的 API服务器。服务器以 JSON 格式为模型提供数据,其余的由您的客户端应用程序完成,广泛使用 AJAX。如果您在用户与您的站点交互时执行正常的页面加载,那么这种 MVC 和繁重的客户端可能会为您带来开销. 另一件事是您将不得不重构一些代码,这可能不是一个选项,具体取决于您的要求和资源。)

    【讨论】:

    • 谢谢!这说明了很多事情。仍然令人困惑的是这个。现在我独立于 Backbone 生成了我的帖子的 DOM,我如何像我在问题中提到的那样“改造”每个帖子的视图?我想我会阅读您发布的链接以及 Brendan 发布的链接,以加快活动部分的速度。我的帖子不是很简单,我使用客户端微模板(非 Underscore.js)来生成它们。我不想仅仅为了适应 Backbone 就重构大量的客户端代码。但是如何将视图绑定到每个 li??
    • (续)您还提到这是最自然的事情,但我仍然坚持“如何”?有没有办法将视图绑定到不是由主干生成的当前和未来的 DOM 元素?这是否意味着骨干需要从一开始就使用并且很难“改造”?我可以让非主干视图收听模型/集合吗?所以如果使用主干确实很难做到这一点,我可以使用 jQuery 创建自定义“视图”!只是想让你知道 - 我真诚地感谢你的帮助,并享受帮助我更好地学习骨干的见解:)
    • 好吧,这似乎很有意义。我们决定使用 freemarker 在服务器上生成大量数据。我们没有选择纯粹的 JSON 数据交换,而是选择了基于表单的表示来提交数据。我现在了解了 OO-JS 与 Jquery 在数据属性方面的设计差异。很有意义 :) 好吧,发现改变设计有点晚了,但这就是我所说的经验 :) - 设计可能是由于缺乏它的方式 :) 我会用我所拥有的以及在给定限制的情况下我可以适应的情况
    • 我猜是这种情况(即您给出的解释),因为直觉上我就是这样考虑设计应用程序的 - 每个帖子的视图。事后我无法做到这一点的事实让我措手不及,让我意识到除了 jquery/data-* 属性之外,可能还有一种不同的做事方式。我从来没有从 OO 的角度看 JS,但是既然你已经把我指向了骨干,我就忍不住想了想 :) 我真诚地感谢所有的帮助/指针。我可以对系统的其他部分使用纯主干/Json 方法并学习:)
    • 只是出于好奇:你能设置视图的 data-* 属性吗?我无法用 view.make 做到这一点(我在视图中尝试了 this.make)如果使用主干,我猜你总是会“通过它”并且将数据存储在类属性中更有意义。但是如何使用主干将 data-* 属性分配给视图?这是禁忌吗??老实说,不想添加另一个问题:)
    【解决方案2】:

    请注意,事件传递对自身及其起源点的引用,在我看来,这是访问事件起源的最简单方法。

    像这样尝试,看看这是否是您所需要的(并且不那么复杂):

    var MyView = Backbone.View.extend({
    
        el:$('.listOfPosts'),
    
        initialize: function() {
             _.bindAll(this,"postClicked");
        },
    
        events: {
            "click .wcCheckbox":"postClicked"
        },
    
        postClicked: function(e) {
            alert("Here is my event origin: "+e.target);
        }
    
    });
    

    可以从事件中获得大量数据,如下所示:http://www.quirksmode.org/js/events_properties.html

    一旦您对 javascript 事件有所了解,您可能会研究 PubSub/Observer 模式以获得更多解耦的 UI 组件,这里有一个很好的介绍:

    http://blog.bobcravens.com/2011/01/loosely-coupled-javascript-using-pubsub/

    【讨论】:

    • 那里很好。我可以通过事件的目标,但我不必处理浏览器不兼容性只是为了获取 traget/srcElement 的“data-*”属性吗?没有其他方法可以将事件绑定到页面上的单个 <li>s</li> 吗?这不是使用主干的严重缺点吗???
    • @Nupul,您仍然可以使用 jQuery 的 data() 方法为您处理所有浏览器问题:alert("Got a click from: " + $(e.target).parent('.WCPost').data('postId'))。 (您需要向您的 postClicked 函数添加一个 e 参数才能像这样访问事件对象。)
    • 数据有多复杂?如果它只是一个“id”——你可以在元素的“id”属性上设置它,比如&lt;li id="{generated id}"&gt; blah blah &lt;/li&gt;——同样,这将使用 HTML 的核心部分为你完成工作,而不是复杂的方法 - 这有帮助吗?
    • @Brendan:实际上我有很多 data-* 标签。它们不仅是 id,还有很多其他的东西。我们选择了这种设计方法并将坚持一段时间:)
    【解决方案3】:

    没有什么能阻止您嵌套视图。即,外视图代表UL。 UL 视图将呈现一堆内部 LI 视图。您可以尽可能深入地嵌套视图。

    我一直以这种方式做事非常成功。

    【讨论】:

      猜你喜欢
      • 2018-08-30
      • 2015-01-24
      • 1970-01-01
      • 2013-01-23
      • 1970-01-01
      • 2012-12-12
      • 1970-01-01
      • 2019-06-16
      • 1970-01-01
      相关资源
      最近更新 更多