将事件绑定到视图 - 射击次数不均匀

时间:2016-02-18 15:14:16

标签: javascript backbone.js javascript-events

我有UserModelUserViewUserCollectionUserCollectionView。有了这个,我试图将click事件绑定到UserView(我在UserView中这样做)。所以,这是我的代码:

var root = 'http://localhost:5000/api/v1';
var app = {};

// Backbone Model
app.UserModel = Backbone.Model.extend({
    defaults: {
        // urlRoot: root + '/users',
        name: 'Default Name',
        email: '30',
        username: 'default_username'
    },

    initialize: function() {
        this.set({
            id: this.get('username')
        });
        console.log('User model \'' + this.id + '\' has been initialized.');
    },

    // parse: function(data) {
    //     console.log('Model parse funciton called');
    //     return data;
    // }
});

// Backbone Model View
app.UserView = Backbone.View.extend({
    // el: '#users-list',
    // tagName: 'div',
    el: '.user-box-wrapper',

    events: {
        'click .user-data': 'userClicked'
    },

    userClicked: function(ev) {
        console.log("User selected");
        // console.log(ev.currentTarget);
    },

    template: _.template($('#connections-user-template').html()),

    initialize: function() {
        this.render();
    },

    render: function() {
        $('#users-list').append(this.template(this.model.toJSON()));
        // this.$el.append( this.template( this.model.toJSON()));
        console.log('User view is rendered');
    }
});

// Backbone Collection
app.UserCollection = Backbone.Collection.extend({
    model: app.UserModel,
    url: root + '/users',

    initialize: function() {
        // this.fetch();
    },

    parse: function(data) {
        // console.log(data.data);
        return data.data;
    }
});

// Backbone Collection View
app.UserCollectionView = Backbone.View.extend({
    el: '#users-list',

    template: _.template($('#connections-template').html()),

    initialize: function() {
        this.connections = new app.UserCollection();
        var self = this;
        this.connections.fetch().done(function() {
            self.render();
        });
    },

    render: function() {
        console.log('User collection view is rendered');
        this.$el.html(this.template());
        // this.$el.append( this.template( this.model.toJSON()));
        this.connections.each(function(user) {
            console.log('User : ' + user.get('id'));
            var userView = new app.UserView({
                model: user
            });
            // userView.model.fetch();
            // userView.render();
        });
    }
});
var connectionsView = new app.UserCollectionView();

JSON数据实际上返回14个对象(在本例中为UserModels)。问题是,如果我点击第一个用户视图,它会被触发13次,第二个视图点击会被触发12次,依此类推,点击时根本没有触发最后一个视图点击事件。

各个UserViews每次都会呈现一次(但至少我认为是这样)。有人可以解释这里的问题是什么以及这里到底发生了什么?

P.S。 - 我知道在CollectionView中绑定事件的解决方法。

修改1

这是DOM结构:

<!doctype html>
<html>
  <head>
    <title>Hey there</title>
    <link rel="stylesheet" href="../static/css/normalize.css">
    <link rel="stylesheet" href="../static/css/index.css">
    <script src="/static/js/jquery-2.2.0.js"></script>
    <script src="/static/js/underscore-1.8.3.js"></script>
    <script src="/static/js/backbone-1.2.3.js"></script>
  </head>

  <body>
    <header id="top">
      <div id="logo-wrapper">
        <img src="../static/img/logo.png" alt="Logo" id="logo">
      </div>
      <div id="top-links">
        <div id="top-profile-box" class="toplink">
          <div id="top-profile-data-box">
            <div id="top-profile-data-name">Kevin Isaac</div>
            <div id="top-profile-data-passion">Writer</div>
          </div>
          <img id="top-profile-image" src="../static/img/user1.jpg" alt="">
        </div>
        <div id="notification-icon" class="toplink"></div>
        <div id="top-message-icon" class="toplink"></div>
        <div id="logout-icon" class="toplink"></div>
      </div>
    </header>
    <div id="middle">
      <nav id="side-nav">
        <div id="side-nav-top">
          <div class="side-nav-link" id="side-nav-home-link">
            <div class="side-nav-link-img"></div>
            <div class="side-nav-link-title">Home</div>
          </div>
          <div class="side-nav-link" id="side-nav-profile-link">
            <div class="side-nav-link-img"></div>
            <div class="side-nav-link-title">Profile</div>
          </div>
          <div class="side-nav-link" id="side-nav-messages-link">
            <div class="side-nav-link-img"></div>
            <div class="side-nav-link-title">Message</div>
          </div>
          <div class="side-nav-link" id="side-nav-account-link">
            <div class="side-nav-link-img"></div>
            <div class="side-nav-link-title">Account</div>
          </div>
        </div>
      </nav>
      <div id="main-content">
        <!-- Start of page specific HTML -->

        <div id="content-title">
          <div class="content-subtitle" id="connections">Connections</div>
          <div class="content-subtitle" id="followers">Followers</div>
          <div class="content-subtitle" id="followings">Followings</div>
        </div>

        <div id="content-body">
          <div id="users-box">
            <div id="users-list">No connection</div>
              <!-- Backbone Template Starts --> 
              <script type="text/template" id="connections-template"></script>
              <script type="text/template" id="connections-user-template">
                <div class="user-box-wrapper">
                  <div class="user-box">
                    <div class="user-pic-wrapper">
                      <img src="/static/img/user1.jpg" alt="">
                    </div>
                    <div class="user-data" id="boox">
                      <div class="user-name"><%= name %></div>
                      <div class="user-passion"><%= username %></div>
                      <div class="user-city"><%= email %></div>
                    </div>
                  </div>
                </div>
              </script>
              <!-- Backbone Template Ends --> 


            <div id="users-side-box">
              <div id="users-box-search">
                <input id="user-search" type="text" name="">
              </div>
              <div id="user-metadata">
                <div id="metadata-user-top-box">
                  <div id="metadata-user-image-wrapper">
                    <img src="/static/img/user1.jpg" alt="">
                  </div>
                  <div id="metadata-user-name-box">
                    <div id="metadata-name">Name's Bond</div>
                    <div id="metadata-passion">Assassin</div>
                  </div>
                </div>
                <div id="metadata-user-bottom-box">
                  <div class="metadata-user-attribute">
                    <span class="metadata-property">Studied at: </span>
                    <span class="metadata-value">Karunya University </span>
                  </div>
                  <div class="metadata-user-attribute">
                    <span class="metadata-property">Native City: </span>
                    <span class="metadata-value">London</span>
                  </div>
                  <div class="metadata-user-attribute">
                    <span class="metadata-property">Website: </span>
                    <span class="metadata-value">www.007.com</span>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>

        <!-- End of page specific HTML -->
      </div>
      <aside id="main-aside">
        Aside one two therr
      </aside>
    </div>

    <script src="../static/js/index.min.js"></script>
  </body>
</html>

2 个答案:

答案 0 :(得分:2)

您的代码存在许多问题。

主要问题是,所有userView都指向同一个选择器,与此选择器.user-box-wrapper匹配的元素位于视图模板内 - 因此,无论何时创建新的userView,它都会添加一个新事件监听器.user-box-wrapper存在于所有现有的userViews中,但不归于自身。因此,您的第一个userView将向其注册n-1个事件处理程序,而最后一个没有(n是userViews的总数)。视图元素不应该是模板的一部分,模板应该被添加到视图元素中。

如果您的视图将拥有自己的多个副本,请不要定义el选项,让骨干为视图的每个实例创建一个新元素。您可以自定义此元素的属性。

另一个问题是你没有将userView模板附加到你的userView元素,而是在它之外的其他东西($('#users-list')。所以点击模板不会触发事件处理程序(在你的情况下这就是你的原因) '最后一个userView没有触发它的事件。它被绑定到所有其他现有视图,因为公共选择器是由el提供的)

您应该尝试在视图中避免使用$('#users-list')等全局选择器。在这种情况下,您可以在userCollectionView中将userView附加到#users-list,其中elc指向#users-list

您的代码应为:

var root = 'http://localhost:5000/api/v1';
var app = {};

// Backbone Model
app.UserModel = Backbone.Model.extend({
  defaults: {
    // urlRoot: root + '/users',
    name: 'Default Name',
    email: '30',
    username: 'default_username'
  },

  initialize: function() {
    this.set({
      id: this.get('username')
    });
    console.log('User model \'' + this.id + '\' has been initialized.');
  }
});

// Backbone Model View
app.UserView = Backbone.View.extend({
  className: 'user-box-wrapper',
  /*--^-- creates a new div with this class for each user*/
  template: _.template($('#connections-user-template').html()),
  events: {
    'click .user-data': 'userClicked'
  },
  initialize: function() {
    this.render();
  },
  render: function() {
    this.$el.append( this.template( this.model.toJSON()));
    console.log('User view is rendered');
  },
  userClicked: function(ev) {
    console.log("User selected");
    // console.log(ev.currentTarget);
  }
});

// Backbone Collection
app.UserCollection = Backbone.Collection.extend({
  model: app.UserModel,
  url: root + '/users',
  initialize: function() {
    // this.fetch();
  },
  parse: function(data) {
    // console.log(data.data);
    return data.data;
  }
});

// Backbone Collection View
app.UserCollectionView = Backbone.View.extend({
  el: '#users-list',
  template: _.template($('#connections-template').html()),
  initialize: function() {
    this.connections = new app.UserCollection();
    var self = this;
    this.connections.fetch().done(function() {
      self.render();
    });
  },
  render: function() {
    this.$el.html(this.template());
    console.log('User collection view is rendered');
    this.connections.each(function(user) {
      console.log('User : ' + user.get('id'));
      var userView = new app.UserView({
        model: user
      });
      this.$el.append(userView.el); /*append child view here*/
    },this);
  }
});
var connectionsView = new app.UserCollectionView();

答案 1 :(得分:-1)

主要问题是您在同一容器中呈现所有 UserView 视图:

 $('#users-list').append(this.template(this.model.toJSON()));

这就是点击事件选择器('click .user-data')正在选择其他 UserView 按钮并触发其点击的原因。

你没有问,但......这里有一些建议:

  1. 请勿从初始化功能调用渲染;
  2. 避免定义 el ;
  3. 避免使用 $ 在视图外添加视图内容。使用 这个。$ el 代替;
  4. 永远不要忘记返回此内容; 来渲染函数。
  5. 这是一个我认为你想要的jsfiddle: https://jsfiddle.net/Neviton/n52j873u/