Full row edit works, but inline edit does not, on setup with per-row Field type and local data

Full row edit works, but inline edit does not, on setup with per-row Field type and local data

payemasterpayemaster Posts: 10Questions: 2Answers: 0

Link to test case:

I'm unable to provide one due to https://datatables.net/forums/discussion/81470. Pending that, I have included hopefully useful info below.

Debugger code (debug.datatables.net):

Error messages shown:

None.

Description of problem:

I am using DataTable and the Editor, with Bootstrap 4. COnfiguration is:

DataTables 2.3.4, Editor 2.5.0, AutoFill 2.7.1, Buttons 3.2.5, Column visibility 3.2.5, HTML5 export 3.2.5, Print view 3.2.5, ColReorder 2.1.2, ColumnControl 1.1.1, DateTime 1.6.1, FixedColumns 5.0.5, FixedHeader 4.0.4, KeyTable 2.12.1, Responsive 3.0.7, RowGroup 1.6.0, RowReorder 1.5.0, Scroller 2.4.3, SearchBuilder 1.8.4, SearchPanes 2.3.5, Select 3.1.3, StateRestore 1.4.3

I have a slightly unusual setup in two regards:

  1. All the data in the DataTable instance is in a JS Array with 2 columns representing the "key"s and "value"s of a Python dict; there is no server involved. Although the documentation suggests that the ajax option is not required when local data is in use, I have one set as follows to convert the data into the form needed by the Editor:

        this.editor = new DataTable.Editor({
                table: tableSelector,
                idSrc: this.INDEX_KEY,
                fields: this.fields,
                //
                // Modify the data in the table without actually doing a server
                // round trip.
                //
                ajax: function(method, url, data, success, error) {
                    //
                    // Convert the object representation used by DataTables into the
                    // Array needed.
                    //
                    data.data = Object.values(data.data).map(row => this.fields.map(field => row[field.name]));
                    success(data);
                }.bind(this),
            });
    
  2. The first "key" column has field.type = "readonly". The second "value" column starts off as a "text" but because each row has a different type, I have the following event handler which updates the "value" column's field to give it an appropriate type when a row is clicked:

        this.dt.dtApi.on("click", "tbody td", function (e) {
                const [key, value] = dicteditor.dt.dtApi.data()[this._DT_CellIndex.row];
                //
                // Replace the value field with one which is type aware. Note
                // that we just leave the replacement in place till next time.
                //
                const field = Object.assign({}, dicteditor.fields[dicteditor.INDEX_VALUE]);
                if (/^-{0,1}\d+(\.{0,1}\d*)$/.test(value)) {
                    field.type = "text";
                    field.attr = {type: "number"};
                } else if (value === "true" || value === "false") {
                    field.type = "checkbox";
                    field.options = [{label: '', value: "true"}];
                    field.separator = '';
                    field.unselectedValue = "false";
                    ...
                } ...more of the same...
                }
                //
                // Get value from editor and push to data source.
                //
                field.getFormatter = function (val) {
                    if (this.type !== "textarea" && this.type !== "checkbox") {
                        if (this.attr?.type !== "number") {
                            val = JSON.stringify(val);
                        }
                    } else if (this.type === "checkbox") {
                        val = val === "true" ? "true" : "false";
                    }
                    return val;
                }.bind(field);
                //
                // Set value pulled from the data source on editor.
                //
                field.setFormatter = function (val) {
                    if (this.type !== "textarea" && this.type !== "checkbox") {
                        val = JSON.parse(val);
                    } else if (this.type === "checkbox") {
                        val = this.actualValue === "true" ? "true" : "false";
                    }
                    return val;
                }.bind(field);
                dicteditor.editor.clear(dicteditor.COLUMN_VALUE);
                dicteditor.editor.add(field);
                //
                // If the value column is clicked, enable inline editing.
                //
                if (this._DT_CellIndex.column === dicteditor.COLUMN_VALUE) {
                    dicteditor.editor.inline(this, dicteditor.COLUMN_VALUE, {
                        onBlur: 'submit',
                        onComplete: 'submit',
                        onReturn: 'submit',
                        submit: 'all',
                    });
            }
            return true;
      });
    

As you can see:

  1. The type detection logic is a cutdown example for the purposes of reporting the problem. But you can see how the field is updated.
  2. I use getFormatter() and setFormatter() to manage the movement of data between the Editor and the DataTable.
  3. I only enable inline() when the second column is the one that is clicked.

So, when the first column is clicked, I can use the Edit button to bring up the full row modal editor, and that works find in that I can view/change/update the data in the table as desired. However, when inline editing is invoked, the correctly typed field ("text", or "text"-with-"type=number", or "datetime", or "textarea", or "checkbox") is shown, and can be interacted with. But any attempt to save the value seems to be ignored. As you can see, I have set onBlur and friends.

Any pointers welcome.

P.S. I reviewed several seemingly related issues without success.

This question has an accepted answers - jump to answer

Answers

  • allanallan Posts: 65,339Questions: 1Answers: 10,838 Site admin

    Hi,

    Could you try removing onComplete: "submit", please? There isn't actually a submit option documented for it, and I think that it is causing some problems - certainly in a little test case I've built it was causing issues.

    What appears to be happening is that the form is still (internally) in inline mode (use display() to get the current state), and that blocks the call to inline() (effectively).

    Thanks,
    Allan

  • payemasterpayemaster Posts: 10Questions: 2Answers: 0

    Unfortunately that made no difference. Here is what display() shows before and after the call to inline():

    dicteditor.editor.display()
    false
    dicteditor.editor.display()
    'inline'
    

    What else can I try?

  • allanallan Posts: 65,339Questions: 1Answers: 10,838 Site admin

    Sorry to hear that didn't solve it outright!

    I've setup this little example based on your code and there are a couple of changes that I've made which I think are worth highlighting:

    1. I've used strings for the property names in the DataTables data store rather than integers. DataTables will read the data from the HTML into an array by default, but by using columns.data you can get it to write to an object instead. Editor does support arrays, but there seems to be something with this specific use case (probably the deleting of the field) that is causing a problem. That is something I'll look into, but in the short term, is using an object acceptable for you here?
    2. Using clear() while the form is still open and not yet submitted, then adding a new field with the same name and submitting the form is causing problems (specifically the new get formatter will be used! What I've done is to add a little check to see if there is another Editor instance displayed, and if so, then submit it and swap the fields around before displaying the edit edit.
    3. For some reason I've add to add delete editor.s.fields['value']; immediately after the clear() call. With that I get an error about a field already existing with that name (i.e. it is like the clear didn't work). This is the one part that I don't understand here, and I'm wondering if it has something to do with the obfuscation used for the site's copy of Editor. clear() basically does that same thing!
    4. I've made some other little tweaks to get the example running, such as changing the variable names, not using COLUMN_VALUE, etc.

    Hopefully this example will help - please let me know if anything needs to be explained further though!

    Allan

  • payemasterpayemaster Posts: 10Questions: 2Answers: 0

    Hi Allan,

    Thanks for the comments and sample. To answer your question, I did originally think I needed to use the integer mapping because at least one of my several uses cases needed it, but I cannot for the life of me remember why, so for now, I am happy to go with the alternative nested object syntax.

    With that, in your sample, the first "float" example row work just fine. However, I did notice that the "textarea" example row fails in the following manner:

    1. Click-to-inline-edit the "textarea" value column.
    2. Make a change to the value.
    3. Saving the value clearly cannot use a vanilla "carriagereturn" since that is used by <textarea>, and the only other option at present seems to be "blur" away. So blur away by clicking another row.

    The seems to correctly save the changed value, but no further click-to-inline-edits are possible. It is as if the original "getting stuck" issue has reoccurred.

    Now, the commit-on-blur is not really what is wanted (I added it as part of my debug attempt), so I guess that begs the question of what other keys can be used to commit a textarea. For example, could ctrl-carriagereturn or shift-carriagereturn or some other "enter" versus "carriagereturn" be used?

    Thanks, Shaheed

    P.S. Also, the checkbox example row cannot correctly save a false/unchecked value. For me, that is a second order issue because I have a hacked solution for this in my real code (which is quite a bit more complicated than what I posted). Now that hack involves not returning early from the click handler, and your example returns early as part of the detection of an conflicting in-progress edit, so that might yet come back to haunt me.

  • allanallan Posts: 65,339Questions: 1Answers: 10,838 Site admin

    I guess that begs the question of what other keys can be used to commit a textarea.

    Would a submit button work for you?

    There isn't currently a built in key combination for textarea which will trigger a submit action, but it would be possible to add an external key listener that will call submit().

    table.on('keyup', 'textarea', function (e) {
      let orig = e.originalEvent;
      
      if (e.ctrlKey && e.keyCode === 13) {
        editor.submit();
      }
    });
    

    Also, the checkbox example row cannot correctly save a false/unchecked value

    Ah, the example I posted used this.actualValue. I'm not sure what that was, I must have missed that while making this example work. Replacing that with val allows the checkbox to work as expected.

    Updated example with ctrl+enter submit and the checkbox change: https://live.datatables.net/nuvihosi/222/edit .

    Allan

  • payemasterpayemaster Posts: 10Questions: 2Answers: 0

    I'm very happy to add the ctrl-key handler for the textarea, and your checkbox fix seems to work nicely too, so I will give both a try.

    However, I'm not quite out of the woods... If I click around a bit, the sample still seems to lock up with the click-to-inline-edit not working. The most reproducible way to make this happen is:

    1. Reload the sample
    2. double-click the number 1234.

    Once it has been highlighted, nothing works. I wonder if there is some other hidden state that needs to be cleared when a row or cell loses focus?

    Apologies that this is proving so tricksy.

  • allanallan Posts: 65,339Questions: 1Answers: 10,838 Site admin

    Hi,

    No worries, we need to get this right for you!

    I wasn't able to get it to lock up by double clicking the number, but if I clicked the number, then the text cell, then the number it would lock up due to a JSON parsing error.

    I've added a try/catch to address that: https://live.datatables.net/nuvihosi/223/edit .

    Allan

  • payemasterpayemaster Posts: 10Questions: 2Answers: 0

    I'm finally out of my sick bed and back at my desk, and am trying to integrate the changes worked up in your sample - for which, thanks!

    ('m actually having a little difficulty because the need to use strings for the property names collides with our pre-existing use of "." for structural and presentational purposes).

    I'll report back in due course.

  • allanallan Posts: 65,339Questions: 1Answers: 10,838 Site admin

    A . in the name of a field, as you'll know in DataTables means that it is a JSON nested property. While the columns.data option allows for it to be escaped, it isn't something that I've tested to any great extent in the Editor stack I'm afraid.

    Allan

  • payemasterpayemaster Posts: 10Questions: 2Answers: 0

    Re use of .: that was indeed a key advantage of using 2-D data and integers. I am having some difficulty completing the move away from the 2-D data and integers...any prospect of being able to go back to integers?

  • allanallan Posts: 65,339Questions: 1Answers: 10,838 Site admin

    I've just tried updating my test case to use integers and it does indeed appear to work okay. I suspect the issues I was running into before were other ones that I've resolved along the way - I jump on that to start with and then didn't switch it back as I'm so used to using key/value stores for Editor.

    Let me know how you get on with it!

    Allan

  • payemasterpayemaster Posts: 10Questions: 2Answers: 0

    LOL. I just managed to get the non-integer approach working, but based on you feedback, I may well try to go back.

    As we know, I am pushing somewhat beyond the Editor design centre of a column having a given type, as I need the type to vary by row. With your help, I have the basics of the inline-editing is working. I think I have two remaining items to think about.

    1. I don't think the onReturn = "submit" and the onBlur = "submit" options are working. In hindsight, it seems obvious that this is probably because you implement these using event handlers on the Field instance which I am overwriting. (And thus, the
  • payemasterpayemaster Posts: 10Questions: 2Answers: 0
    edited October 30

    LOL. I just managed to get the non-integer approach working, but based on your findings, I may well revert to integers.

    As we know, my difficulties arose from pushing beyond the Editor's "type per column" design centre to "type per row", but as above, the basics of inline-editing now seem to work. I think I have two remaining items to work through.

    1. As originally reported, the onReturn = "submit" and onBlur = "submit" options are not working. In hindsight, it seems likely that these are implemented using event handlers which are discarded when I overwrite the Field. Assuming this is correct, I guess I ought to try to restore the intended behaviour...is there a way for me to do that? This is what I currently do:
      dicteditor.editor.inline(this, 'value', { onBlur: 'submit', onReturn: 'submit', submit: 'all', })
    2. I am about to try to integrate with the validation support via preSubmit and the error APIs. The need for row-awareness is clear enough for existing rows, but I suspect the real fly in my ointment will be the case of a New row where the type is not known until after the key for the row is know. My life is such fun.
  • allanallan Posts: 65,339Questions: 1Answers: 10,838 Site admin
    Answer ✓

    As originally reported, the onReturn = "submit" and onBlur = "submit" options are not working.

    They seem to be in the latest example I linked to. If I click the number input, edit the value and then press return it will save. Likewise if I click away after editing it will save. The textarea won't submit on return, although the extra event handler for ctrl+return will make it submit, and clicking away will. Is that not working for you in the example?

    I suspect the real fly in my ointment will be the case of a New row where the type is not known until after the key for the row is know.

    Ooo - that is going to be fun! You can call add() after the form is shown, so you could have some kind of two stage form - have a preSubmit check that will see if there is a value input. If there isn't, then disable the key input (i.e. make it read only) and add the new field based on the key value. Then the next time preSubmit runs it would see there is a value field and allow it to run as normal (i.e. do validation, etc).

    That would not work with inline editing - that would need the full modal.

    Allan

  • payemasterpayemaster Posts: 10Questions: 2Answers: 0

    Allan, thanks for those last remarks, the pointers about the New problem are especially helpful.

    FWIW, I ended up turning off onReturn and onBlur in favour of my own event handlers as I could not get them to work in a consistent/sane way in my setup. (I'm afraid I don't know why exactly, as I exhausted my willingness to use those wretched browser debuggers). Also, as you noted, the textarea needs the extra handling in any event.

    I also reverted to using integer data and idSrc.

    Thanks for all the kindly and timely responses: much appreciated!

  • allanallan Posts: 65,339Questions: 1Answers: 10,838 Site admin

    Pleasure. Let me know how you get on with it :)

    Allan

Sign In or Register to comment.