backbone.js - 主干点击事件绑定(bind)未绑定(bind)到 DOM 元素

标签 backbone.js requirejs underscore.js

我有这些名为beats的div,我想在点击它们时注册。但是,我似乎根本无法让它们注册单击,无论是通过单击它们,还是在控制台中的特定 div 上调用 JQuery 单击事件。无论哪种方式,都不会注册任何内容。

measureView.js 创建这个beatView,它在父度量内创建一个节拍。

beatView.js:

//filename: views/beats/beatView.js
/*  This is the view for a single beat, which is contained in a measure view. */
define([ 'jquery', 'underscore',      'backbone',      'backbone/models/beat',      'text!backbone/templates/measures/audioMeasures.html',      'text!backbone/templates/beats/linearBarBeats.html',      'text!backbone/templates/beats/linearBarSVGBeats.html',      'text!backbone/templates/beats/circularPieBeats.html',      'app/dispatch',      'app/log'
], function($, _, Backbone, BeatModel, audioMeasuresTemplate, linearBarBeatsTemplate, linearBarSVGBeatsTemplate, circularPieBeatsTemplate, dispatch, log){
  return Backbone.View.extend({
    //registering backbone's click event to our toggle() function.
    events : {
      'click' : 'toggle'
    },

    //The constructor takes options because these views are created by measuresView objects.
    initialize: function(options){
      if (options) {
        console.log('options :');
        console.warn(options);
        this.model = options.model;
        // this.parentEl should be measure.cid
        this.measureBeatHolder = options.parentElHolder;
      } else {
        console.log('never really getting here');
        this.model = new BeatModel;
      }    
      this.render();
    },

    //We use css classes to control the color of the beat. A beat is essentially an empty div.
    render: function(toggle){
      var state = this.getSelectionBooleanCSS();
      if (toggle) {
        $('#beat'+toggle).removeClass(state);
        $('#beat'+toggle).addClass(this.switchSelectionBooleanValue());
      } else {
        var compiledTemplate = _.template(this.representations[this.currentBeatRepresentation], {beat: this.model, beatAngle: this.beatAngle, state: state});
        $(this.measureBeatHolder).append( compiledTemplate );
        return this;
      }
    },

    getSelectionBooleanCSS: function(){
      if (this.model.get("selected")) {
        return "ON";
      } else {
        return "OFF";
      }
    },

    switchSelectionBooleanValue: function(){
      if (this.model.get('selected') == true) {
        this.model.set('selected', "false");
      } else {
        this.model.set('selected', "true");
      }
      return this.model.get('selected');
    },

    /*
      This is called when a beat is clicked.
      It does a number of things:
      1. toggles the model's selected field.
      2. re-renders the beat.
      3. prints a console message.
      4. tells log to send a log of the click event.
      5. triggers a beatClicked event.
    */

    toggle: function(){
      console.log('getting to toggle function');
      var selectedBool = this.model.get("selected");
      this.model.set("selected", !selectedBool);
      var newBool = this.model.get("selected");
      this.render(this.model.cid);
      dispatch.trigger('beatClicked.event');
    }
  });
});

供引用:

节拍模型:

//filename: models/beat.js
/*
  This is the beat model.
  It only knows about whether or not it
  is selected.
*/
define([
  'underscore',
  'backbone'
], function(_, Backbone) {
  var beatModel = Backbone.Model.extend({
    defaults: {
      selected: false,
      state: 'OFF'
    },
    initialize: function(){
    },
    getStyleClass: function() {
      if (this.selected) {
        return 'ON';
      }
      else {
        return 'OFF';
      }
    }
  });

  return beatModel;
});

测量模型:

//filename: models/measure.js
/*
  This is the measure model.
  A component has a collection of these models.
  these models have a collection of beats.
*/
define([
  'underscore',
  'backbone',
  'backbone/collections/beats'
], function(_, Backbone, beatsCollection) {
  var measureModel = Backbone.Model.extend({
    defaults: {
      label: '0/4',
      beats: beatsCollection,
      numberOfBeats: 0,
      divisions: 8
    },
    initialize: function(){     
    }

  });

  return measureModel;
});

measureView.js:

// Filename: views/measures/measuresView.js
/*
  This is the MeasuresView.

  This is contained in a ComponentsView.
*/
define([
  'jquery',
  'underscore',
  'backbone',
  'backbone/collections/measures',
  'backbone/collections/beats',
  'backbone/models/measure',
  'backbone/views/beats/beatView',
  'text!backbone/templates/measures/audioMeasures.html',
  'text!backbone/templates/measures/linearBarMeasures.html',
  'text!backbone/templates/measures/linearBarSVGMeasures.html',
  'text!backbone/templates/measures/circularPieMeasures.html',
  'app/dispatch',
  'app/state',
  'app/log'
], function($, _, Backbone, MeasureModel, BeatsCollection, MeasuresCollection, beatView, audioMeasuresTemplate, linearBarMeasuresTemplate, linearBarSVGMeasuresTemplate, circularPieMeasuresTemplate, dispatch, state, log){
  return Backbone.View.extend({
    // el: $('.component'),

    // The different representations
    representations: {
      "audio": audioMeasuresTemplate,
      "linear-bar": linearBarMeasuresTemplate,
      "linear-bar-svg": linearBarSVGMeasuresTemplate,
      "circular-pie": circularPieMeasuresTemplate
    },

    currentMeasureRepresentation: 'linear-bar',

    //registering click events to add and remove measures.
    events : {
      'click .addMeasure' : 'add',
      'click .delete' : 'remove'
    },

    initialize: function(options){
      //if we're being created by a componentView, we are
      //passed in options. Otherwise we create a single
      //measure and add it to our collection.
      if (options) {
        this.measuresCollection = options.collection;
        this.parent = options.parent;
        this.el = options.el;
      }
      // else {
      //   this.measure = new BeatsCollection;

      //   for (var i = 0; i < 4; i++) {
      //     this.measure.add();
      //   }

      //   this.measuresCollection = new MeasuresCollection;
      //   this.measuresCollection.add({beats: this.measure});
      // }

      if (options["template-key"]) {
        this.currentBeatRepresentation = options["template-key"];
      }

      //registering a callback for signatureChange events.
      dispatch.on('signatureChange.event', this.reconfigure, this);
      //Dispatch listeners
      dispatch.on('measureRepresentation.event', this.changeMeasureRepresentation, this);

      this.render();

      //Determines the intial beat width based on the global signature. Has to be below this.render()
      this.calcBeatWidth(this.parent.get('signature'));
    },

    changeMeasureRepresentation: function(representation) {
      this.currentMeasureRepresentation = representation;
      this.render();      
    },

    render: function(){
      $(this.el).html('<div class="addMeasure">+</div>');
      var measureCount = 1;
      //we create a BeatsView for each measure.
      _.each(this.measuresCollection.models, function(measure) {
        // when representation button changes, the current representation template will get updated
        var compiledTemplate = _.template( this.representations[this.currentMeasureRepresentation], {measure: measure, beatHolder:"beatHolder"+measure.cid, measureCount:measureCount, measureAngle: 360.0 } );
        $(this.el).find('.addMeasure').before( compiledTemplate );
          console.log('measure beats: ');
          console.warn(measure.get('beats').models);
            _.each(measure.get('beats').models, function(beat) {
              // console.warn("#beat"+beat.cid.toString());
              new beatView({model:beat, parentElHolder:'#beatHolder'+measure.cid, parentCID:measure.cid, singleBeat:"#beat"+beat.cid});
            }, this);
        measureCount ++;
      }, this);
      return this;
    },

    /*
      This is called when the user clicks on the plus to add a new measure.

      It creates a new measure and adds it to the component.
      It generates a string representing the id of the measure and the ids of
      its beats and logs the creation.

      Lastly, it triggers a stopRequest, because we can't continue playing until
      all the durations get recalculated to reflect this new measure.
    */
    add: function(){
        console.log('add measure');
        var newMeasure = new BeatsCollection;

        for (var i = 0; i < this.parent.get('signature'); i++) {
          newMeasure.add();
        }

        this.measuresCollection.add({beats: newMeasure});

        //Logging
        name = 'measure' + _.last(this.measuresCollection.models).cid + '.';
        _.each(newMeasure.models, function(beats) {
          name = name + 'beat'+ beats.cid + '.';
        }, this);
        log.sendLog([[3, "Added a measure: "+name]]);

        //Render
        this.render();
        //Dispatch
        dispatch.trigger('stopRequest.event', 'off');
    },

    /*
      This is called when the user clicks on the minus to remove a measure.
    */
    remove: function(ev){
      if ($('#measure'+this.measuresCollection.models[0].cid).parent()) {
        //removing the last measure isn't allowed.
        if(this.measuresCollection.models.length == 1) {
          console.log('Can\'t remove the last measure!');
          return;
        }
        console.log('remove measure');

        //we remove the measure and get its model.
        var model = this.measuresCollection.get($(ev.target).parents('.measure').attr('id').replace('measure',''));
        this.measuresCollection.remove(model);

        //send a log event showing the removal.
        log.sendLog([[3, "Removed a measure: measure"+model.cid]]);

        //re-render the view.
        this.render();

        //trigger a stop request to stop playback.
        dispatch.trigger('stopRequest.event', 'off');
        dispatch.trigger('signatureChange.event', this.parent.get('signature'));
      }
    },
    // This is triggered by signatureChange events.
    reconfigure: function(signature) {
      console.log('MeasureView.reconfigure(signature) : signature=' +signature);
      /* if the containing component is selected, this
         triggers a request event to stop the sound.

         Then this destroys the beat collection and creates
         a new collection with the number of beats specified
         by the signature parameter.
      */
      if ($(this.parent).hasClass('selected')) {
        dispatch.trigger('stopRequest.event', 'off');
        this.measure.reset();

        for (var i = 0; i < signature; i++) {
          this.measure.add();
        }
        //re-render the view.
        this.render();

        //recalculate the widths for each beat.
        this.calcBeatWidth(signature);
        dispatch.trigger('signatureChange.event', this.parent.get('signature'));

      }
    },

    //This determines the width of each beat based on the
    //number of beats per measure or 'signature'.
    calcBeatWidth: function(signature) {
      if ($(this.el).hasClass('selected')) {
        var px = 100/$('.measure').css('width').replace(/[^-\d\.]/g, '');
        var beatWidth = (100 - ((signature*1+1)*px))/signature;

        $(this.el).children('.beat').css({
          'width' : beatWidth+'%'
        });
      }
    }
  });
});

最佳答案

Backbone 获取您的 events 对象并将所有这些事件类型和选择器委托(delegate)给 View 的 el。您希望在其上注册事件的任何 HTML 都需要插入到 View 的 el 内,并且需要将 el 插入到页面上。

通常我会这样设置我的观点:

var myView = Backbone.View.extend({

  id: 'myView',

  events: {
    'click li' : 'myEventCallback'
  },

  initialize: function() {

    $('body').append(this.el); //el on the page now
    this.render(); //fills up el with useful markup
  },

  render: function() {
    //fills el with useful markup from a template
    this.el.html( JST['myTemplate' ]() ); 
  },

  myEventCallback: function() {
    //code for handling click events on the li's inside the el of this view
  }
});

关于backbone.js - 主干点击事件绑定(bind)未绑定(bind)到 DOM 元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15768152/

相关文章:

Javascript 字符串模板慢 vs knockoutjs 模板

javascript - 使用 JavaScript API 的 Parse 中的对象安全性 - 将用户绑定(bind)到保存的对象

javascript - 如何在 Marionette ItemView 中更新的集合中的对象上运行 toJSON?

javascript - typescript - 在没有构造函数的情况下扩展类定义

firebase - Requirejs - 尝试使用 firebase 脚本时出错

conditional - RequireJS 条件依赖

javascript - 如果我有超过 1 个 JSON 组,则主干错误

javascript - 如何在更新数据库记录时使用 $watch 查看更改

javascript - Underscore.js 代码不起作用 - 给出未定义

javascript - 按距离与钛合金型号排序