buddyp450
buddyp450

Reputation: 572

How to prevent event propagation for "Enter" key press in ag-grid-react cellEditor component?

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')} />

Upvotes: 10

Views: 15233

Answers (8)

Bohdan Tokarskyi
Bohdan Tokarskyi

Reputation: 41

found a solution to use the onKeyDown param, so basically, we prevent all the key events

cellEditorParams: {
  onKeyDown: () => undefined
}

Upvotes: 0

Nupur
Nupur

Reputation: 361

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

Upvotes: 2

cmbro
cmbro

Reputation: 46

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());
        }
    };

Upvotes: 0

Luis Palacios
Luis Palacios

Reputation: 405

@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

Upvotes: 7

buddyp450
buddyp450

Reputation: 572

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;

Upvotes: 2

Niall Crosby
Niall Crosby

Reputation: 2411

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 :(

Upvotes: 0

majoshi1
majoshi1

Reputation: 116

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
    }
}

Upvotes: 2

Amoolya S Kumar
Amoolya S Kumar

Reputation: 1468

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
}

Upvotes: 0

Related Questions