14

正如大多数指南所示,我有一个简单的视图,当模型发生更改时,它会将自身绑定到重绘:

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

这个视图有一个带有输入框和提交按钮的表单。我正在绑定输入框的“更改”事件以更改模型上的相关项。(我可以手动执行此操作,也可以使用 ModelBinder 项目执行此操作,两种方式都一样。)

用户更改表单输入框中的值,然后单击提交按钮。模型被更新,视图和表单被重新渲染。提交按钮的点击事件被压扁。

我正在使用 Backbone 的 events 属性:

events : {
    'submit form' : 'save'
},

我知道该事件被忽略了,因为单击的 DOM 元素不再存在。我可以在 render() 中放置一个小的 setTimeout 以防止 HTML 被换出并且事情按预期工作,但这需要等待。

我不能成为第一个为此苦苦挣扎的人——在不丢失一些按键点击或按键信息的情况下,捕获表单“更改”事件、更新模型和重绘视图的标准方法是什么?

同样,如果我有多个表单项,则在重绘表单时更改内容后,用户无法在项目之间切换。

更新 1 - 3 天后

仍在努力寻找一个好的解决方案。我尝试过的事情:

  • 将视图的内容移动或克隆到页面上的不同区域。仍然从未收到点击事件。
  • 使用 $(document).on 或 $(document).live 而不是标准视图事件对象注册点击事件
  • 分离表单,使整个表单(输入和按钮)保持在一起而不被重绘。重绘父元素(依赖于表单值)并重新插入已绘制的表单。这修复了无法在元素之间切换的相关问题,但不修复点击事件。
  • 在 Firefox 4 中可以正常工作,但不能在 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>
4

5 回答 5

1

您要求主干中的模式。该模式是使用模型来保存表单信息(就像您正在做的那样),然后更新页面上的特定元素,而不是在每次模型更改时重新渲染所有内容。ModelBinder 提供了一种很好的方式来完成所有这些工作 (https://github.com/theironcook/Backbone.ModelBinder)。

我认为您不会找到解决无法提交不再存在的表单的方法。你需要以不同的方式完成你想要的。

于 2012-08-29T17:36:26.493 回答
1

如果我理解正确,您需要做的是将视图拆分为 2 个较小的视图。一个将在更改时重新渲染,另一个将显示表单本身。

根据您的情况,您可能希望也可能不希望第三个视图将这两个视图绑定在一起。

旁注:

当您使用 MVC 设计模式时,它往往是递归的。我认为情况就是这样。将表单中的数据视为模型,将重新渲染视图视为 MVC 的“视图”部分,将表单视图视为“控制器”。

于 2012-09-05T18:29:41.077 回答
0

不太明白你在做什么,看代码我明白你要模拟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>
于 2012-08-27T18:05:08.503 回答
0

您的问题应该有两种可能的解决方案:

  1. 绑定到点击提交输入,而不是提交事件本身。

    events: { 'click input[type=submit]' : 'save' }

    这应该在完全替换 html 后仍然存在。

  2. 渲染后手动重新委托事件。如果您要重新渲染具有子视图的视图,这通常是必要的,但它也适用于此处。

    this.delegateEvents()

于 2012-08-28T20:26:11.743 回答
0

我不知道我是否会完全依靠骨干来解决您的问题。我认为这是骨干允许您填写自己的代码的情况之一。您可以稍微修改模板并听取模型中的变化,然后决定需要重新渲染或更新视图的天气。我不认为我会尝试重新渲染表单部分,除非模型被重置

<!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>
于 2012-08-30T19:22:06.897 回答