更新Backbone.js查看变得一团糟

时间:2012-05-06 21:03:10

标签: backbone.js

给出一个模型:

MyModel = Backbone.Model.extend({
  defaults: {
    name: '',
    age: -1,
    height: '',
    description: ''
  }
});

和一个视图将模型呈现为列表:

MyView  = Backbone.View.extend({
  tagName: 'ul',
  className: 'MyView',

  render() {
    var values = {
      name: this.model.get('name'),
      age: this.model.get('age'),
      height: this.model.get('height'),
      description: this.model.get('description')
    }

    var myTemplate = $('#MyView-Template').html();
    var templateWithValues = _.template(myTemplate , values);
  }
});

和视图加载的模板:

<script type="text/template" id="MyView-Template">  
  <li class="name"><%= name %></li>
  <li class="age"><%= age %></li>
  <li class="name"><%= height%></li>
  <li class="name"><%= description%></li>
</script>

一切正常,虽然这是一个人为的例子,真正的代码在模型中有很多很多属性。我遇到的问题是如何处理模型的更新。

我创建了一个HTML表单,其中包含每个字段的相应输入元素。表单被建模并作为模板加载:

<script type="text/template" id="MyEditView-Template">  
  <input type"text" value="<%= name %>" /> <br />
  <input type"text" value="<%= age%>" /> <br />
  <input type"text" value="<%= height%>" /> <br />
  <input type"text" value="<%= description%>" /> 
</script>

并加载到视图中:

MyEditView  = Backbone.View.extend({
      tagName: 'form',
      className: 'MyEditView',

      render() {
        var values = {
          name: this.model.get('name'),
          age: this.model.get('age'),
          height: this.model.get('height'),
          description: this.model.get('description')
        }

        var myTemplate = $('#MyEditView-Template').html();
        var templateWithValues = _.template(myTemplate , values);
      }
    });

当用户保存表单时,新值将在模型(MyModel)中设置。但是我不想重新渲染整个原始视图,它需要太长时间并且有许多嵌套元素。我只想更新模型中值已更改的HTML元素。

问题是如何优雅地将模型的属性链接到HTML元素,以便我可以对已渲染的视图执行以下操作:

  1. 迭代模型的属性。
  2. 确定哪些属性已被修改。
  3. 仅更新已修改属性的UI。
  4. 隐藏任何以前不再显示的呈现属性的UI。
  5. 我有一个相当丑陋的JavaScript查找表(只是一个对象)的解决方案,它将属性名称映射到HTML元素字符串:

    var AttributesMap = {
        name: {
            htmlRef: 'li.name',
            attributeName: 'name'
        },
        age: {
            htmlRef: 'li.age',
            attributeName: 'age'
        }
        ...
    }
    

    这种感觉很糟糕,导致了一些非常臃肿的代码。

3 个答案:

答案 0 :(得分:6)

实际上,您的帖子中隐藏着两个问题。您对模型的属性有疑问,并且您不知道如何订阅模型更改事件。幸运的是,使用backbone.js可以轻松实现这两个目标。将您的观看代码更改为以下

1

render: function () {
    var model = this.model;
    $(this.el).empty().html(_.template(this.template, this.model.toJSON()))
    return this;
}

其中el是定义容器的视图的属性。 toJSON()是一种可以在模型上调用的方法,用于序列化可以通过网络传输的格式。

2

视图应该在其initialize函数中订阅模型更改事件,或者更恰当地使用delegated event支持。当模型属性发生更改时,会在该模型上调用change事件,您可以订阅here以及下面的示例。

window.ListView = Backbone.View.extend({
    initialize: function () {
        //pass model:your_model when creating instance
        this.model.on("change:name", this.updateName, this);
        this.model.on("change:age", this.changedAge, this);
    },
    render: function () {
        var model = this.model;
        $(this.el).empty().html(_.template(this.template, this.model.toJSON()))
        return this;
    },
    updateName: function () {
        alert("Models name has been changed");
    },
    changedAge: function () {
        alert("Models age has been changed");
    }
});

JsBin示例

http://jsbin.com/exuvum/2

答案 1 :(得分:1)

我遇到了类似的问题,我想让模板只显示有数据的字段。我从下划线模板方面解决了这个问题,因为&lt;%= undefinedKey%&gt;抛出一个例外。对我来说,The solution是将包装器对象传递给包含模型数据的模板。模型数据的包装看起来像:

this.$el.html(this.template({my_data: this.model.toJSON()}));

您的模板会检查是否存在所需的属性:

<% if(my_data.phone) { %><p><%= my_data.phone %> </p><% } %>

每次模型更改时,您都可以自动渲染整个视图。使用此方法将显示新值,删除的值将从UI中消失。

与您的要求相关的一些进一步信息:

  

迭代模型的属性。确定具有哪些属性   被修改了。

如果您想知道自上次模型的“更改”事件被触发以来哪些属性发生了变化,您可以使用Backbone Model's changedAttributes方法。

  

仅更新已修改属性的UI。隐藏任何先前的UI   渲染的属性,不再显示。

代替为每个属性更改呈现整个视图,您可以通过让每个UI字段成为离散的骨干视图来手动更新已更改属性的UI部分。所有视图都将侦听特定模型属性的更改事件的共享模型:

    this.model.on("change:phone", this.render, this);

答案 2 :(得分:0)

好吧,Backbone使用事件委托,并且在替换this.el内容之后,不需要重新订阅元素事件。但是,每次模板同步都会破坏DOM子树,这意味着您将丢失表单的状态。只需尝试订阅您的模型以进行输入/更改事件。因此用户输入输入元素并形成验证。恢复控制状态(让我们说输入[type = file])将具有挑战性和资源匮乏。

我认为最好的方法是使用基于DOM的模板引擎,该引擎仅更新必要的目标元素。例如。我的是https://github.com/dsheiko/ng-template

您可以拥有这样的模板:

<form id="heroForm" novalidate>
  <div class="form-group">
    <label for="name">Name</label>
    <input id="name" type="text" class="form-control" required >
    <div class="alert alert-danger" data-ng-if="!name.valid">
      Name is required
    </div>
  </div>
  <div class="form-group">
    <label for="power">Hero Power</label>
    <select id="power" class="form-control"  required>
      <option data-ng-for="let p of powers" data-ng-text="p" >Nothing here</option>
    </select>
    <div class="alert alert-danger" data-ng-if="!power.valid">
      Power is required
    </div>
  </div>
   <button type="submit" class="btn btn-default" data-ng-prop="'disabled', !form.valid">Submit</button>
</form>

我们绑定了模型namepowerform。只要他们的状态发生变化(例如在用户输入时),模板就会做出反应。它可能隐藏/显示错误消息,或禁用/启用提交按钮。

如果它有趣 - 如何将它与Backbone捆绑在一起,这里有一本免费的在线书籍https://dsheiko.gitbooks.io/ng-backbone/