子视图更改事件未触发

时间:2016-11-10 03:15:52

标签: javascript jquery events backbone.js ecmascript-6

我从使用Grunt作为我的构建工具切换到Webpack。转换相对平滑。至少直到我注意到我遇到的以下问题。

我目前在我的应用程序中有很多Backbone视图,并且在我移动时使用工厂方法实例化和垃圾收集。将我的项目转换为Webpack之后,虽然我的一个子视图change事件不再触发(也就是说它之前已经触发,所以我知道它在某些时候起作用)。当用户在for循环中输入父视图时生成子视图:

for (var i=0; i<this.dayIds.length; i++) {
  this.timeSelectorDayViews[i] = new timeSelectorDay({
    el: '#timePickersWrapper',
    data: {
      time: this.model.get(this.dayIds[i]),
      date: moment(this.model.get('weekStartDate')).add(i, 'd')
    }
  }).render();
}

以下是子视图代码:

'use strict';

import { app } from '../app';

export const timeSelectorDay = Backbone.View.extend({
    initialize: function(data) {
      this.options = data;
      this.options.data.formattedDate = moment(this.options.data.date).format('YYYY-MM-DD');
      this.events = _.clone(this.events) || {};
      var inputEventKey = 'change' + ' .input-' + this.options.data.formattedDate;
      this.events[inputEventKey] = 'inputCheck';
      this.totalHours = 0;
    },
    render: function(){
      this.template = _.template(app.createTemplate('templates/timeSelectorDay.tpl', this.options.data));
      $(this.options.el).append(this.template({}));
      this.delegateEvents();
      this.checkTotalTime();
      return this;
    },
    inputCheck: function() {
      ...
      ... //Doing some computational logic here
      ...
      app.event_bus.trigger('totalTime');
      app.event_bus.trigger('inputChange');
    },
    checkTotalTime: function(){
      var totalTimeDiv = document.getElementById('totalHours-'+this.options.data.formattedDate);
      var totalTime = parseFloat(totalTimeDiv.innerHTML);
      if (totalTime>0) {
        totalTimeDiv.style.backgroundColor = '#69a776';
      } else {
        totalTimeDiv.style.backgroundColor = '#dd3c3c';
      }
      this.totalHours = totalTime;
    }
});

值得注意的是,我通过字符串连接以及更改事件数组以非正常骨干方式生成事件。这是因为我需要为HTML元素分配唯一ID。

我的第一个想法是,由于某种原因,可能由于新的webpack实现而没有正确附加事件。我尝试通过调用this.delegateEvents()解决此问题但没有成功。

我的下一个想法是,事件可能不属于视图范围或者以某种方式被分离,因此我确认input元素是视图中可附加的事件。也不是问题。

我认为对于这种情况最令人费解的是,如果我将change事件更改为click事件,它可以完美地运行并触发相应的函数:

var inputEventKey = 'click' + ' .input-' + this.options.data.formattedDate;

更新(附加数据):

子视图模板:

<div id="timeselectors-<%= formattedDate%>" class="row small-collapse medium-uncollapse margin-bottom border">
  <div class="small-12 columns"><%= moment(date).format('dddd') %> (<%= moment(date).format('MM/DD/YYYY') %>)</div>
  <div class="small-10 columns">
    <div class="row small-collapse">
      <div class="small-5 medium-2 columns morning">
        <input id="morning-login" class="input-<%= formattedDate%>" type="text" id="example" placeholder="Login"/>
      </div>
      <div class="small-1 medium-1 columns morning dash"> - </div>
      <div class="small-5 medium-2 columns morning">
        <input id="morning-logout" class="input-<%= formattedDate%>" type="text" id="example" placeholder="Logout"/>
      </div>
      <div class="small-5 medium-2 medium-offset-1 columns afternoon">
        <input id="afternoon-login" class="input-<%= formattedDate%>" type="text" id="example" placeholder="Login"/>
      </div>
      <div class="small-1 medium-1 columns afternoon dash"> - </div>
      <div class="small-5 medium-2 columns afternoon end">
        <input id="afternoon-logout" class="input-<%= formattedDate%>" type="text" id="example" placeholder="Logout"/>
      </div>
    </div>
  </div>
  <div id="totalHours-<%= formattedDate%>" class="small-2 columns hours">
    <%= time %>
  </div>
</div>

至于我的webpack配置,我使用gulp作为我的webpack驱动程序,但是这里是我传递给webpack实例的配置:

{
  entry: './src/app/client-app/index.js',
  output: {
    path: path.join(__dirname, 'dist/app/js'),
    filename: 'app.bundle.js'
  },
  plugins: [
    new webpack.ProvidePlugin({
      '_': 'lodash'
    })
  ],
  module: {
    loaders: [
      {test: /\.js$/, exclude: /node_modules/, loaders: ['babel'] },
    ]
  }
}

webpack条目文件:

// import 'babel-polyfill';
import 'script!jquery';
import 'script!lodash';
// import 'script!what-input';
import Backbone from 'backbone';
import 'backbone-validation';
import 'script!jquery.cookie';
import 'script!moment';
import 'script!fastclick';
import 'script!clndr';
import 'script!timepicker';
import 'script!foundation-sites/js/foundation';

import { app } from './app';

$(document).ready(function () {
    app.init();

  //Initialize Foundation
  $(document).foundation();
});

除了我应该提到的所有内容之外,如果您想要克隆存储库并重现问题,所有代码都可以在github上公开获取:

https://github.com/wootencl/timeSheetApplication/tree/ProjectRewrite

更新(2016年11月14日):在一个受控环境(plunkr等)中不成功地尝试复制之后,我决定开始慢慢地向后工作来诊断问题。到目前为止,我发现问题不是来自webpack,而是来自我项目的模块化。我从每个文件的brash脚本标记到导入所有依赖模块的根index.js,这似乎是事情开始出错的地方。

1 个答案:

答案 0 :(得分:1)

虽然我无法用提供的代码发现问题,但我可以开始指出一些可以改进的事情。

首先,我会直接传递自定义选项,不需要嵌套的data对象。

this.timeSelectorDayViews[i] = new timeSelectorDay({
    el: '#timePickersWrapper',
    time: this.model.get(this.dayIds[i]),
    date: moment(this.model.get('weekStartDate')).add(i, 'd')
}).render();

然后在子视图中,在Backbone使用提供的events创建调用setElement之前,我会将el更改移动到构造函数中。

export const timeSelectorDay = Backbone.View.extend({
    // The template should be compiled once here, then used in the render function.
    template: _.template(app.createTemplate('templates/timeSelectorDay.tpl')),

    constructor: function(options) {
        this.options = _.extend({
            /* default options could go here */
        }, options); // make a copy and use it.
        this.options.formattedDate = moment(this.options.date).format('YYYY-MM-DD');

        // change the events hash before delegating the events.
        // the constructor is a good place for this.
        this.events['change .input-' + this.options.formattedDate] = 'inputCheck';

        Backbone.View.prototype.constructor.apply(this, arguments);
    },

    initialize: function(options) {
        this.totalHours = 0;
    },
    render: function() {
        // use the $el directly, avoid global jQuery.
        this.$el.html(this.template(this.options));
        this.checkTotalTime();
        return this;
    },
    inputCheck: function() {
        // Doing some computational logic here
        // ...snip...
        app.event_bus.trigger('totalTime');
        app.event_bus.trigger('inputChange');
    },
    checkTotalTime: function() {
        // use Backbone's jQuery shortcut instead of the Javascript native
        // api, it's easier and it is scoped to the view's el.
        var totalTimeDiv = this.$('#totalHours-' + this.options.formattedDate),
            totalTime = parseFloat(totalTimeDiv.html());
        // I would avoid changing the css in favor of toggling a class.
        // It would keep the style separated from the logic.
        totalTimeDiv.css('background-color', totalTime > 0 ? '#69a776' : '#dd3c3c');
        this.totalHours = totalTime;
    }
});