formOptions.onComplete = 'none' clears s.action

formOptions.onComplete = 'none' clears s.action

abrookieabrookie Posts: 11Questions: 4Answers: 0

jQuery DataTables Editor v1.5.4 - Standalone editing

The Scenario:
I have the form showing immediately when the user opens the page (no user action triggers the form to show, it is always showing). The user clicks submit and it succeeds. Before moving on to somewhere else, they notice they need to fix an incorrect value they entered so they change it and save again, but it does nothing.

The Cause:
After calling editor.submit(...) with the formOptions.onComplete = 'none' (either in the editor constructor or from the edit function) an internal variable 's.action' is set to null on line 5124. This is a problem since the .submit function checks this value before allowing a submit. So the second save (submit call) does nothing and immediately returns.

The Workaround:
Add a success handler to the submit function call that calls edit again. This is pretty annoying though because you have to call .set() for every field because they all get cleared again. In addition, you must do this in a setTimeout, I'm guessing because the internal state hasn't been fully cleared at this point.

Questions:
Whats the intended behavior/usage here? To me, it doesn't make sense to leave it open if you can't re-save. Although it would be confusing for a remove action (you can't remove twice) and likewise for a create action (are you creating two or now editing the one you created?).

As a side question, is there anyway to set the Object that the editor works with rather than setting each field? To me, it would be super useful if it just took my object and found the defined field names and used those values to set the initial state.

Thanks,
Austin

Answers

  • allanallan Posts: 61,744Questions: 1Answers: 10,111 Site admin

    Hi Austin,

    Thanks for the details - sounds like a nice setup you are using.

    The life cycle for an editor form is designed to go through the create/edit/delete init / submit loop every time - i.e. once a submit is complete, in order to take some other action you need to use the API to trigger that action.

    The ability to leave the form "open" is really just for visual end user effect - for example if you wanted to immediately trigger editing of the next row in the table, instead of visually closing and then reopening the form it could just remain open. However, it would need to be set up for the next edit (or whatever action is required).

    Hope that makes sense! I do see your point about it remaining in the edit state, but that's not currently how it is designed.

    As a side question, is there anyway to set the Object that the editor works with rather than setting each field?

    Could you just use $.map to loop over the object and produce the array that you need?

    The problem with having Editor do that by default is that it could only set the data property from the object information. Labels, field types and other options might also be possible, but it would need to be in a defined format that Editor understands - which is basically what the fields array is at the moment.

    Allan

  • abrookieabrookie Posts: 11Questions: 4Answers: 0

    So the intended use is to call edit/create/remove afterwards regardless of the onComplete formOption. Fair enough. What's the reason that I need to do a setTimeout in the submit callback before a call to edit() will work? I couldn't seem to find where it was failing (it failed silently).

    Regarding the setting of an object. My thought was something like the following (which I'm guessing is already done somewhere when its attached to a table).

    I define the Editor with all the fields and their properties.
    Then instead of calling set() for each property I call a set(object) that then scans the given object for the fields defined in the editor.

    For example:

    var model = { Name: 'DataTables is Great', Phone: '888-888-8888' };
    var editor = new $.fn.dataTable.Editor({
      ...
      fields: [
        {
           label: 'Name',
           name: 'Name',
           type: 'text'
        },
        {
           label: 'Cell',
           name: 'Phone',
           type: 'text'
        }
      ]
    });
    
    // then
    editor.edit(1, false).set(model).open();
    

    So the set function would use the defined fields in the editor and scan the given object for those names, calling the field's set method using the value. For an object with a bunch of fields, this would save a lot code and be much easier to maintain.

    Thanks for the help and this great product!

  • allanallan Posts: 61,744Questions: 1Answers: 10,111 Site admin

    Could you show me the full code please, I'm specifically interested in how the setTimeout is being used and where it is being called. I'll need to reproduce the issue locally to be able to give you a properly informed answer.

    editor.edit(1, false).set(model).open();

    You should be able to do that already. In fact, internally the set() method converts the input into an object to operate in basically the way you describe if something other than an object is passed in.

    Allan

  • abrookieabrookie Posts: 11Questions: 4Answers: 0

    You should be able to do that already. In fact, internally the set() method converts the input into an object to operate in basically the way you describe if something other than an object is passed in.

    Somehow I missed this in the docs. Sadly, there's problem with it. It iterates the properties of the object given rather that the defined fields. In my case, the object has properties that are not editable nor sent to the server so it blows up when it can't find a field for a property. See line 3245.

    Could you show me the full code please, I'm specifically interested in how the setTimeout is being used and where it is being called. I'll need to reproduce the issue locally to be able to give you a properly informed answer.

    Of course, here's a quick code sample of what I'm doing:

    var model = {
        Id: '1',
        Name: 'Jim',
        Type: 1
    }
    
    var editor = new $.fn.dataTable.Editor({
        ajax: {
            edit: {
                type: 'PUT',
                url: '/api/People/'
            }
        },
        idSrc: 'Id',
        fields: [
            {
                name: 'Id',
                type: 'hidden'
            },
            {
                label: 'Name',
                name: 'Name',
                type: 'text'
            }
        ]
    });
    
    editor
        // initiate edit mode, but don't show yet
        .edit(model.Id, false)
        // NOTE: this blows up because of the Type property
        .set(model)
        // here's the meat of the setTimeout issue
        .buttons([
            {
                label: 'Save',
                fn: function() {
                    editor.submit(function(e) {
                        // on success I want to retain the edit mode
                        
                        // this doesn't work for some reason
                        //editor
                        //  .edit(e.data[0].Id, false)
                        //  .set(e.data[0])
                        //  .open();
                        
                        // this does
                        setTimeout(function() {
                            editor
                                .edit(e.data[0].Id, false)
                                .set(e.data[0])
                                .open();
                        }, 0);
                    }, null, null, false);
                }
            }
        ])
        // finally show it
        .open();
    
  • abrookieabrookie Posts: 11Questions: 4Answers: 0

    Sadly, there's problem with it. It iterates the properties of the object given rather that the defined fields. In my case, the object has properties that are not editable nor sent to the server so it blows up when it can't find a field for a property. See line 3245.

    Another problem with this particular line is that you can define a field with a name that is a sub property of a given object. For example:

    var model = { 
        Name: 'Jim', 
        Phone: '888-888-8888',
        Brother: {
            Name: 'Bob',
            Phone: '999-999-9999'
        }
    };
    
    var editor = new $.fn.dataTable.Editor({
      ...
      fields: [
        {
           label: 'Name',
           name: 'Name',
           type: 'text'
        },
        {
           label: 'Brother',
           name: 'Brother.Name',
           type: 'text'
        }
      ]
    });
    
    editor.set(model);
    

    I don't think the set() function will work in this case either right?

  • allanallan Posts: 61,744Questions: 1Answers: 10,111 Site admin

    Thanks for the code - I don't actually see where the onComplete option is being set, but I presume that's just been stripped out in the code reduction.

    I've just tried the following and it appears to work - the selected row remains editable and the form display is retained:

        $('div.trigger').on( 'click', function () {
            editor
                .edit( table.rows( { selected: true } ).indexes(), false, { onComplete: 'none' } )
                .buttons( {
                    "label": "Submit",
                    "fn": function () {
                        editor.submit( function () {
                            editor.edit( editor.modifier() );
                        } );
                    }
                } )
                .open();
        } );
    

    It does get a little confused due to the second editor.edit() call without the form options and I'm not sure that is correct (it currently remains open - I would expect it to do the default action and close at that point). I will look into this.

    Regarding the object setting additional parameters and the nested values - when I saw your message about it throwing errors on unknown values I took a look at the code and realised your second point as well. I think the fix for the second will likely resolve the first as well. I'll post back when done.

    Allan

  • abrookieabrookie Posts: 11Questions: 4Answers: 0
    edited January 2016

    Yeah, I left it off on the sample. Right, the form stays open and the fields are all editable, this behavior is working fine. The problem was that I couldn't click the save button again without another call to edit, which clears the fields. I'm assuming I have to do the whole shebang to get it to allow saving again (ie. editor.edit(...).set(..).open(...))? I see you are using it with a table, would this have any behavior differences vs. standalone?

    I will try again without the setTimeout and let you know.

    Really appreciate the quick responses.

  • allanallan Posts: 61,744Questions: 1Answers: 10,111 Site admin

    Its a interesting issue - thanks for bringing it up and the discussion :-)

    I was able to submit the form from the second edit() call above and the changes were saved (Ajax request made) but the form stayed option and it shouldn't have done that in that case since the default form options are to close the form.

    I can't think of any immediate reason why standalone would make any difference. The submitSuccess and submitComplete events occur after the submit callback, so if something were listening for them and taking an action, that might cause an issue.

    Allan

  • abrookieabrookie Posts: 11Questions: 4Answers: 0
    edited January 2016

    Ok. I think I've figured out whats happening. It's because I'm using standalone editing without defining HTML elements.

    Let me explain by using this code sample:

    // goal: within the submit() function's success callback
    // we want to re-init edit mode
    editor
        // we call edit
        .edit(e.data[0].Id], false)
        // now, regardless of what I give the edit() function it
        // calls _tidy() which checks the value of this.s.processing,
        // which is true during the callback.  It then places an event 
        // listener to fire after the submitComplete event (line 5168)
        // and immediately exits the edit() function.
        
        // I set all my field values, which works
        .set(e.data[0])
        
        // I call open it which works
        .open();
        
    // now, after the callback finishes, the event listener
    // placed on submitComplete gets called, which calls
    // edit() again -- clearing the fields I set earlier
    
    // it clears the fields because it uses __dataSources.html.fields
    // function to set them based on HTML elements with data-editor-field
    // attribute.  In my case these don't exist because I'm setting the
    // data via the set() method.
    

    This explains why wrapping the code above in a setTimeout works.

    Here are some thoughts of mine, but I'll defer to you:

    1. Add a flag for the HTML element stuff (on/off) that toggles whether the setting/getting of values from those elements is performed.

    2. Modify the __dataSources.html.fields function to do nothing when it doesn't find an HTML element in the dom.

    3. Add a new __dataSources.object type that uses an object as the datasource, maybe specified by the set(object) function, a new function, or at editor constructor time.

  • allanallan Posts: 61,744Questions: 1Answers: 10,111 Site admin

    Thanks for the additional information - yes, using submitComplete would explain the behaviour you are seeing.

    I think what needs to be done here is a general improvement of how Editor operates in standalone mode without the HTML elements on the page - possibly 1 and 2 of your suggestions above together, or possibly 1 and 3. Its a feature that I've been slowly cooking ideas for as I want to improve how it operates with the Ajax aspect as well.

    For the moment I think the setTimeout is probably the best way to go, but for Editor 1.6 I hope to have a better approach available.

    Regards,
    Allan

  • abrookieabrookie Posts: 11Questions: 4Answers: 0

    For the moment I think the setTimeout is probably the best way to go, but for Editor 1.6 I hope to have a better approach available.

    No problem, and thanks!

    The standalone editor is really nice since you can reuse a lot of code (field types in particular) for just normal forms. This coupled with custom display controllers really gives a bunch of flexibility. Also the fact that a normal form behaves and is written nearly identical to a editable datatable really helps with maintenance especially on the server side, since we can expect the same format of edit/create/remove input and output.

This discussion has been closed.