Completed
Last Updated: 06 Nov 2020 08:28 by ADMIN
Release 2.19.0
Eric
Created on: 19 Feb 2020 15:30
Category: Grid
Type: Feature Request
15
InCell editing should focus the first input in an opened cell

At the moment, the focus remains on the cell. It should be in the input so the user does not have to perform an extra action (say, click with the mouse) in order to edit data.

This also applies to inserting a new row - the first cell should be focused.

3 comments
ADMIN
Marin Bratanov
Posted on: 14 Jul 2020 07:34

Hello Eric,

For that, please Follow the other request you opened: https://feedback.telerik.com/blazor/1454022-incell-editing-keyboard-navigation-to-be-closer-to-excel-more-flexibility-on-opening-cells-for-editing-and-moving-between-them-with-fewer-actions

You could try using a Template for every cell and handling the keydown/keypress event to store the current text and open another cell for editing through the grid state, although I don't imagine it will be an easy implementation.

 

Regards,
Marin Bratanov
Progress Telerik

Eric
Posted on: 13 Jul 2020 22:24

Hi Marin,

 

This seems like a good start but I don't think it's enough. How do you then move from one cell to another in edit mode? Say you edit the first cell then when you press tab it should save that value and move on to the next cell. There should be some sort of key listener, I think.

 

Can you elaborate further?

ADMIN
Marin Bratanov
Posted on: 29 Jun 2020 18:15

A workaround can be a small JS function that finds the first (or any desired) input and focuses it, that you can call from the grid events when needed - e.g., in the OnEdit, or from OnClick of the Add button, or when you alter the grid state.

    <script>
        function focusInputInEditableCell() {
            setTimeout(function () {
                // you may want to tweak this logic to look more carefully at inserted items, especially when editing
                // of if you have unediable columns at the start of the grid - then there will be more than one editable cell
                var currCell = document.querySelector(".k-grid-edit-cell");
                if (currCell) {
                    var firstInput = currCell.querySelector("input");
                    if (firstInput && firstInput.focus) {
                        firstInput.focus();
                    }
                }
            }, 100); // you may need to increase this timeout if you have a laggy server connection, but then you may have other issues too
        }
    </script>

@inject IJSRuntime _js

<TelerikButton OnClick="@EditItemFour">Put item 4 in Edit mode</TelerikButton>

<TelerikGrid @ref="@GridRef" Data=@MyData EditMode="@GridEditMode.Incell" Pageable="true" Height="500px"
             OnUpdate="@UpdateHandler" OnEdit="@EditHandler" OnDelete="@DeleteHandler" OnCreate="@CreateHandler">
    <GridToolBar>
        <GridCommandButton Command="Add" Icon="add" OnClick="@FocusFirstInput">Add Employee</GridCommandButton>
    </GridToolBar>
    <GridColumns>
        <GridColumn Field=@nameof(SampleData.ID) Title="ID" />
        <GridColumn Field=@nameof(SampleData.Name) Title="Name" />
        <GridCommandColumn>
            <GridCommandButton Command="Save" Icon="save" ShowInEdit="true">Update</GridCommandButton>
            <GridCommandButton Command="Delete" Icon="delete">Delete</GridCommandButton>
        </GridCommandColumn>
    </GridColumns>
</TelerikGrid>

@code {
    //workaround start
    async Task FocusFirstInput()
    {
        //workaround for focusing
        await _js.InvokeVoidAsync("focusInputInEditableCell");
    }
    async Task EditHandler(GridCommandEventArgs args)
    {
        SampleData item = (SampleData)args.Item;

        await FocusFirstInput();
    }

    async Task EditItemFour()
    {
        var currState = GridRef.GetState();
        // reset any current insertion and any old edited items. Not mandatory.
        currState.InsertedItem = null;

        // add item you want to edit to the state, then set it to the grid
        SampleData itemToEdit = SampleData.GetClonedInstance(MyData.Where(itm => itm.ID == 4).FirstOrDefault());
        // you can alter values here as well (not mandatory)
        //itemToEdit.Name = "Changed from code";
        currState.OriginalEditItem = itemToEdit;
        currState.EditField = "Name";
        // for InCell editing, you can use the EditField property instead
        await GridRef.SetState(currState);

        //workaround for focusing
        await FocusFirstInput();
    }
    //workaround end


    TelerikGrid<SampleData> GridRef { get; set; }

    async Task UpdateHandler(GridCommandEventArgs args)
    {
        string fieldName = args.Field;
        object newVal = args.Value; // you can cast this, if necessary, according to your model

        SampleData item = (SampleData)args.Item; // you can also use the entire model

        // perform actual data source operation here through your service

        // if the grid Data is not tied to the service, you may need to update the local view data too
        var index = MyData.FindIndex(i => i.ID == item.ID);
        if (index != -1)
        {
            MyData[index] = item;
            // this copies the entire item, consider altering only the needed field
        }
    }

    async Task CreateHandler(GridCommandEventArgs args)
    {
        SampleData item = (SampleData)args.Item;

        // perform actual data source operation here through your service

        // if the grid Data is not tied to the service, you may need to update the local view data too
        item.ID = MyData.Count + 1;
        MyData.Insert(0, item);
    }

    async Task DeleteHandler(GridCommandEventArgs args)
    {
        SampleData item = (SampleData)args.Item;

        // perform actual data source operation here through your service

        // if the grid Data is not tied to the service, you may need to update the local view data too
        MyData.Remove(item);
    }

    // in a real case, keep the models in dedicated locations, this is just an easy to copy and see example
    public class SampleData
    {
        public int ID { get; set; }
        public string Name { get; set; }

        // example of comparing stored items (from editing or selection)
        // with items from the current data source - IDs are used instead of the default references
        public override bool Equals(object obj)
        {
            if (obj is SampleData)
            {
                return this.ID == (obj as SampleData).ID;
            }
            return false;
        }


        // define constructors and a static method so we can deep clone instances
        // we use that to define the edited item - otherwise the references will point
        // to the item in the grid data sources and all changes will happen immediately on
        // the Data collection, and we don't want that - so we need a deep clone with its own reference
        // this is just one way to implement this, you can do it in a different way
        public SampleData()
        {

        }

        public SampleData(SampleData itmToClone)
        {
            this.ID = itmToClone.ID;
            this.Name = itmToClone.Name;
        }

        public static SampleData GetClonedInstance(SampleData itmToClone)
        {
            return new SampleData(itmToClone);
        }
    }

    public List<SampleData> MyData { get; set; }

    protected override void OnInitialized()
    {
        MyData = new List<SampleData>();

        for (int i = 0; i < 50; i++)
        {
            MyData.Add(new SampleData()
            {
                ID = i,
                Name = "Name " + i.ToString()
            });
        }
    }
}

Regards,
Marin Bratanov
Progress Telerik

Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
Our thoughts here at Progress are with those affected by the outbreak.