1. #31
    Sencha User ykey's Avatar
    Join Date
    Mar 2010
    Location
    USA
    Posts
    245
    Vote Rating
    27
    ykey has a spectacular aura about ykey has a spectacular aura about

      0  

    Default


    I have been experimenting with this as well. I thought I'd show what I ended up with. I only ran this through the most basic test cases.

    I added a new config option to form fields called 'bindToModel'. If you load a model using loadRecord() the formpanel then looks to the model and adds any validations it finds there if the 'bindToModel' property is set on the field.

    Ext.form.field.Base override
    Code:
           Ext.override(Ext.form.field.Base, {
                    bindToModel : false,
                    
                    getErrors: function() {
                        var errors = this.callOverridden(arguments),
                            record = this.up('form').getRecord(),                        
                              modelValidations, 
                              fieldValidations;    
                        
                          if (!this.bindToModel || !record) return errors;
                          
                          modelValidations = record.validate();
                          if (modelValidations.isValid()) {
                              return errors;
                          }
                          
                          fieldValidations = modelValidations.getByField(this.name);
                          fieldValidationsLength = fieldValidations.length;
                          
                          if (fieldValidationsLength > 0) {
                              for (var j = 0; j < fieldValidationsLength; j++) {
                                  errors.push(fieldValidations[j].message);
                              }                      
                          }
                          
                          return errors;
                    },
                    
                    onChange: function() {
                        var form = this.up('form').getForm(),
                            record = form.getRecord();
                        
                        if (this.bindToModel && record) {
                            form.updateRecord(record);
                        }
                        
                        this.callOverridden(arguments);
                    }
                });
                
                Ext.define('Workgroup', {
                    extend: 'Ext.data.Model',
                    fields: [
                        'name',
                        {name: 'startDate', type: 'date'},
                        {name: 'endDate', type: 'date'}
                    ],
                    validations: [
                        {type: 'presence', name: 'name', message: 'Name is required.'},
                        {type: 'length', name: 'name', min: 3, message: 'Names must be at least 3 characters in length.'},
                        {type: 'presence', name: 'startDate', message: 'Start Date is required.'}                    
                    ]
                });            
    
                var formPanel = Ext.create('Ext.form.Panel', {
                    frame: true,
                    title: 'Work Group',
                    width: 340,
                    bodyPadding: 5,
                    renderTo: Ext.getBody(),
                    fieldDefaults: {
                        anchor: '100%',               
                        bindToModel: true
                    },
    
                    items: [
                        {
                            xtype: 'textfield',
                            name: 'name',
                            fieldLabel: 'Name'
                        },
                        {
                            xtype: 'datefield',
                            name: 'startDate',
                            fieldLabel: 'Start Date'
                        }
                    ],
                    
                    buttons: [
                        {
                            text: 'Check',
                               handler: function() {    
                                   console.log('Record data: ', workgroup.data);
                                   console.log('Form record data: ', formPanel.getRecord().data);
                                console.log('Valid: ', formPanel.getForm().isValid());
                            }
                        }
                    ]
                });
                
                var workgroup = Ext.create('Workgroup', {
                    name: 'R12345'
                });
                
                formPanel.loadRecord(workgroup);

  2. #32
    Sencha User
    Join Date
    Nov 2010
    Posts
    13
    Vote Rating
    1
    stuchy is on a distinguished road

      0  

    Default


    this works great. But when you start uploading files, it wont

  3. #33
    Sencha User
    Join Date
    Jun 2011
    Posts
    1
    Vote Rating
    0
    shighfill is on a distinguished road

      0  

    Default BasicForm.LoadRecord uses record.data not record.raw - Why?

    BasicForm.LoadRecord uses record.data not record.raw - Why?


    I have a form that utilizes a HasMany association and find that the form initializes propperly when I alter loadRecord to use record.raw rather than record.data because record.data does not include any association elements. This seems to have changed between ExtJs4.0 and ExtJs4.0.2a. If anyone could provide advice on this I would greatly appreciate it. Additionally, if there is a reason for the change that might help my understanding, please share. Thanks.

  4. #34
    Sencha User
    Join Date
    Oct 2010
    Posts
    4
    Vote Rating
    0
    fishbone is on a distinguished road

      0  

    Lightbulb updateRecord and events after failure

    updateRecord and events after failure


    I just came up with this idea for handling forms and server-side validation:
    It would be great, if there was a form-event which is called if updateRecord fails, because the server returned an error. Then I would be able to display the form again, load the record and display form-errors returned by the server. The event I'm suggesting passes form, record and server-response to the handler.

    Can this be implemented? I don't have any ideas how to do this.

    Here's why I need this feature and why I think it would be great:
    server-side validation is always available, since it's required, whereas client-side validation will always be optional. Therefore, it would be nice if my programs "fall back" to sever-side validation automatically as soon as client-side validation isn't available.

    Thanks in advance,
    fishbone

  5. #35
    Ext JS Premium Member skullbooks's Avatar
    Join Date
    Nov 2010
    Location
    Dillenburg, Hessen, Germany
    Posts
    35
    Vote Rating
    1
    skullbooks is on a distinguished road

      0  

    Default


    well at least this is on your backend.
    i case of not validation on the backend you have to return a right error, something like
    Code:
    {"result":{"success":false,"errors":{"password":"Password wrong"},"data":null}
    and on frontend side you have to handel not just success events, you should also check if you gote an error and if so do something like
    Code:
    this.getForm().markInvalid(_result.errors);
    it's always good to work with both front and backend validation. frontend is faster for the user and can prevent annoying roundtrips for the user (send - error - send) he can see when he enters wrong values at realtime.
    anyway the backend should validate anyhow, so that nobody can submit wrong data to your backend by hacking a bit js in the console.

  6. #36
    Ext JS Premium Member
    Join Date
    Aug 2011
    Posts
    25
    Vote Rating
    1
    robert.peszek is on a distinguished road

      0  

    Default


    I am working on a project using Ext 4 MVC.

    I think it would be great if presentation of validation errors (from the server or from the model) were handled automatically by Ext.form.Basic/Panel and Ext.data.Model.
    If not, maybe this would be a room for ux… to define something like ModelPlus and FormPanelPlus?
    This would shorten the amount of project specific modifications/extensions that everybody working with MVC and Ext 4 probably has to do. I ended up overriding formPanel.loadRecord() and model.save() to accomplish this.

    Some other comments:
    I learned it the hard way that I need to set the trackResetOnLoad=true on the form.Basic. Otherwise things like isDirty() flag do not work properly.

    Almost all form.Basic methods relevant to validation on (hasInvalidField() is one example) do not work with model validation.
    I needed to query form directly to verify which fields are valid and which are not in my jasmine tests. I ended up using field.activeError property which seems not to care where the error came from. I do not know what it the best way to ask the form: are you presenting any errors to the user?

    My so far experience with Ext 4 is very positive. Sure, there were (and are) rocky moments, but most of the time it was me who done something wrong.

    Best,
    Robert

  7. #37
    Sencha Premium Member
    Join Date
    Jul 2010
    Posts
    9
    Vote Rating
    0
    Jeremy Solarz is on a distinguished road

      0  

    Default


    Hi shighfill,

    can you show an example how you defined your form (and eventual sub-forms) to load data from a model
    that has "hasMany" associations?

    I'm facing the exact problem right now and want to keep the data in the model, change the model via UI (e.g. a form) and POST it back to the server.

    Any help much appreciated

  8. #38
    Sencha User
    Join Date
    Oct 2011
    Posts
    21
    Vote Rating
    0
    OliverColeman is on a distinguished road

      0  

    Default Fix for multiple form fields for "bindToModel" solution.

    Fix for multiple form fields for "bindToModel" solution.


    The code from ykey works (thanks ykey!) great, except I found that if more than one form field was bound to the Model, and form.Basic.loadRecord was called on the form to set the initial form field values from those in the Model, then all except one form field (and the corresponding Model field) are cleared. This happens because the overridden onChange function in form.field.Base sets all the Model fields, via form.Basic.updateRecord, to the current values of the form fields whenever a form field is changed. When form.Basic.loadRecord is called and the first form field is updated and fires its onChange event, the overriden onChange function then sets the bound Model field values to the empty values of all the other form fields.

    The following modified onChange function addresses this by only updating the individual Model field that is bound to the changed form field:

    Code:
          onChange: function() {
             var me = this,
              form = me.up('form').getForm(),
              record = form.getRecord();
            
            if (me.bindToModel && record && (me.name in record.data)) {
              record.beginEdit();
              record.set(me.name, me.getValue());
              record.endEdit();
            }
            
            this.callOverridden(arguments);
          }

  9. #39
    Touch Premium Member
    Join Date
    Sep 2011
    Posts
    47
    Vote Rating
    1
    ruslan.talpa is on a distinguished road

      0  

    Default Model binding without changing the model

    Model binding without changing the model


    The examples above, in order to validate based on model rules, had to update the model.
    if that model is from a store and to that store you also have other bound views (or grids) then whenever you make a modification in the form, that change is instantly reflected in other components that "display" this particular model.
    I modified the code so that you can call .updateRecord only when you need to (for example a save button) but still have the validation work based on the model.
    In addition, i monitor the the bound model so whenever it changes i call again loadRecord to reset the field values.

    Whant do you think?
    Code:
    <!DOCTYPE html>
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Form <-> Model bind</title>
        <link rel="stylesheet" type="text/css" href="http://extjs.cachefly.net/ext-4.0.2a/resources/css/ext-all.css"/>
        <script type="text/javascript" src="http://extjs.cachefly.net/ext-4.0.2a/ext-all-debug.js"></script>
        <script>
        
        Ext.override(Ext.form.Basic, {
            loadRecord: function(record) {
                var me = this;
                me._validationRecord = record.copy();
                me.callOverridden(arguments);
                me.mon( record, 'changed', me.onRecordChanged, me );
            },
            onRecordChanged: function(){
                this.loadRecord( this.getRecord() );
            }
        });
        Ext.override(Ext.form.field.Base, {
            getErrors: function() {
                var me = this,
                    errors = me.callOverridden(arguments),
                    record = me.up('form').getForm()._validationRecord,                        
                    modelValidations, 
                    fieldValidations;    
                
                  if (!me.bindToModel || !record) return errors;
                  
                  modelValidations = record.validate();
                  if (modelValidations.isValid()) {
                      return errors;
                  }
                  
                  fieldValidations = modelValidations.getByField(me.name);
                  fieldValidationsLength = fieldValidations.length;
                  
                  if (fieldValidationsLength > 0) {
                      for (var j = 0; j < fieldValidationsLength; j++) {
                          errors.push(fieldValidations[j].message);
                      }                      
                  }
                  
                  return errors;
            },
            
            onChange: function() {
                var me = this,
                form = me.up('form').getForm(),
                record = form._validationRecord;
    
    
                if (me.bindToModel && record && (me.name in record.data)) {
                    record.beginEdit();
                    record.set(me.name, me.getValue());
                    record.endEdit();
                }
    
    
                this.callOverridden(arguments);
            }
            
        });
                    
        Ext.define('Workgroup', {
            extend: 'Ext.data.Model',
            fields: [
                'name',
                {name: 'startDate', type: 'date'},
                {name: 'endDate', type: 'date'}
            ],
            validations: [
                {type: 'presence', name: 'name', message: 'Name is required.'},
                {type: 'length', name: 'name', min: 3, message: 'Names must be at least 3 characters in length.'},
                {type: 'presence', name: 'startDate', message: 'Start Date is required.'}                    
            ],
            afterEdit: function() {
                
                var me = this;
                if(me.dirty){
                    me.fireEvent('changed', me);
                }
                this.callParent(arguments);
            } 
        });            
    
    
        Ext.onReady(function() {
            var formPanel = Ext.create('Ext.form.Panel', {
                frame: true,
                title: 'Work Group',
                width: 340,
                bodyPadding: 5,
                renderTo: Ext.getBody(),
                
                fieldDefaults: {
                    anchor: '100%',               
                    bindToModel: true
                },
                
    
    
                items: [
                    {
                        xtype: 'textfield',
                        name: 'name',
                        fieldLabel: 'Name'
                    },
                    {
                        xtype: 'datefield',
                        name: 'startDate',
                        fieldLabel: 'Start Date'
                    }
                ],
                
                buttons: [
                    {
                        text: 'Check',
                           handler: function() {    
                                console.log('Record data: ', workgroup.data);
                                console.log('Form record data: ', formPanel.getRecord().data);
                                console.log('Form validation record data: ', formPanel.getForm()._validationRecord.data);
                                console.log('Valid: ', formPanel.getForm().isValid());
                        }
                    }
                ]
            });
    
    
            var workgroup = Ext.create('Workgroup', {
                name: 'R12345'
            });
    
    
            formPanel.loadRecord(workgroup);
            workgroup.set('name', 'New Name');
        });
    
    
    </script>
    </head>
    <body></body>
    </html>

  10. #40
    Sencha User
    Join Date
    Oct 2011
    Posts
    21
    Vote Rating
    0
    OliverColeman is on a distinguished road

      0  

    Default


    I actually quite like that changes in the form are echoed in other views (a tree panel in my case)!

Similar Threads

  1. Replies: 5
    Last Post: 15 May 2012, 8:06 PM
  2. Form Binding to BeanModel
    By zathril in forum Ext GWT: Discussion
    Replies: 0
    Last Post: 28 Mar 2010, 1:08 PM
  3. Form binding / update model
    By tsegismont in forum Ext GWT: Help & Discussion (1.x)
    Replies: 0
    Last Post: 2 Apr 2009, 1:22 AM
  4. Form binding
    By exo in forum Ext 1.x: Help & Discussion
    Replies: 1
    Last Post: 16 Apr 2007, 4:20 AM

Thread Participants: 19