1. #1
    Sencha User Greffin's Avatar
    Join Date
    Sep 2009
    Location
    Haugesund, Norway
    Posts
    25
    Vote Rating
    0
    Greffin is on a distinguished road

      0  

    Default Ext Databinding

    Ext Databinding


    Hi.

    I was unhappy with the lack of support for databinding in Ext for regular form panels.
    In order to bind a record to datafields in a form, you need to apply listeners to each field listening to changes and storing these back into the datastore.

    I've created this small override to Ext.Panel, which should manage the datastore automagically based on values from form fields. It will populate the fields on store load, and resubmit values to store from form fields. This will work with any number of components in the form.

    It would be nice with some feedback from the comunity:
    Code:
    //function createOverrides()  {
    // Override on fieldsets
    Ext.form.FieldSet.prototype.onCheckClick = Ext.form.FieldSet.prototype.onCheckClick.createSequence(function() {
      this.onCheckFire = true;
      this.fireEvent('change', this, !this.checkbox.dom.checked, this.checkbox.dom.checked);
    });
    
    Ext.override(Ext.form.FieldSet, {
      getCheckbox : function()  {
        return this.checkbox;
      },
      
      parseBoolean : function(value){
        if (Ext.isDefined(value) && Ext.isString(value)) {
          return (value.toLowerCase() == 'true' ? true:false);
        }
        return value;
      },
    
      getValue : function() {
        return this.getCheckbox().dom.checked;
      },
      
      setValue : function(value) {
        if (!this.onCheckFire)  {
          this.getCheckbox().dom.checked = this.parseBoolean(value);
          this.onCheckClick();
          this.onCheckFire = false;
        }
      }
    })
    
    
    // Override on HtmlEditor
    Ext.form.HtmlEditor.prototype.initComponent = Ext.form.HtmlEditor.prototype.initComponent.createSequence(function() {
      this.addEvents('change');
      this.on('initialize', function(editor)  {
        if(document.all) {
          editor.iframe.attachEvent("onblur", this.onEditorBlur.createDelegate(this));
        }
        else {
          editor.iframe.contentDocument.addEventListener("blur",this.onEditorBlur.createDelegate(this), false);
        }
      })
      this.on('sync', function(editor, html)  {
        this.value = html;
      })
    });
    
    Ext.form.HtmlEditor.prototype.setValue = Ext.form.HtmlEditor.prototype.setValue.createSequence(function(value) {
      this.oldValue = value;
    });
    
    Ext.override(Ext.form.HtmlEditor, {
      oldVal : null,
      value  : null,
      onEditorBlur: function()  {
        this.fireEvent('change', this, this.oldVal, this.value);
      }
    })
    //}
    
    // Apply databinding to items in the panel.
    // This should also consider binding to child containers as well.
    Ext.Panel.prototype.initComponent = Ext.Panel.prototype.initComponent.createSequence(function() {
    //  createOverrides();
      if (this.store)  {
        this.store = Ext.StoreMgr.lookup(this.store);
        this.store.on({
          scope: this, 
          load : function(store, records, options ) {
            // Can only contain one row of data. 
            if (records.length > 0) {
              this.onBind(records[0]);
            } else  {
              this.onUnbind();
            }
          }
        });
    
        // Make sure that edits return to the datastore through fields onChange event.
        this.on({
          scope: this,
          afterRender: function() {
            this.walkChildren(this, function(f)  {
              f.on({
                scope: this,
                change: function(field) {
                  if(this.boundRecord) {
                    var val = (field instanceof Ext.form.RadioGroup ? field.getValue().inputValue : field.getValue());
                    this.boundRecord.set(field.dataIndex, val);    
                    this.updateBound();
                  }
                }
              });
            })
          }
        })
      }
    });
    
    Ext.override(Ext.Panel, {
      enableOnBind: false,
      getStore : function() {
        return this.store;
      },
      getDataboundFields : function() {
        var fields = [];
        if (this.items) {
          var j = 0;
          for (; j < this.items.getCount(); j++) {
            var item = this.items.itemAt(j);
            if (item.dataIndex)  {
              fields.push(item);
            }
            if (item.getDataboundFields)  {
              var i = 0;
              var other = item.getDataboundFields();
              for (; i < other.length; i++)  {
                fields.push(other[i]);
              }
            }
          }
        }
        return fields;
      },
      onBind:function(record) {
        if(record && (this.boundRecord !== record)) {
          this.internalUpdate = false;
          this.boundRecord = record;
          this.updateBound();
    
          if (this.enableOnBind)  {
            Ext.each(this.getDataboundFields(), function(f)  {
              f.setDisabled(false);
            }, this);
          }
        }
      },
      onUnbind:function() {
        this.internalUpdate = false;
        this.boundRecord = null;
        Ext.each(this.getDataboundFields(), function(f)  {
          if (this.enableOnBind)  {
            f.setDisabled(true);
          }
          f.setValue(null);
        }, this);
    //    this.updateBound();
      },
      walkChildren : function(comp, fn) {
        if (comp.items) {
          comp.items.each(function(f) {
            if (f instanceof Ext.form.Field ) {
              fn.call(this, f);
            }
            else if (f instanceof Ext.form.FieldSet)  {
              fn.call(this, f);
              this.walkChildren(f, fn);
            }
            else if (f instanceof Ext.Panel)  {
              this.walkChildren(f, fn);
            }
          }, this);
        }
      },
      updateBound : function()  {
        if (!this.internalUpdate) {
          this.walkChildren(this, function(f)  {
            if (f.dataIndex)  {
              this.internalUpdate = true;
              f.setValue((this.boundRecord != null ? this.boundRecord.get(f.dataIndex) : null));
              this.internalUpdate = false;
            }
          }, this);
        }
      }
    });
    The above will allow an Ext.data.Store to be bound to a Ext.Panel, and fields in the store linked to form fields in said panel. The following code will illustrate this:
    Code:
      var dataStore = new Ext.data.Store({
        proxy:       new Ext.data.HttpProxy({url: 'storeFetcher', method: 'GET'}), 
        reader:      new Ext.data.JsonReader({
          root: 'DataItem',
          id:   'ID'
        }, [{name: 'ID',                  type: 'int'}, 
            {name: 'NAME',                type: 'string'},
            {name: 'TYPE_ID',             type: 'int'},
            {name: 'SHORTNAME',           type: 'string'}]
        )
      });  
    
      var win = new Ext.Window   ({
        title:      'Databind example',
        width:      700,
        height:     500,
        layout:     'fit',
        items:      [{
          xtype:      "panel",
          border:     false,
          store:       dataStore,
          defaults: { xtype: 'textfield', anchor: '100%'},
          items: [{fieldLabel: 'Name', dataIndex: 'NAME'},
                  {fieldLabel: 'Shortname', dataIndex: 'SHORTNAME'},
                  {fieldLabel: 'Type', dataIndex: 'SITE_TYPE_ID'}]
        }
      });
    This should work on more complex layouts as well, where the root panel contains a datastore, and all types of form fields.

    Please look through, and feel free to use and extend the code as you wish.

    Thank you.

  2. #2
    Sencha User Greffin's Avatar
    Join Date
    Sep 2009
    Location
    Haugesund, Norway
    Posts
    25
    Vote Rating
    0
    Greffin is on a distinguished road

      0  

    Default


    Updated!
    Added support for FieldSet's and HtmlEditor.
    Also tested for Ext 3.1

  3. #3
    Sencha User
    Join Date
    Mar 2008
    Posts
    566
    Vote Rating
    0
    moegal is on a distinguished road

      0  

    Default


    this looks great, I will try it this weekend.

    Marty

  4. #4
    Sencha Premium Member
    Join Date
    Jun 2008
    Posts
    322
    Vote Rating
    4
    Scorpie is on a distinguished road

      0  

    Default


    This is exactly what I need! Will give this a try tonight!
    I`m from Holland!

  5. #5
    Sencha Premium Member
    Join Date
    Jun 2008
    Posts
    322
    Vote Rating
    4
    Scorpie is on a distinguished road

      0  

    Default


    Works great, except how do I bind an member thats a nested array?

    I tried array.membername and array[0] but no luck as of yet...
    I`m from Holland!

Thread Participants: 2

Turkiyenin en sevilen filmlerinin yer aldigi xnxx internet sitemiz olan ve porn sex tarzi bir site olan mobil porno izle sitemiz gercekten dillere destan bir durumda herkesin sevdigi bir site olarak tarihe gececege benziyor. Sitenin en belirgin ozelliklerinden birisi de Turkiyede gercekten kaliteli ve muntazam, duzenli porno izle siteleri olmamasidir. Bu yuzden iste. Ayrica en net goruntu kalitesine sahip adresinde yayinlanmaktadir. Mesela diğer sitelerimizden bahsedecek olursak, en iyi hd porno video arşivine sahip bir siteyiz. "The Best anal porn videos and slut anus, big asses movies set..." hd porno faketaxi