使用Backbone在模型更改时渲染表单会导致表单UI错误
Using Backbone to render a form on model change causes form UI errors
我有一个简单的视图,当模型发生变化时,它会绑定自己来重新绘制,正如大多数指南所示:
this.model.bind('change', this.render, this);
此视图有一个带有输入框和提交按钮的表单。我正在绑定到输入框的"更改"事件,以更改模型上的相关项。(我可以手动完成,也可以使用ModelBinder项目,无论哪种方式都可以。)
用户更改表单输入框中的值,然后单击提交按钮。模型将更新,视图和形状将重新渲染。提交按钮的单击事件被压扁。
我正在使用Backbone的事件属性:
events : {
'submit form' : 'save'
},
我知道这个事件被忽略了,因为被点击的DOM元素已经不在了。我可以在render()中放置一个小的setTimeout,以防止HTML被交换出去,并且事情按预期进行,但这需要等待。
我不是第一个遇到这种情况的人——在不丢失一些按键或按键信息的情况下,捕捉表单"更改"事件、更新模型和重新绘制视图的标准方法是什么?
类似地,如果我有多个表单项,则在重新绘制表单时更改内容后,用户无法在这些项之间切换。
更新1-3天后
仍在努力寻找一个好的解决方案。我尝试过的东西:
- 将视图的内容移动或克隆到页面上的其他区域。单击事件仍然从未收到
- 使用$(document).on或$(document).live而不是标准视图事件对象注册单击事件
- 分离出表单,使整个表单(输入和按钮)保持在一起而不被重新绘制。重新绘制父元素(依赖于表单值)并重新插入已绘制的表单。这解决了无法在元素之间进行制表的相关问题,但不能解决单击事件
- 在firefox4中按需工作,但不是ie9或chrome。*
更新2-使用示例代码
其中一条评论要求提供一些代码。我已经将代码大量简化为一页,并将其包含在下面。实际应用要复杂得多。使用下面这样简单的代码,我可以在更改时手动重新呈现页面的部分内容。在实际的应用程序中,我使用的是dustjs模板,即使我不重新呈现表单,但重新呈现包含表单的元素,我在点击提交时也会遇到问题。我希望有一种典型的主干应用程序的"模式",包括复杂的页面、模型和表单。
大多数"演示"应用程序,甚至我看到的使用主干网的网站,似乎大多是以演示为重点的应用程序,实际上并没有从用户那里收集大量输入。如果您知道一个好的基于主干的以数据收集为重点的应用程序/演示程序,那将是很有帮助的。
代码:
<!doctype html>
<html lang="en">
<head>
<script src="libs/jquery.js"></script>
<script src="libs/underscore.js"></script>
<script src="libs/backbone.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/template" id="edit_user_template">
<h3>Edit User <%=id%> : <%=name%></h3>
<form>
Name: <input type="text" name="name" value="<%=name%>"><br/>
Email: <input type="text" name="email" value="<%=email%>"><br/>
<input type="submit">
</form>
</script>
<script>
var UserModel = Backbone.Model.extend({});
var model = new UserModel({id: '1', name:'Joe', email:'a@a.com'});
var UserView = Backbone.View.extend({
events : {
'submit form' : 'save',
'change input' : 'inputChange'
},
initialize: function(){
this.model.bind('change', this.render, this);
},
render: function(){
var template = _.template($('#edit_user_template').html(), this.model.toJSON());
this.$el.html(template);
},
inputChange : function(evt){
var target = $(evt.currentTarget);
this.model.set(target.attr('name'), target.val());
},
save : function(event){
console.log('saving');
event.preventDefault();
//this.model.save();
}
});
var view = new UserView({model: model, el: '#container'});
view.render();
</script>
</body>
</html>
您要求在主干中使用模式。模式是使用模型来保存表单信息(就像您正在做的那样),然后更新页面上的特定元素,而不是每次模型更改时都重新呈现所有内容。ModelBinder提供了一种很好的方法来实现这一切(https://github.com/theironcook/Backbone.ModelBinder)。
我不认为你会找到一种方法来避免无法提交一份已经不存在的表格。你需要用不同的方式来完成你想要的。
如果我理解正确,您需要做的是将视图拆分为两个较小的视图。一个将在更改时重新渲染,另一个将显示形式本身。
根据您的情况,您可能希望也可能不希望第三视图将这两个视图绑定在一起。
附带评论:
当您使用MVC设计模式时,它往往是递归的。我认为情况就是这样。将表单中的数据视为模型,将重新呈现视图视为MVC的"视图"部分,将表单视图视为"控制器"。
不太明白你想做什么,看着代码我明白你想模拟knockoutjs的绑定模型,这太棒了。
不管怎样,如果你把两个视图分成在同一个模型上操作,你可以得到类似的"效果"。一个更新模型,而另一个观察变化并自动更新。
这是代码:
<!doctype html>
<html lang="en">
<head>
<script src="libs/jquery.min.js"></script>
<script src="libs/underscore.min.js"></script>
<script src="libs/backbone.min.js"></script>
</head>
<body>
<div id="header"></div>
<div id="container"></div>
<script type="text/template" id="user_template">
<h3>Edit User <%=id%> : <%=name%></h3>
</script>
<script type="text/template" id="edit_user_template">
<form>
Name: <input type="text" name="name" value="<%=name%>"><br/>
Email: <input type="text" name="email" value="<%=email%>"><br/>
<input type="submit">
</form>
</script>
<script>
var UserModel = Backbone.Model.extend({});
var model = new UserModel({id: '1', name:'Joe', email:'a@a.com'});
var UserView = Backbone.View.extend({
initialize: function(){
this.model.on("change", this.render, this);
},
render: function(){
var template = _.template($('#user_template').html(), this.model.toJSON());
this.$el.html(template);
return this;
},
});
var FormView = Backbone.View.extend({
events : {
'submit form' : 'save',
'keyup input' : 'inputChange'
},
render: function(){
var template = _.template($('#edit_user_template').html(), this.model.toJSON());
this.$el.html(template);
return this;
},
inputChange : function(evt){
console.log('change');
var target = $(evt.currentTarget);
this.model.set(target.attr('name'), target.val());
},
save : function(event){
console.log('saving');
event.preventDefault();
//this.model.save();
}
});
var user = new UserView({model: model, el: '#header'});
user.render();
var form = new FormView({model: model, el: '#container'});
form.render();
</script>
</body>
</html>
您的问题应该有两种可能的解决方案:
-
绑定以单击提交输入,而不是提交事件本身。
events: { 'click input[type=submit]' : 'save' }
这应该在html被完全替换后仍然有效。
-
渲染后手动重新委托事件。如果重新渲染具有子视图的视图,这通常是必要的,但它也适用于此处。
this.delegateEvents()
我不知道我是否会完全依靠骨干来解决你的问题。我认为这是主干网允许您用自己的代码填充的情况之一。您可以稍微修改一下模板,听取模型中的更改,然后决定是需要重新渲染视图,还是只更新视图。我不认为我会尝试重新渲染表单部分,除非模型重置
<!doctype html>
<html lang="en">
<head>
<script src="libs/jquery.min.js"></script>
<script src="libs/underscore.min.js"></script>
<script src="libs/backbone.min.js"></script>
</head>
<body>
<div id="container"></div>
<script type="text/template" id="edit_user_template">
<h3>Edit User <span class="f-id"><%=id%></span> : <span class="f-name"><%=name%></span></h3>
<form>
Name: <input type="text" name="name" value="<%=name%>"><br/>
Email: <input type="text" name="email" value="<%=email%>"><br/>
<input type="submit">
</form>
</script>
<script>
var UserModel = Backbone.Model.extend({});
var model = new UserModel({id: '1', name:'Joe', email:'a@a.com'});
var UserView = Backbone.View.extend({
events : {
'submit form' : 'save',
'change input' : 'inputChange'
},
initialize: function(){
//you could have the change event trigger updates instead of re-rendering the form
//this.model.bind('change', this.update, this);
},
render: function(){
var template = _.template($('#edit_user_template').html(), this.model.toJSON());
this.$el.html(template);
},
inputChange : function(evt){
var target = $(evt.currentTarget);
this.model.set(target.attr('name'), target.val());
},
update:function(){
// you could trigger other things here if you need other views to know what is going on.
$(".f-id").text(this.model.get("id"));
$(".f-name").text(this.model.get("name"));
},
save : function(event){
console.log('saving');
this.update();
event.preventDefault();
}
});
var view = new UserView({model: model, el: '#container'});
view.render();
</script>
</body>
</html>
- JavaScript表单错误
- 流星 1.3 自动表单/快速表单错误
- PHP&jQuery联系人表单错误
- windows onload-从表单错误中不引人注意地解析值
- JavaScript表单错误
- j查询验证重置表单错误,当我关闭对话框
- 在 jsf 的表单错误上重新加载某个 jQuery 代码
- 自动提交表单错误
- 如何返回我的表单错误
- jqBootstrap验证表单错误
- Symfony2+Ajax:删除表单错误
- 滚动到第一个表单错误使用livevalvalidation
- 在提交之前用javascript检查django表单错误的最好方法是什么?
- 如何在Rails中正确地使用Ajax并处理部分的表单错误
- javascript游戏中的表单错误
- 带有警报的表单错误验证
- 动态更改表单错误的角度提示内容
- 通过javascript/ajax在动态创建的表中创建表单错误
- jQuery表单错误
- 手机上的表单错误,而不是桌面