如何使用Backbone.js报告无效的表单字段
我使用主干来管理HTML表单的状态。模型的作用是处理验证。视图的作用是包装HTML表单并响应模型发出的如何使用Backbone.js报告无效的表单字段,backbone.js,validation,Backbone.js,Validation,我使用主干来管理HTML表单的状态。模型的作用是处理验证。视图的作用是包装HTML表单并响应模型发出的更改或错误事件 主干似乎只在给定字段实际有效时发出change事件。这导致了一些意想不到的行为,让我觉得我做错了 以下是我所做工作的总结: 1.初始加载序列化表单并将其注入模型 2.当发出错误事件时,我会在无效字段旁边生成错误节点。 3.当发出change事件时,我会删除(现在有效)字段旁边的错误注释 当使用初始有效表单呈现页面,并且用户使字段无效时,消息按预期显示;但是,模型从不在内部更新字段
更改
或错误
事件
主干似乎只在给定字段实际有效时发出change
事件。这导致了一些意想不到的行为,让我觉得我做错了
以下是我所做工作的总结:
1.初始加载序列化表单并将其注入模型
2.当发出错误
事件时,我会在无效字段旁边生成错误节点。
3.当发出change
事件时,我会删除(现在有效)字段旁边的错误注释
当使用初始有效表单呈现页面,并且用户使字段无效时,消息按预期显示;但是,模型从不在内部更新字段。因此,当用户纠正错误时,永远不会发出change
事件
当一个页面以一个最初无效的表单呈现时,一切看起来都很正常。。。但这只是因为模型的初始属性为空。更正字段会使消息消失,但如果再次将其更改为无效状态,消息将永远不会消失
我做错了什么?也许我应该用另一种方法来代替
我的模型
我的看法
FooForm=Backbone.View.extend({
活动:{
“更改:输入”:“onFieldChange”
},
初始化:函数(选项){
this.model.on('error',this.renderErrors,this);
this.model.on('change',this.updateFields,this);
//仅调试
this.model.on('all',function()){
console.info(“[Foo all]”,参数,this.toJSON()
});
this.model.set(this.serialize());
},
onFieldChange:函数(事件){
变量字段=event.target,
name=field.name,
value=field.value;
此.model.set(名称、值);
},
渲染器:函数(模型、错误){
_.每个(错误、功能(消息、字段名){
变量el=$(“#”+字段名),
警报=$('').addClass('error');
el.parent().find('.error').remove();
_.每个(消息、功能(消息){
alert.clone().text(message).insertAfter(el);
});
});
},
updateFields:函数(模型、选项){
如果(!options | |!options.changes)返回;
_.each(u.keys(options.changes)、函数(fieldName){
变量el=$(“#”+字段名);
el.parent().find('.error').remove();
});
},
序列化:函数(){
var raw=this.$el.find(':input').serializeArray(),
数据={},
视图=此;
$.each(原始,函数(){
//从表单字段的名称中获取模型的字段名称
var name=this.name;
if(数据[名称]!==未定义){
如果(!data[name].push){
数据[名称]=[数据[名称];
}
数据[名称].push(this.value | |“”);
}
否则{
数据[名称]=this.value | |“”;
}
});
返回数据;
}
});
无法使用本机主干验证来验证单个字段
在我的应用程序中,我使用此验证插件:
然后在模型中为每个字段添加验证规则(这是可选的,因此不需要将其添加到所有模型):
或
我确实考虑过Brase.ValueStudio,但是它的许可证与我的应用程序不兼容,所以我不得不自己翻滚。我想总体来说,这是大多数应用程序的解决方案。我的问题是,我假设当我
model.set('field',…)
时,验证也会起同样的作用。谢谢你的回答。
var Foo = Backbone.Model.extend({
validate: function(attr) {
var errors = {};
if (_.isEmpty(attr)) return;
if (attr.foo && attr.foo != 123) {
errors.foo = ['foo is not equal to 123'];
}
if (attr.bar && attr.bar != 456) {
errors.bar = ['bar is not equal to 456'];
}
return _.isEmpty(errors) ? undefined : errors;
}
});
FooForm = Backbone.View.extend({
events: {
'change :input': 'onFieldChange'
},
initialize: function(options) {
this.model.on('error', this.renderErrors, this);
this.model.on('change', this.updateFields, this);
// Debugging only
this.model.on('all', function() {
console.info('[Foo all]', arguments, this.toJSON())
});
this.model.set(this.serialize());
},
onFieldChange: function(event) {
var field = event.target,
name = field.name,
value = field.value;
this.model.set(name, value);
},
renderErrors: function(model, errors) {
_.each(errors, function(messages, fieldName) {
var el = $('#' + fieldName),
alert = $('<div/>').addClass('error');
el.parent().find('.error').remove();
_.each(messages, function(message) {
alert.clone().text(message).insertAfter(el);
});
});
},
updateFields: function(model, options) {
if (!options || !options.changes) return;
_.each(_.keys(options.changes), function(fieldName) {
var el = $('#' + fieldName);
el.parent().find('.error').remove();
});
},
serialize: function() {
var raw = this.$el.find(':input').serializeArray(),
data = {},
view = this;
$.each(raw, function() {
// Get the model's field name from the form field's name
var name = this.name;
if (data[name] !== undefined) {
if (!data[name].push) {
data[name] = [data[name]];
}
data[name].push(this.value || '');
}
else {
data[name] = this.value || '';
}
});
return data;
}
});
var NewReview = Backbone.Model.extend({
initialize: function() {
/* ... */
},
validation: {
summary: {
required: true,
minLength: 10
},
pros: {
required: true,
minLength: 10
},
cons: {
required: true,
minLength: 10
},
overall: function(value) {
var text = $(value).text().replace(/\s{2,}/g, ' ');
if (text.length == 0) text = value;
if (text.length < 20) return "Overall review is too short";
},
rating: {
range: [0.5, 5]
},
product_id: {
required: true
}
}
});
if (this.model.validate()) { ... }
if (this.model.isValid("summary")) { ... }