This is a migrated thread and some comments may be shown as answers.

Custom editor for Grid

10 Answers 703 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Kenny
Top achievements
Rank 2
Kenny asked on 10 May 2019, 04:46 AM

I have a relation that I am trying to represent in a grid.

 

One object has a Guid that points to another object which has a textual representation of the field.

I want to display the textual value in the grid. 

The editor should display a pick list of values that display the textual values (drop down list), but puts the guid into the referencing object.

 

So to say this another way, I want a guid field in the grid to display as the related textual value.  When you edit the field, it should be a pic list with the textual values from that table.  The selected value from that list should save the related guid into the model object.

 

I imagine this is a common use case.

 

Do you have an example of something like this?

 


10 Answers, 1 is accepted

Sort by
0
Marin Bratanov
Telerik team
answered on 10 May 2019, 10:51 AM
Hi Kenny,

To do this you need to use:

  • the cell template of the column to translate the guid to a human-readable string
  • the edit template of the column to place a dropdown bound to the desired data

You can find examples of both in the documentation: https://docs.telerik.com/blazor-ui/components/grid/templates.

Here is a more specific one I made for you:

@using Telerik.Blazor.Components.Grid
 
<TelerikGrid Data=@MyData EditMode="inline" Pageable="true">
    <TelerikGridColumns>
        <TelerikGridColumn Field=@nameof(SampleData.ID) Title="ID" Editable="false" />
        <TelerikGridColumn Field=@nameof(SampleData.Name) Title="Name" />
        <TelerikGridColumn Field=@nameof(SampleData.Role) Title="Position">
            <Template Context="currEmployee">
                @(GetRoleText(currEmployee as SampleData))
            </Template>
            <EditorTemplate>
                @{
                    CurrentlyEditedEmployee = context as SampleData;
                    <select bind=@CurrentlyEditedEmployee.Role>
                            @foreach (MyDdlModel item in myDdlData)
                            {
                                <option value=@item.MyValueField>@item.MyTextField</option>
                            }
                        </select>
                }
            </EditorTemplate>
        </TelerikGridColumn>
        <TelerikGridCommandColumn>
            <TelerikGridCommandButton Command="Save" Icon="save" ShowInEdit="true">Update</TelerikGridCommandButton>
            <TelerikGridCommandButton Command="Edit" Icon="edit">Edit</TelerikGridCommandButton>
        </TelerikGridCommandColumn>
    </TelerikGridColumns>
    <TelerikGridEvents>
        <EventsManager OnUpdate="@UpdateHandler"></EventsManager>
    </TelerikGridEvents>
</TelerikGrid>
 
@functions {
    public SampleData CurrentlyEditedEmployee { get; set; }
 
    public void UpdateHandler(GridCommandEventArgs args)
    {
        SampleData item = (SampleData)args.Item;
 
        //perform actual data source operations here
        //if you have a context added through an @inject statement, you could call its SaveChanges() method
        //myContext.SaveChanges();
 
        var matchingItem = MyData.FirstOrDefault(c => c.ID == item.ID);
 
        if (matchingItem != null)
        {
            matchingItem.Name = item.Name;
            matchingItem.Role = item.Role;
        }
    }
 
    protected override void OnInit()
    {
 
        GetRoles();
        GetGridData();
    }
 
    void GetRoles()
    {
        if (myDdlData == null)
        {
            myDdlData = new List<MyDdlModel>();
 
            myDdlData = Enumerable.Range(1, 20).Select(x => new MyDdlModel
            {
                MyTextField = "role " + x,
                MyValueField = x
            }).ToList();
        }
    }
 
    void GetGridData()
    {
        MyData = new List<SampleData>();
 
        for (int i = 1; i < 50; i++)
        {
            MyData.Add(new SampleData()
            {
                ID = i,
                Name = "name " + i,
                Role = i % (myDdlData.Count - 1)
            });
        }
    }
 
    private string GetRoleText(SampleData currEmployee)
    {
        MyDdlModel currRoleModel = myDdlData.Where(x => x.MyValueField == currEmployee.Role).FirstOrDefault();
        if (currRoleModel != null)
        {
            return currRoleModel.MyTextField;
        }
        return "unknown";
    }
 
    //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; }
        public int Role { get; set; }
    }
 
    public class MyDdlModel
    {
        public int MyValueField { get; set; }
        public string MyTextField { get; set; }
    }
 
    List<MyDdlModel> myDdlData { get; set; }
 
    public List<SampleData> MyData { get; set; }
}


Regards,
Marin Bratanov
Progress Telerik UI for Blazor
0
Kenny
Top achievements
Rank 2
answered on 10 May 2019, 12:09 PM

Awesome, I should have tried combining the two template sections.  It wasn't immediately obvious that you could do that.

Thanks for all your help!  I am almost done with my application.  I will send you screen shots of my creation.  I am stealing your Dashboard css/layout scheme.

Thanks,

Kenny

0
Marin Bratanov
Telerik team
answered on 10 May 2019, 12:25 PM
Hi Kenny,

It's good to hear you are making progress :)

I hope the sample app will be helpful to you, we published it so it can be used as an example and you can freely reuse it.

I'll be looking forward to seeing what you create with the help of our components, it will be interesting to see them in the wild!


Regards,
Marin Bratanov
Progress Telerik UI for Blazor
0
Kenny
Top achievements
Rank 2
answered on 10 May 2019, 02:52 PM

I converted this over to a Guid and I am getting some strange compile time errors.  Can you see what I missed?

 


@using Telerik.Blazor.Components.Grid

using Telerik.Blazor.Components.Grid

<TelerikGrid Data=@MyData EditMode="inline" Pageable="true">
    <TelerikGridColumns>
        <TelerikGridColumn Field=@nameof(SampleData.ID) Title="ID" Editable="false" />
        <TelerikGridColumn Field=@nameof(SampleData.Name) Title="Name" />
        <TelerikGridColumn Field=@nameof(SampleData.Role) Title="Position">
            <Template Context="currEmployee">
                @(GetRoleText(currEmployee as SampleData))
            </Template>
            <EditorTemplate>
                @{
                    CurrentlyEditedEmployee = context as SampleData;
                    <select bind=@CurrentlyEditedEmployee.Role>
                        @foreach (MyDdlModel item in myDdlData)
                        {
                            <option value=@item.MyValueField>@item.MyTextField</option>
                        }
                    </select>
                }
            </EditorTemplate>
        </TelerikGridColumn>
        <TelerikGridCommandColumn>
            <TelerikGridCommandButton Command="Save" Icon="save" ShowInEdit="true">Update</TelerikGridCommandButton>
            <TelerikGridCommandButton Command="Edit" Icon="edit">Edit</TelerikGridCommandButton>
        </TelerikGridCommandColumn>
    </TelerikGridColumns>
    <TelerikGridEvents>
        <EventsManager OnUpdate="@UpdateHandler"></EventsManager>
    </TelerikGridEvents>
</TelerikGrid>

@functions {
    public SampleData CurrentlyEditedEmployee { get; set; }

    public void UpdateHandler(GridCommandEventArgs args)
    {
        SampleData item = (SampleData)args.Item;

        //perform actual data source operations here
        //if you have a context added through an @inject statement, you could call its SaveChanges() method
        //myContext.SaveChanges();

        var matchingItem = MyData.FirstOrDefault(c => c.ID == item.ID);

        if (matchingItem != null)
        {
            matchingItem.Name = item.Name;
            matchingItem.Role = item.Role;
        }
    }

    protected override void OnInit()
    {

        GetRoles();
        GetGridData();
    }

    void GetRoles()
    {
        if (myDdlData == null)
        {
            myDdlData = new List<MyDdlModel>();

            for (int i = 1; i <= 20; i++)
            {
                var model = new MyDdlModel();
                model.MyTextField = "role " + i;
                model.MyValueField = new Guid();
                myDdlData.Add(model);
            }
        }
    }

    void GetGridData()
    {
        MyData = new List<SampleData>();

        for (int i = 1; i < 50; i++)
        {
            MyData.Add(new SampleData()
            {
                ID = i,
                Name = "name " + i,
                Role = new Guid()
            });
        }
    }

    private string GetRoleText(SampleData currEmployee)
    {
        MyDdlModel currRoleModel = myDdlData.FirstOrDefault(x => x.MyValueField == currEmployee.Role);
        if (currRoleModel != null)
        {
            return currRoleModel.MyTextField;
        }
        return "unknown";
    }

    //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; }
        public Guid Role { get; set; }
    }

    public class MyDdlModel
    {
        public Guid MyValueField { get; set; }
        public string MyTextField { get; set; }
    }

    List<MyDdlModel> myDdlData { get; set; }

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

 

 

I am getting a compiler error: Cannot implicitly convert type 'string' to 'System.Guid'

 

The line of code from the razor.g.cs file is here:

builder3.AddAttribute(28, "onchange", Microsoft.AspNetCore.Components.EventCallback.Factory.CreateBinder(this, __value => CurrentlyEditedEmployee.Role = __value, CurrentlyEditedEmployee.Role));

 

0
Marin Bratanov
Telerik team
answered on 11 May 2019, 09:42 AM
Hello Kenny,

Before we continue, would it be OK with you if I moved this thread to the public forum? I believe the information and examples in it can be useful for other people as well.

It looks like the select element can't handle a Guid value, because the following will not compile either, for the same reason:

<select bind=@TestVar.Role>
    @foreach (MyDdlModel item in myDdlData)
    {
        <option value=@item.MyValueField>@item.MyTextField</option>
    }
</select>
 
@TestVar.Role
 
@functions {
    SampleData TestVar { get; set; } = new SampleData { ID = 1234, Name = "the test", Role = new Guid() };
}

 

Perhaps you can go through the string representation of the guid for the value, and populate it in the model through the onchange handler of the select (I highlighted the changes):

@using Telerik.Blazor.Components.Grid
 
<TelerikGrid Data=@MyData EditMode="inline" Pageable="true">
        <TelerikGridColumns>
            <TelerikGridColumn Field=@nameof(SampleData.ID) Title="ID" Editable="false" />
            <TelerikGridColumn Field=@nameof(SampleData.Name) Title="Name" />
            <TelerikGridColumn Field=@nameof(SampleData.Role) Title="Position">
                @*<Template Context="currEmployee">
                    @(GetRoleText(currEmployee as SampleData))
                </Template>*@
                <EditorTemplate>
                    @{
                        CurrentlyEditedEmployee = context as SampleData;
                        <select onchange="@SetGuidFromString">
                            @foreach (MyDdlModel item in myDdlData)
                            {
                                <option value=@item.MyValueField>@item.MyTextField</option>
                            }
                        </select>
                    }
                </EditorTemplate>
            </TelerikGridColumn>
            <TelerikGridCommandColumn>
                <TelerikGridCommandButton Command="Save" Icon="save" ShowInEdit="true">Update</TelerikGridCommandButton>
                <TelerikGridCommandButton Command="Edit" Icon="edit">Edit</TelerikGridCommandButton>
            </TelerikGridCommandColumn>
        </TelerikGridColumns>
        <TelerikGridEvents>
            <EventsManager OnUpdate="@UpdateHandler"></EventsManager>
        </TelerikGridEvents>
    </TelerikGrid>
 
@functions {
    public SampleData CurrentlyEditedEmployee { get; set; }
 
    void SetGuidFromString(UIChangeEventArgs e)
    {
        string selectedString = e.Value.ToString();
        Guid actualGuid = new Guid(selectedString);
        CurrentlyEditedEmployee.Role = actualGuid;
    }
 
    public void UpdateHandler(GridCommandEventArgs args)
    {
        SampleData item = (SampleData)args.Item;
 
        //perform actual data source operations here
        //if you have a context added through an @inject statement, you could call its SaveChanges() method
        //myContext.SaveChanges();
 
        var matchingItem = MyData.FirstOrDefault(c => c.ID == item.ID);
 
        if (matchingItem != null)
        {
            matchingItem.Name = item.Name;
            matchingItem.Role = item.Role;
        }
    }
 
    protected override void OnInit()
    {
        GetRoles();
        GetGridData();
    }
 
    void GetRoles()
    {
        if (myDdlData == null)
        {
            myDdlData = new List<MyDdlModel>();
 
            for (int i = 1; i <= 20; i++)
            {
                var model = new MyDdlModel();
                model.MyTextField = "role " + i;
                model.MyValueField = Guid.NewGuid().ToString();
                myDdlData.Add(model);
            }
        }
    }
 
    void GetGridData()
    {
        MyData = new List<SampleData>();
 
        for (int i = 1; i < 50; i++)
        {
            MyData.Add(new SampleData()
            {
                ID = i,
                Name = "name " + i,
                Role = new Guid()
            });
        }
    }
 
    private string GetRoleText(SampleData currEmployee)
    {
        MyDdlModel currRoleModel = myDdlData.FirstOrDefault(x => x.MyValueField == currEmployee.Role.ToString());
        if (currRoleModel != null)
        {
            return currRoleModel.MyTextField;
        }
        return "unknown";
    }
 
    //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; }
        public Guid Role { get; set; }
    }
 
    public class MyDdlModel
    {
        public string MyValueField { get; set; }
        public string MyTextField { get; set; }
    }
 
    List<MyDdlModel> myDdlData { get; set; }
 
    public List<SampleData> MyData { get; set; }
}

 


Regards,
Marin Bratanov
Progress Telerik UI for Blazor
0
Kenny
Top achievements
Rank 2
answered on 11 May 2019, 04:12 PM

Yea, you can put any of our threads in public forum.  Should I ask these kind of questions there?

 

Thanks for all your help!

0
Kenny
Top achievements
Rank 2
answered on 11 May 2019, 04:15 PM
Is this related to the bug in event handling that is mentioned in the documentation for drop down lists?
0
Kenny
Top achievements
Rank 2
answered on 11 May 2019, 08:47 PM
Nevermind that last bit.  It is obviously not related. ;-)
0
Accepted
Marin Bratanov
Telerik team
answered on 12 May 2019, 06:19 AM
Thank you, Kenny, I moved the thread to the public forums to promote visibility.

If you don't have anything that you want kept private, or anything urgent, I would suggest considering a public forum thread. This makes the information available to everyone and our discussion may help someone else down the road.

As for the guid issue - it it not, indeed, related to the event handling problems in the framework. What I can say on the matter is that we are looking into the possibility to allow guid fields to be used for the value of a Telerik dropdownlist, but it is not available there yet either.


Regards,
Marin Bratanov
Progress Telerik UI for Blazor
0
Kenny
Top achievements
Rank 2
answered on 12 May 2019, 04:06 PM

I ran into an issue with this work around when adding new items to the grid.

 

If you don't select a new item from the drop down list (you go with the default or first element), then in the call to OnCreate the Guids value will be all 0's (or Guid.Empty).  Makes sense, since we changed over from a bind to oncreate.  Since we don't pick a value, the onchange event is never called.

 

I came up with a work around.  In the OnCreate handler, check for the Guid value being empty.  If it is set it to the first value in the pick list values List like this:

            if (itemForPost.ApplicationNameId.Equals(Guid.Empty))
            {
                itemForPost.ApplicationNameId = ApplicationNames[0].Id;
            }

I know it's a bit of a kludge, but until we get native support for Guids in select this will do.

 

I have a another small improvement to the example you sent me.  This set's the selected attribute for the item that is currently selected, so it will be the default value in the drop down:

                    CurrentlyEditedEmployee = context as SampleData;
                    <select onchange="@SetGuidFromString" >
                        @foreach (MyDdlModel item in myDdlData)
                        {
                            if (item.MyValueField.Equals(CurrentlyEditedEmployee.Role.ToString()))
                            {
                                <option value=@item.MyValueField  selected="selected">@item.MyTextField</option>
                            }
                            else
                            {
                                <option value=@item.MyValueField>@item.MyTextField</option>
                            }
                        }
                    </select>

Just thought that be a useful addition.

Thanks again for all your help!

 

Tags
General Discussions
Asked by
Kenny
Top achievements
Rank 2
Answers by
Marin Bratanov
Telerik team
Kenny
Top achievements
Rank 2
Share this question
or