一个Backbone.js的模型的新实例为什么包含对predefined默认的附加值? [英] Why do new instances of a Backbone.js model contain additional values to the predefined defaults?

查看:79
本文介绍了一个Backbone.js的模型的新实例为什么包含对predefined默认的附加值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Backbone.js的创建与该电话号码的类别一起举行一个电话号码复合表单域。有两种观点:一种呈现化合物字段(参见PhoneFieldView),而其他的呈现含有字段(见PhoneFieldSetView)的字段集。该字段集视图还包含了动态添加新手机领域的一个按钮。

当PhoneFieldsetView初始化它从DOM(VAR window.Namecards.phone)的对象中读取值。这是一个包含(重装表单时,如果手机领域验证失败,它会重新加载$ ​​P $ pvious值,并添加错误的CSS类为例)用于填充字段在初始化设置的值对象的数组。一个例子DOM对象是:

  window.Namecards.phone = [
  {
    值:'1111111',
    输入:'工作',
    的CssClass:['表单域错误']
  },
  {
    值:'222222',
    类型:'家',
    的CssClass:['表单域错误']
  }
]

的问题是,当PhoneFieldsetView呈现每个调用

  VAR phoneField =新PhoneField();

结果实例看起来像这样:

  phoneField = {
  的CssClass:电话号码,表单字段的出错,表单域误差],
  号:111,
  selectTypeElementName:电话【类型】
  textInputElementName:电话[数字]
  类型:家
}

问题是与CssClass属性。正如你可以看到它包含了表单域误差两个字符串。事实证明,字符串表单字段误差出现在阵列的CssClass的次数等于在window.Namecards.phone对象的数目。例如,如果window.Namecards.phone包含5个项目,那么表单域错误'将在PhoneField的每个实例出现5次。

这让我为难,因为根据我的了解,当我打电话

  VAR phoneField =新PhoneField();

我应该只具有用于PhoneField模型的默认值。那么,为什么这些附加值的CssClass也被加入。

当我添加一个新的空白字段的形式同样的事情正在发生(见PhoneFieldsetView.addNewField())。当我创建了PhoneField模型的新实例,它也包含额外的表单域误差的CssClass值。

我猜这是与范围的问题,但我不能找到源。以下是完整code。

(函数($){  $(文件)。就绪(函数(){    VAR PhoneField = Backbone.Model.extend({
      默认值:{
        textInputElementName:电话[数字]',
        selectTypeElementName:电话【类型】',
        编号:'',
        输入:'工作',
        的CssClass:电话号码]
      }
    });    VAR PhoneFields = Backbone.Collection.extend({
      型号:PhoneField
    });    VAR PhoneFieldView = Backbone.View.extend({
      标签名:'格',
      事件:{
        点击a.delete电话号码':'删除'
      },
      初始化:功能(){
        _.bindAll(这一点,'渲染','删除');
      },
      渲染:功能(计数器){
        VAR inputCssClass = this.model.get('的CssClass')加入('')。
        这$ el.html('<输入ID =电话号码 - '+专柜+'类型=文本名称=+ this.model.get('textInputElementName')+'值= '+ this.model.get('编号')+'级=+ inputCssClass +'自动完成=关闭/>'+
            '<选择一个id =电话类型 - '+专柜+'NAME =+ this.model.get('selectTypeElementName')+'手机=手机型>' +
            '<期权价值=工作>工作与LT; /选项>' +
            '<期权价值=家>家庭和LT; /选项>' +
            '<期权价值=其他>其他< /选项>' +
            '< /选择>' +
            '和; NBSP;< A HREF =#类=删除电话号码>删除< / A>');
        //选择默认选项。
        这$('选择选项[值='+ this.model.get('类型')+']')ATTR('选择','选择')。
        返回此;
      },
      删除:功能(){
        //销毁与该视图关联的模型。
        this.model.destroy();
        //删除从DOM这种模式的看法。
        这$ el.remove()。
      }
    });    VAR PhoneFieldsetView = Backbone.View.extend({
      EL:$('fieldset.phone'),
      事件:{
        点击按钮#外接电话按钮':'addNewField
      },
      初始化:功能(){
        VAR自我=这一点;
        _.bindAll(在此,呈现,激活addField','addNewField','appendField','removeField');
        this.counter = 0;
        this.collection =新PhoneFields();
        //创建初始场。变量window.Namecards.phone设置
        //由服务器侧控制器。请注意,这是有约束力的前加入
        //添加事件到集合。这prevents领域被追加
        //两次;一旦在初始化过程中,一旦渲染过程中。
        如果(typeof运算window.Namecards.phone!==未定义){
          _.each(window.Namecards.phone,函数(项目,索引列表){
            self.addField(item.value,项目。形式,item.cssClass);
          });
        }
        //绑定收集事件进行查看。
        this.collection.bind(添加,this.appendField);
        this.collection.bind(删除,this.removeField);
        //渲染视图。
        this.render();
      },
      渲染:功能(){
        VAR自我=这一点;
        这$ el.append('<传奇>电话和LT; /传说>');
        这$ el.append('< D​​IV ID =电话场>< / DIV>');
        这$ el.append('<按钮式=按钮ID =添加电话键>全新< /按钮>')。
        _(this.collection.models)。每个(函数(项目){//情况下收集非空
          self.appendField(项目);
        }, 这个);
      },
      //添加含predetermined数量和类型的字段。
      激活addField:功能(数量,类型的CssClass){
        VAR phoneField =新PhoneField();
        的console.log(phoneField.attributes);
        如果(typeof运算数!==未定义){
          phoneField.set({
            号码:号码
          });
        };
        如果(typeof运算的类型!==未定义){
          phoneField.set({
            型:
          });
        }
        如果(typeof运算的CssClass =='不确定'和;!&安培; cssClass.trim()==''!){
          phoneField.get('的CssClass')推(的CssClass)。
        }
        this.collection.add(phoneField);
      },
      //添加新的空场。
      addNewField:功能(){
        VAR phoneField =新PhoneField();
        的console.log(phoneField.attributes);
        this.collection.add(phoneField);
      },
      appendField:函数(项目){
        //这似乎是什么绑定模型视图。
        //在这种情况下,它会结合PhoneField到PhoneFieldView。
        phoneFieldView =新PhoneFieldView({
          型号:项目
        });
        。这个$('#电话场')追加(phoneFieldView.render(this.counter).el);
        this.counter ++;
      },
      removeField:函数(项目){
        //创建一个新的领域,如果最后剩下领域已删除。
        //这确保了总是会有至少一个场present。
        如果(this.collection.length === 0){
          this.addField();
        }
      }
    });    //创建PhoneFieldView的实例。
    VAR phoneFieldsetView =新PhoneFieldsetView();  });})(jQuery的);


解决方案

理解问题,我可以与下面的解决方案。诀窍是一个函数分配给默认值属性,它返回模型的默认值。这样做创建阵列ccsClass为PhoneField的每个实例的一个单独的实例。

VAR PhoneField = Backbone.Model.extend({
  默认设置:功能(){
    返回{
      textInputElementName:电话[数字]',
      selectTypeElementName:电话【类型】',
      编号:'',
      输入:'工作',
      的CssClass:电话号码]
    };
  }
});

I am using Backbone.js to create compound form fields which hold a telephone number along with the category of the phone number. There are two views: one renders the compound field (see PhoneFieldView), while the other renders the fieldset containing the fields (see PhoneFieldSetView). The fieldset view also contains a button for dynamically adding new phone fields.

When PhoneFieldsetView is initialized it reads values from an object in the DOM (var window.Namecards.phone). This is an Array of objects containing values used to populate the field set on initialization (for example when reloading the form if the phone field failed validation, it would reload the previous values and add an error css class). An example DOM Object would be:

window.Namecards.phone = [ 
  {  
    value: '1111111',
    type: 'work',
    cssClass: ['form-field-error']
  }, 
  {
    value: '222222',
    type: 'home',
    cssClass: ['form-field-error']
  }
]

The problem is that when the PhoneFieldsetView is rendered each call to

var phoneField = new PhoneField(); 

results in an instance looking something like this:

phoneField = {
  cssClass: ["phone-number", "form-field-error", "form-field-error"],
  number: "111",
  selectTypeElementName: "phone[type]",
  textInputElementName: "phone[number]",
  type: "home"
}

The problem is with the cssClass property. As you can see it contains two strings for "form-field-error". Turns out that the number of times the string 'form-field-error' appears in the array cssClass is equal to the number of objects in window.Namecards.phone. For example if window.Namecards.phone contains 5 items, then 'form-field-error' will appear 5 times in each instance of PhoneField.

This puzzles me, as according to my understanding, when I call

var phoneField = new PhoneField(); 

I should only have the default values for the PhoneField model. So why are these additional values for cssClass also being added.

Same thing is happening when I add a new blank field to the form (see PhoneFieldsetView.addNewField()). When I create a new instance of the PhoneField model, it also contains additional 'form-field-error' cssClass values.

I am guessing this is an issue with scope, but I can't locate the source. Below is the full code.

(function($){

  $(document).ready(function() { 

    var PhoneField = Backbone.Model.extend({
      defaults: {
        textInputElementName: 'phone[number]',
        selectTypeElementName: 'phone[type]',
        number: '',
        type: 'work',
        cssClass: ['phone-number']
      }
    });

    var PhoneFields = Backbone.Collection.extend({
      model: PhoneField
    });

    var PhoneFieldView = Backbone.View.extend({
      tagName: 'div',
      events: {
        'click a.delete-phone-number': 'remove'
      },
      initialize: function() {
        _.bindAll(this, 'render', 'remove');
      },
      render: function(counter) {
        var inputCssClass = this.model.get('cssClass').join(' ');
        this.$el.html('<input id="phone-number-' + counter + '" type="text" name="' + this.model.get('textInputElementName') + '" value="' + this.model.get('number') + '" class="' + inputCssClass + '" autocomplete="off" />' +
            '<select  id="phone-type-' + counter + '" name="' + this.model.get('selectTypeElementName') + '" phone="phone-type">' +
            '  <option value="work">Work</option>' +
            '  <option value="home">Home</option>' +
            '  <option value="other">Other</option>' +
            '</select>' +
            '&nbsp;<a href="#" class="delete-phone-number">Delete</a>');
        // Select default option.
        this.$('select option[value="' + this.model.get('type') + '"]').attr('selected', 'selected');
        return this;
      },
      remove: function() {
        // Destroy the model associated with this view.
        this.model.destroy();
        // Remove this model's view from DOM.
        this.$el.remove();
      }
    });

    var PhoneFieldsetView = Backbone.View.extend({
      el: $('fieldset.phone'),
      events: {
        'click button#add-phone-button': 'addNewField'
      },
      initialize: function() {
        var self = this;
        _.bindAll(this, 'render', 'addField', 'addNewField', 'appendField', 'removeField');
        this.counter = 0;
        this.collection = new PhoneFields();
        // Create initial fields. The variable window.Namecards.phone is set 
        // by the server-side controller.  Note that this is added before binding 
        // the add event to the collection. This prevents the field being appended 
        // twice; once during initialization and once during rendering. 
        if (typeof window.Namecards.phone !== 'undefined') {
          _.each(window.Namecards.phone, function(item, index, list) {
            self.addField(item.value, item.type, item.cssClass);
          });
        }
        // Bind collection events to view.
        this.collection.bind('add', this.appendField);
        this.collection.bind('remove', this.removeField);
        // Render view.
        this.render();
      },
      render: function() {
        var self = this;
        this.$el.append('<legend>Phone</legend>');
        this.$el.append('<div id="phone-field"></div>');
        this.$el.append('<button type="button" id="add-phone-button">New</button>');
        _(this.collection.models).each(function(item){ // in case collection is not empty
          self.appendField(item);
        }, this);
      },
      // Add field containing predetermined number and type.
      addField: function(number, type, cssClass) {
        var phoneField = new PhoneField();
        console.log(phoneField.attributes);
        if (typeof number !== 'undefined') { 
          phoneField.set({
            number: number
          });
        };
        if (typeof type !== 'undefined') {
          phoneField.set({
            type: type
          });
        }
        if (typeof cssClass !== 'undefined' && cssClass.trim() !== '') {
          phoneField.get('cssClass').push(cssClass);
        }
        this.collection.add(phoneField);
      },
      // Add new empty field.
      addNewField: function() {
        var phoneField = new PhoneField();
        console.log(phoneField.attributes);
        this.collection.add(phoneField);
      },
      appendField: function(item) {
        // This appears to be what is binding the model to the view.  
        // In this case it would be binding PhoneField to PhoneFieldView.
        phoneFieldView = new PhoneFieldView({
          model: item
        });
        this.$('#phone-field').append(phoneFieldView.render(this.counter).el);
        this.counter++;
      },
      removeField: function(item) {
        // Create a new field if the last remaining field has been remove.  
        // This ensures that there will always be at least one field present.
        if (this.collection.length === 0) {
          this.addField();
        }
      }
    });

    // Create instance of PhoneFieldView.
    var phoneFieldsetView = new PhoneFieldsetView();

  });

})(jQuery);

解决方案

After understanding the problem, I can up with the following solution. The trick is to assign a function to the 'defaults' property, which returns the model's default values. Doing so creates a separate instance of the Array ccsClass for each instance of PhoneField.

var PhoneField = Backbone.Model.extend({
  defaults: function() { 
    return {
      textInputElementName: 'phone[number]',
      selectTypeElementName: 'phone[type]',
      number: '',
      type: 'work',
      cssClass: ['phone-number']
    };
  }
});

这篇关于一个Backbone.js的模型的新实例为什么包含对predefined默认的附加值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆