javascript - Backbone : Show validation errors for each model in a collection on VIEW

标签 javascript backbone.js backbone.js-collections

我正在开发一个 Backbone 应用程序,允许用户添加多个项目。

这是我的模型:

//Model
    var Item = Backbone.Model.extend({
      defaults: {
        part1: 'hello',
        part2: 'world'
      },
      validate: function (attr, options) {
        var error = '';
        //console.log(attr);
        if(attr.part2 == "world1"){
          this.trigger('err:world1');
          error = 'world 1 error';
        }
        if(attr.part2 == "world3"){
          this.trigger('err:world3');
          error =  'world 3 error';
        }
      }
    });

集合:

 //Collection
    var List = Backbone.Collection.extend({
      model: Item,

      validateModels: function() {
        var cloneCollection = this.clone();
        var errorModels = this.filter(function(m) {
          if (!m.isValid()) {
            return m;
          }
        });
        // cloneCollection.remove(errorModels);
        return cloneCollection;
      }
    });

我允许用户从 View 中添加/删除项目:

//Item View
    var ItemView = Backbone.View.extend({
      tagName: 'li', // name of tag to be created        

      events: {
        'click span.swap':  'swap',
        'click span.delete': 'remove'
      },    

      initialize: function(){
        _.bindAll(this, 'render', 'unrender', 'swap', 'remove'); // every function that uses 'this' as the current object should be in here

        this.model.bind('change', this.render);
        this.model.bind('remove', this.unrender);

        this.model.on('err:world1', this.world1Err);
        this.model.on('err:world3', this.world3Err);
      },

      render: function(){
        $(this.el).html('<span style="color:black;">'+this.model.get('part1')+' '+this.model.get('part2')+'</span> &nbsp; &nbsp; <span class="swap" style="font-family:sans-serif; color:blue; cursor:pointer;">[swap]</span> <span class="delete" style="cursor:pointer; color:red; font-family:sans-serif;">[delete]</span> <span class="error" style="color:red; font-family:sans-serif;"></span>');
        return this; // for chainable calls, like .render().el
      },

      unrender: function(){
        $(this.el).remove();
      },

      swap: function(){
        var swapped = {
          part1: this.model.get('part2'),
          part2: this.model.get('part1')
        };
        this.model.set(swapped);
      },

      remove: function(){
        this.model.destroy();
      },

      world1Err: function(){
        alert('World 1 Err');
        //console.log(this);
      },

      world3Err: function(){
        alert('World 3 Err');
      }
    });

//Composite View
    var ListView = Backbone.View.extend({
      el: $('body'), // el attaches to existing element
      events: {
        'click button#add': 'addItem',
        'click  button#save': 'saveCollection'
      },

      initialize: function(){
        _.bindAll(this, 'render', 'addItem', 'appendItem'); // every function that uses 'this' as the current object should be in here

        this.collection = new List();
        this.collection.bind('add', this.appendItem); // collection event binder

        this.counter = 0;
        this.render();
      },

      render: function(){
        var self = this;
        $(this.el).append("<button id='add'>Add list item</button>");
        $(this.el).append("<button id='save'>SAVE</button>");
        $(this.el).append("<ul></ul>");
        _(this.collection.models).each(function(item){ // in case collection is not empty
          self.appendItem(item);
        }, this);
      },

      addItem: function(){
        this.counter++;
        var item = new Item();
        item.set({
          part2: item.get('part2') + this.counter // modify item defaults
        });
        this.collection.add(item);
      },

      appendItem: function(item){
        var itemView = new ItemView({
          model: item
        });
        $('ul', this.el).append(itemView.render().el);
      },

      saveCollection: function(){
        var collectionLength = this.collection.length;
        if(collectionLength > 0){
          this.collection.validateModels();
          //console.log(status);
        } 
        else
          alert('Collection is empty. Please add something.');
      }

    });

现在,当用户启动应用程序时,他/她将看到以下屏幕: enter image description here

当用户单击“添加”时,将添加项目,如下所示: enter image description here

我已经进行了硬编码验证,当用户单击保存时,第一个和第三个添加的元素将返回错误。

我遇到的问题是如何仅在特定项目 View 中显示这些错误。 例如,如果1st3rd项出现错误,则模型会返回该错误,但我想将该错误映射到<仅strong>1st和3rd列表项,就像这样: enter image description here

请帮我建议解决方法。提前致谢!

更新: 我已经找到了解决办法。因此,每当出现验证错误时,我都会执行以下操作:

world1Err: function(){
        this.$el.find('span.error').text('Error Occured.')
      },

最佳答案

需要注意的关键事项:

  • 不要使用 $(this.el),而是使用 this.$el
  • 使用listenTo而不是on(绑定(bind))来避免内存泄漏(额外的优点是回调将在监听器作为上下文的情况下被触发,在您的情况下 View )
  • 不要重写 Backbone.Viewremove 方法,除非您知道自己在做什么并自己处理它所做的所有事情

明智的举动:

  • 使用主干事件哈希绑定(bind)的事件处理程序的默认上下文是 View 本身,同时使用listenTo,无需使用_.bindAll
  • 主干集合有lots of underscore methods built in ,您可以执行 this.collection.each 而不是 _(this.collection.models).each
  • 您可以使用下划线,请使用 template方法而不是手动生成模板
  • 您可以快速执行this.$(selector)而不是 this.$el.find(selector)$(selector, this.el)
  • 无需像new Item()那样手动创建模型实例,设置其属性,然后将其添加到集合中,只需将属性传递给集合add方法即可,它将在内部创建一个模型实例
  • 您可以使用集合长度,而不是手动跟踪计数属性

建议:

  • 不要使用内联样式
  • 让项目 View 自行渲染,并使用 view.el 而不是 view.render().el (我真的不知道是谁发明了这种方式或为什么)

您可以概括您的代码,如下所示:

var Item = Backbone.Model.extend({
  defaults: {
    message: 'hello world',
    count: 0
  },
  validate: function(attr, options) {
    if (attr.count % 2 != 0) {
      this.trigger('err', this.get('message') + attr.count + ' error');
    }
  }
});

var List = Backbone.Collection.extend({
  model: Item,
  validateModels: function() {
    this.each(function(m) {
      m.isValid(); // invoke models validate method
    });
  }
});

var ItemView = Backbone.View.extend({
  tagName: 'li',
  template: _.template($('#item').text()),
  events: {
    'click span.swap': 'swap',
    'click span.delete': 'remove' // triggers view's built in remove method
  },
  initialize: function() {
    this.listenTo(this.model, 'change', this.render);
    this.listenTo(this.model, 'err', this.errorHandler);
    this.render();
  },
  render: function() {
    this.$el.html(this.template(this.model.toJSON()));
    return this;
  },
  swap: function() {
    var words = this.model.get('message').split(' ');
    this.model.set({
      message: words.reverse().join(' ')
    });
  },
  errorHandler: function(msg) {
    this.$('span.error').text(msg)
  }
});

var ListView = Backbone.View.extend({
  template: $('#itemView').text(),
  events: {
    'click button#add': 'addItem',
    'click  button#save': 'saveCollection'
  },
  initialize: function() {
    this.collection = new List();
    this.listenTo(this.collection, 'add', this.appendItem);
    this.render();
  },
  render: function() {
    this.$el.html(this.template);
    this.collection.each(function(model) {
      this.appendItem(model);
    }, this);
  },
  addItem: function() {
    this.collection.add({
      count: this.collection.length
    }, {
      validate: true
    });
  },
  appendItem: function(item) {
    this.$('ul').append(new ItemView({
      model: item
    }).el);
  },
  saveCollection: function() {
    if (this.collection.length > 0) {
      this.collection.validateModels();
    } else
      alert('Collection is empty. Please add something.');
  }
});
new ListView().$el.appendTo('body');
li span {
  font-family: sans-serif;
}
span.control {
  cursor: pointer;
}
span.swap {
  color: blue;
}
span.delete {
  color: orange;
}
span.error {
  color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.2.3/backbone-min.js"></script>
<script type="text/template" id="item">
  <span><%=message%> <%=count? count: ''%></span>
  <span class="swap control">[swap]</span>
  <span class="delete control">[delete]</span> 
  <span class="error"></span>
</script>
<script type="text/template" id="itemView">
  <button id='add'>Add list item</button>
  <button id='save'>SAVE</button>
  <ul></ul>
</script>

关于javascript - Backbone : Show validation errors for each model in a collection on VIEW,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34426550/

相关文章:

javascript - backbone.js model.fetch 不向 php 发送任何数据

backbone.js - 如何设置收藏集的网址

javascript - 修改javascript中的局部变量

javascript - 根据地理位置检测区域

javascript - Backbone 过滤器集合按名称返回模型?

javascript - 通过 cid 而不是 id 在 Backbone.js 集合中查找模型

使用 Marionette 模板的backbone.js Collection View 示例

客户端的 JavaScript require()

javascript - 图像无法在phonegap上正确加载

javascript - 依次打开每个 Bootstrap 3 Modal,而不是一起打开