Javascript 主干:在视图上显示集合中每个模型的验证错误

Javascript 主干:在视图上显示集合中每个模型的验证错误,javascript,backbone.js,backbone.js-collections,Javascript,Backbone.js,Backbone.js Collections,我正在开发一个主干应用程序,允许用户添加多个项目 这是我的型号: //Model var Item = Backbone.Model.extend({ defaults: { part1: 'hello', part2: 'world' }, validate: function (attr, options) { var error = ''; //console.log(attr);

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

这是我的型号:

//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;
      }
    });
收藏:

//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;
      }
    });
我允许用户从视图中添加/删除项目,如下所示:

//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.');
      }

    });

需要注意的关键事项

  • 不要使用
    $(this.el)
    ,而是使用
    this.$el
  • 使用
    listenTo
    而不是
    (bind)上的
    ,以避免内存泄漏(另一个优点是,在您的情况下,
    视图将以侦听器作为上下文触发回调)
  • 不要覆盖
    Backbone的
    remove
    方法。除非您知道自己在做什么,并且自己处理它所做的所有事情,否则请查看
智能移动:

//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;
      }
    });
  • 使用主干事件散列绑定的事件处理程序的默认上下文是视图本身,以及使用
    listenTo
    ,无需使用
    \uu.bindAll
  • 主干集合有,您可以执行
    this.collection.each
    而不是
    (this.collection.models)。each
  • 您可以使用下划线,使用它的方法而不是手动生成模板
  • 您可以快速执行,而不是
    this.$el.find(selector)
    $(selector,this.el)
  • 无需手动创建模型实例,如
    newitem()
    ,设置其属性,然后将其添加到集合,只需将属性传递到集合
    add
    方法,它将在内部创建一个模型实例
  • 您可以使用集合长度,而不是手动跟踪count属性
建议:

//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.el
    而不是
    view.render().el
    (我真的不知道是谁发明了这种方法,也不知道为什么)

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

var Item=Backbone.Model.extend({
默认值:{
信息:“你好,世界”,
计数:0
},
验证:功能(属性、选项){
如果(属性计数%2!=0){
this.trigger('err',this.get('message')+attr.count+'error');
}
}
});
var List=Backbone.Collection.extend({
型号:项目,
validateModels:function(){
此。每个功能(m){
m、 isValid();//调用模型验证方法
});
}
});
var ItemView=Backbone.View.extend({
标记名:“li”,
模板:35;.template($('#item').text()),
活动:{
'单击span.swap':'swap',
'单击span.delete':'删除'//触发视图的内置删除方法
},
初始化:函数(){
this.listenTo(this.model,'change',this.render);
this.listenTo(this.model'err',this.errorHandler);
这个。render();
},
render:function(){
this.el.html(this.template(this.model.toJSON());
归还这个;
},
交换:函数(){
var words=this.model.get('message').split('');
这个。模型。集合({
消息:words.reverse().join(“”)
});
},
errorHandler:函数(msg){
此.$('span.error').text(msg)
}
});
var ListView=Backbone.View.extend({
模板:$(“#项目视图”).text(),
活动:{
'单击按钮#添加':'添加项',
'单击按钮#保存':'保存集合'
},
初始化:函数(){
this.collection=新列表();
this.listenTo(this.collection,'add',this.appendItem);
这个。render();
},
render:function(){
this.$el.html(this.template);
此.collection.each(函数(模型){
本附录项(型号);
},这个);
},
附加项:函数(){
this.collection.add({
计数:this.collection.length
}, {
验证:正确
});
},
附录项:功能(项){
此.$('ul').append(新项目视图({
型号:项目
})(e.el);
},
saveCollection:函数(){
如果(this.collection.length>0){
this.collection.validateModels();
}否则
警报('集合为空。请添加内容');
}
});
新建ListView().$el.appendTo('body')
li span{
字体系列:无衬线;
}
跨度控制{
光标:指针;
}
互换{
颜色:蓝色;
}
span.delete{
颜色:橙色;
}
广度误差{
颜色:红色;
}

[互换]
[删除]
添加列表项
拯救

    哇,谢谢@TJ,这很有帮助。我一定会记住你的建议并付诸实施。