Search code examples
extjsgridcursorcelledit

ExtJS 4 grid - How to paste text at the cursor position in a cell?


I have a grid panel using the cell editing plugin. Next to this grid there is a collection of links representing available placeholders which can be inserted into the cell.

Goal: When a user edits a cell and clicks on one of the links, the corresponding placeholder should be inserted at the position of the users cursor.

What I found out so far:

  • While editing, then I click on a link, the input element in the cell loses focus and editing is stopped. It seems like the input element is removed from the dom.
  • To get the cursor position, I need to get hold of the input element (link). There are 4 events on a CellEditing plugin: beforeedit, canceledit, edit and validateedit. None of them is fired while the input element is displayed.
  • There is a method Ext.grid.plugin.CellEditingView.startEditByPosition but I think this selects only the cell which should be edited.

Any help, please?

Edit

I'm stuck on implementing the following callback.

// var placeholder =
// ...
listeners: {
    itemclick: function(view, link) {
        console.debug('selected placeholder: ', link.data.placeholder)
        console.debug(exampleGrid);
        // Todo: insert placeholder into the grid cell at the cursor position
    }
}

Edit 2

Ok thanks to @Chris I got it working. Here is the code on JsFiddle

The solution is to use a CustomEditor (a Picker to be precise) which overlays the normal TextField:

Ext.define('CustomEditorField', {
    extend: 'Ext.form.field.Picker',
    alias: 'widget.customeditorfield',
    // ...
    createPicker: function() {
        var me = this,
            format = Ext.String.format;
        return Ext.create('Ext.form.Panel', {
            // ...
            items: [
                {
                    xtype: 'textfield',
                    name: 'description'
                }, Ext.create('Ext.view.View', {
                    listeners: {
                        itemclick: function(view, link) {
                            var textToInsert = link.data.placeholder,
                                textInField = me.picker.getForm().getValues()['description'],
                                position = me.picker.getEl().query('input')[0].selectionStart;

                            var changedText = textInField.substring(0, position) +
                                              textToInsert  + textInField.substring(position);
                            me.picker.getForm().setValues({description: changedText});
                            return false;
                        }
                    },
                    store: {
                        fields: ['placeholder', 'text'],
                        data: [
                            {placeholder: '{param1}', text: 'Parameter 1'},
                            {placeholder: '{param2}', text: 'Parameter 2'}
                        ]
                    },
                    itemSelector: 'a',
                    tpl:  [
                        '<tpl for=".">',
                        '<a href="#" data-placeholder="{placeholder}">{text}</a><br />',
                        '</tpl>'
                    ]
                })
            ],
            listeners: {
                afterrender: function(panel, opts) {
                    panel.getForm().setValues({description: me.getValue()});
                }
            }
        });
    }

Solution

  • It looks like you could benefit from creating a custom cell editor. It would keep your link list close to your edited cell, so it would improve appearance, and it would avoid the problem you see where your links are being hidden before you can interact with them. Here's an example of an editor that creates a custom panel that you could use to host your placeholder list.

    http://existdissolve.com/2012/12/extjs-4-custom-editor-for-property-grid/

    Edit:

    To address your concern about the itemclick not firing, I recommend you tweak your view definition like so:

    // other view config props, then...
    itemSelector: 'div.item',
    tpl:  [
        '<tpl for=".">',
        '<div class="item">{text}</div>',
        '</tpl>'
    ]