asp.net core kendo editor inputs lose doubleclick bindings after editor undo action is executed.

1 Answer 115 Views
Editor
Michel
Top achievements
Rank 1
Iron
Michel asked on 26 Dec 2022, 02:23 AM

I have an editor (classic mode) I use in an editor template like @(Html.Kendo().EditorFor(m => m.Content) for a grid to create and update HTML content coming from the database that has inputs and selects. When I first load the content to the editor, I have a processEditorInputs() JavaScript function that loops thru all the inputs and selects in the editor's iframe, adds a css class to them, and adds a dblclick event listener so they can be edited via a kendo dialog I have also in my view. Everything is working fine but I have an issue I have not been able to figure out. After the undo action is performed in the editor, either by ctrl+Z or toolbar action, all my inputs in the editor lose their doubleclick binding and therefore cannot be edited again via the dialog. I tried subscribing to the 'Execute' event of the editor to call my processEditorInputs() function again after undo is performed like below:

function onExecute(e) {
            if(e.name == 'undo'){
                processEditorInputs();
            }            
}

This seems to be only working after executing undo multiple times only and it does not seem reliable.

Here is my editor.

@(Html.Kendo()
        .EditorFor(m => m.Content)
            .StyleSheets(css => css.Add(Url.Content("~/css/site.css?3")))
            .Tools(tools => tools            
            .Clear()
            .ViewHtml()
            .CustomTemplate(ct => ct.Template("<li class='k-tool-group k-button-group' role='presentation'><button type='button' onclick='openInsertCheckbox();' role='button' class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base k-tool' title='Insert Checkbox'><i class='mdi mdi-checkbox-marked-outline'></i></button><button type='button' onclick='openInsertInput();' role='button' class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base k-tool' title='Insert Input'><i class='mdi mdi-form-textbox'></i></button><button type='button' onclick='openInsertSelect();' role='button' class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base k-tool' title='Insert Select'><i class='mdi mdi-form-dropdown'></i></button></li>"))            
            .Undo()
            .Redo()
            .Bold()
            .Italic()
            .Underline()
            .Strikethrough()
            .JustifyLeft()
            .JustifyCenter()
            .JustifyRight()
            .JustifyFull()
            .InsertUnorderedList()
            .InsertOrderedList()
            .Outdent()
            .Indent()
            .CreateLink()
            .Unlink()
            .InsertImage()
            .InsertFile()            
            .SubScript()
            .SuperScript()
            .TableEditing()            
            .Formatting()
            .CleanFormatting()
            .FontName()
            .FontSize()
            .ForeColor()
            .BackColor()
            .Pdf()
            .Print()
            .CustomTemplate(ct => ct.Template("<li class='k-tool-group k-button-group' role='presentation'><button type='button' onclick='insertPageBreak();' role='button' class='k-button k-button-md k-rounded-md k-button-solid k-button-solid-base k-tool' title='Insert Page Break'><i class='fas fa-grip-lines'></i></button>"))
            ).Events(events => events                
                .Execute("onExecute")                
            ))

My javascript functions.

$(document).ready(function () {
        $(".t-placeholder").on("dragstart", function (e) {
            e.originalEvent.dataTransfer.setData("text", $(this).html());
        });  
        processEditorInputs();
    });

function processEditorInputs() {
            let iframe = document.getElementsByClassName('k-iframe')[0];
            let inputElements = iframe.contentWindow.document.getElementsByTagName('input');
            let selectElements = iframe.contentWindow.document.getElementsByTagName('select');

            //inputs
            for (let i = 0; i < inputElements.length; i++) {
                //style                
                styleEditorField(inputElements[i]); 
                //add unique identifier
                if (!inputElements[i].id || inputElements[i].id == '') {
                    var id = generateUniqueId();
                    inputElements[i].id = `input-${id}`;
                }                
                //add double click event listener to edit according to input type (if not already bound only)
                bindEditOnDoubleClick(inputElements[i]);                
            }

            //selects
            for (let i = 0; i < selectElements.length; i++) {
                //style
                styleEditorField(selectElements[i]);
                //add unique identifier
                if (!selectElements[i].id || selectElements[i].id == '') {
                     var id = generateUniqueId();
                    selectElements[i].id = `select-${id}`;
                }               
                //add double click event listener to edit
                bindEditOnDoubleClick(selectElements[i]);
            }
        }

        function generateUniqueId(){
            return Date.now() + Math.floor(Math.random() * 1000000);
        }

        function bindEditOnDoubleClick(input){
            //debugger;
            if($(input).is('select')){
                input.addEventListener("dblclick", function () {
                    openInsertSelect(this);
                });
            }
            else{
                switch (input.type) {
                    // If the input type is text, date, time, number
                    case 'text':
                    case 'date':
                    case 'time':
                    case 'number':
                        input.addEventListener("dblclick", function () {
                            openInsertInput(this);
                        });
                        break;
                    // If the input type is checkbox
                    case 'checkbox':
                        input.addEventListener("dblclick", function () {
                            openInsertCheckbox(this);
                        });
                        break;
                }
            }            
        }

        function styleEditorField(input){
            input.classList.add('k-edt-field');           
        }

I would appreciate your assistance with this matter. Please let me know if more information is needed.

Thanks.

 

 

1 Answer, 1 is accepted

Sort by
0
Alexander
Telerik team
answered on 28 Dec 2022, 04:56 PM

Hi Michel,

Thank you for reaching out.

I noticed that there is not a Telerik UI for ASP.NET Core license associated with your account but particularly for the Telerik UI for ASP.NET AJAX suite only which limits the availability of our support services for the product. In this regard, It is recommended that you either purchase a license in order to gain access to the latest updates, fixes, features, and support regarding the Telerik UI for ASP.NET Core components. More information regarding the currently available plans can be reviewed here:

Regardless, I have thoroughly analyzed the provided resources on your end regarding the experienced sporadic behavior on your end. Generally, seems to be rather expected behavior as the Undo action will cause the existing iframe's DOM to rerender with the newly(trimmed) content. This can further be observed within the browser inspected for further verification:

To be more specific, the change of the content is not achieved through an existing Editor command, therefore the change is not tracked, thus the observed undo behavior.

In order to achieve the desired functionality, a custom command for the Editor has to be implemented. I realize there is no dedicated example of creating a custom command, however, you can refer to the below resource for an example of how to implement custom Editor Commands:

That being said, this would fall into the developer's hands to implement such functionality based on his requirements.

I would also like to mention that the Support Scope generally aims to provide explanation and guidance of built-in features and API methods, but if you need assistance with the implementation of custom functionality, such as the mentioned custom tools, we can suggest you consider using the services of our Professional Services team. Our colleagues specialize in customizing the widgets and implementing custom behavior tailored to specific client requirements.

Kind Regards,
Alexander
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

Tags
Editor
Asked by
Michel
Top achievements
Rank 1
Iron
Answers by
Alexander
Telerik team
Share this question
or