9

My question primarily revolves around this statement in the docs w.r.t. the react component:

cellEditor Params

onKeyDown Callback to tell grid a key was pressed - useful to pass control key events (tab, arrows etc) back to grid - however you do not need to call this as the grid is already listening for the events as they propagate. This is only required if you are preventing event propagation.

I understand that the cellEditor params exist as the props being passed to the react version of the component but I can't seem to find how to attach to onKeyDown as specified in the docs. In my constructor for my cellEditor the onKeyDown function exists and matches the onKeyDown specified in cellEditorParams inside my column definition (if it exists).

constructor(props) {
  super(props);
  // console.log(typeof props.onKeyDown == 'function') => 'true'
}

But it's never reached if it simply exists in the component

onKeyDown(event) {
  console.log('not reached');
}

It does get invoked if I put onKeyDown={this.props.onKeyDown} inside of a top level wrapping div around my input but it still doesn't catch the "Enter" press.

I tried listening to the cell containing my custom cell editor

this.props.eGridCell.addEventListener('keyup', (event) => {
  console.log(event.keyCode === 13)
})

Which does capture the enter press but it seems to unmount when enter is pressed before I can capture the final enter press inside the field? I've seen behavior where this doesn't work too so I'm very confused.

I currently have a simple cell editor MyCellEditor that I am trying to make focus and select the next cell when enter is pressed in addition to just tab. I already have the ability to extract the rowIndex and column properties I need from the rowRenderer at this.props.api.rowRenderer which I then use like:

this.props.api.rowRenderer.moveFocusToNextCell(rowIndex, column, false, false, true);

My issue is where to prevent the event propagation by default from the "Enter" press.

Below is my Cell Editor and the usage.

import React from 'react';
import _ from 'lodash';

class MyCellEditor extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: props.value,
    };
  }

  getValue() {
    return this.state.value;
  }

  isPopup() {
    return false;
  }

  isCancelBeforeStart() {
    return false;
  }

  afterGuiAttached() {
    const eInput = this.input;
    eInput.focus();
    eInput.select();
  }

  onKeyDown(event) {
    // Never invoked!
  }

  onChangeListener = (e) => {
    this.setState({ value: e.target.value });
  }

  render() {
    return (
      <input
        ref={(c) => { this.input = c; }}
        className="ag-cell-edit-input"
        type="text"
        value={this.state.value}
        onChange={this.onChangeListener} />
    );
  }
}
export default MyCellEditor;

Column definition:

columnDefs = [{
    headerName: 'CustomColumn',
    field: 'customField',
    editable: true,
    cellClass: 'grid-align ag-grid-shop-order-text',
    sortable: false,
    cellEditorFramework: MyCellEditor,
    // Do I need cellEditorParams?
    cellEditorParams: {
      // onKeyDown: (event) => console.log('does not output')
    }
  },
  ...
}

React:

<AgGridReact
  columnDefs={columnDefs}
  rowData={this.props.rows}
  enableColResize="false"
  rowSelection="single"
  enableSorting="false"
  singleClickEdit="true"
  suppressMovableColumns="true"
  rowHeight="30"
  // onKeyDown also does nothing here
  onGridReady={this.onGridReady}
  onGridResize={() => console.log('grid resized')}
  onColumnResize={() => console.log('column resized')} />
Community
  • 1
  • 1
buddyp450
  • 601
  • 1
  • 10
  • 26

7 Answers7

6

@buddyp450, had exactly same problem and created an issue under ag-grid gitgub, however found workaround few minutes later, you can change key code to 13 in my example and works perfect :)

https://github.com/ceolter/ag-grid/issues/1300

export default class PATableCellEditor extends Component {
    constructor(props) {
        super(props);
        :
    }
    :
    afterGuiAttached() {
        // get ref from React component
        let eInput = this.refs.textField;
        :
        // Add a listener to 'keydown'
        let self = this;
        eInput.addEventListener('keydown', function (event) {
            self.myOnKeyDown(event)
        });
        :
    }
    :
    // Stop propagating 'left'/'right' keys
    myOnKeyDown(event) {
        let key = event.which || event.keyCode;
        if (key === 37 ||  // left
            key === 39) {  // right
            event.stopPropagation();
        }
    }
    :

Luis

Luis Palacios
  • 386
  • 3
  • 14
  • Tested on 6.0.1 and 7.0.0, works on 6.0.1 and 7.0.0 but the navigation feels broken in 7.0.0. I think that's unrelated. I'm sticking with 6.0.1. – buddyp450 Dec 04 '16 at 16:17
2

Add listener to grid containers to capture the key-down.

onGridReady: {(event) =>  
    event.api.gridPanel.eAllCellContainers.forEach(
        function (container) {
             container.addEventListener('keydown', keyDownFunc);
    });
}
...

Define the listener.

function keyDownFunc(e) {
    var key = event.which || event.keyCode;
    if (e.keyCode == 13) { // enter = 13
       // TODO: Handle event
    }
}
user3294566
  • 116
  • 1
  • 2
2

Unfortunately none of the provided answers are actually able to prevent the propagation of the native ag-grid navigation events despite stopping event propagation, immediate propagation, and preventing the default.

Somewhere in an unknown area to me with the react ag-grid the navigation elements are hijacking everything. I know this since I added a console out to the renderedCell.ts in the source after adding the listeners as suggested in this thread and got the renderedCell's console out before the listener's console out.

I eventually went for the simply-stupid approach of forking ag-grid and tweaking the onKeyDown:

case Constants.KEY_ENTER:
  // this.onEnterKeyDown();
  // Whatever makes the clients happy amirite?
  this.onTabKeyDown();
  break;
buddyp450
  • 601
  • 1
  • 10
  • 26
2

From v13.2.0 onwards, ag-grid has a suppressKeyboardEvent callback that can be added to the column definition to customise the default keyboard navigation. You can find the documentation and an example here in the official docs

Nupur
  • 351
  • 2
  • 5
  • I've been struggling with this, for some reason stopPropagation stops my editor from doing its function properly. So I need to use suppressKeyboardEvent, but it only gets called when navigating, params.editing is never true. I have no idea why. Maybe you can help me? – William Wino May 29 '19 at 14:35
0

I found from the documentation of ag-grid that the "grid-api" is provided by the onGridRead() callback of the React component. The following api function can help you register an event for keyPress.

addEventListener(eventType, listener)

Try something like:

onGridReady = (api)=>{
    api.addEventListener('keyPress', this.keyPressEventHandlerCallback);
}

keyPressEventHandlerCallback=(e)=>{
    ...handler code here
}
Amoolya S Kumar
  • 1,320
  • 7
  • 10
  • This does not work, `grid.api.addEventListener("keydown", (ev: any) =>{ })` is not fired on a key down; and `grid.api.addGlobalListener` is not helpful for this situation. @Luis Palacios suggestion works. – Artru Jan 23 '17 at 16:03
0

the intention is you capture the 'enter' press in you cellRenderer as follows:

render() {
 return (
  <input
    ref={(c) => { this.input = c; }}
    className="ag-cell-edit-input"
    type="text"
    value={this.state.value}
    onChange={this.onChangeListener}
    onKeyDown={this.onKeyDownListener} />
);

then you would prevent the propagation from the onKeyDownListener. that's how it works in javascript. if something is happening different in React then I don't know :(

Niall Crosby
  • 2,233
  • 13
  • 16
  • I believe the enter press is being captured and redirected somewhere higher up the chain. It never hits this listener. – buddyp450 Dec 02 '16 at 21:45
0

What I wound up doing is changing a line in afterGuiAttached (example LargeTextCellEditor.prototype.afterGuiAttached) so that this.textarea.focus is called from a timeout. ( I didn't look further for the source of the problem yet, but this works for me for now )

LargeTextCellEditor.prototype.afterGuiAttached = function () {
        if (this.focusAfterAttached) {
            // this.textarea.focus();
            setTimeout(()=>this.textarea.focus());
        }
    };
cmbro
  • 46
  • 1