The new Telerik UI for Blazor has more controls than just the grid – and they work very well together to create rich UIs for Single Page Applications.
The TelerikGrid
in Telerik UI for Blazor is a powerful tool for displaying multiple rows of objects. However, sometimes a grid can't provide a complete interface to the data. When that happens, for the most extensible solution, you can provide the user with the ability to switch from a row to the view they want. By integrating the TelerikGrid
and the TelerikTabStrip
, you can let the user select the item they want from a grid in one tab and then switch to another tab that shows additional information.
The source code for this article is available on GitHub: TelerikBlazorUIComponents
The first step in this process is to give the user the ability to select a row in the TelerikGrid
. As of this writing, there isn't a native way to select an item in the grid (though there will be), but there are a couple of options you can use, including defining a template that includes a select button. For this post, however, I've chosen to exploit the event architecture of the TelerikGrid
.
To access the TelerikGrid
's events, first add the <TelerikGridEvents>
element to the grid's markup, like this (for more about the TelerikGrid
[Ed Note: formerly KendoGrid] see my earlier post):
<TelerikGrid Data="@customers">
<TelerikGridEvents>
<!-- ... -->
</TelerikGridEvents>
<!-- rest of grid markup... -->
</TelerikGrid>
As I said, while there is no OnSelect
event in the grid's events, there are multiple other events (including OnDelete
, OnUpdate
, OnCreate
, and OnCancel
). For example, I can convert the existing OnEdit
event into a “select row” UI event by tying it to a method in my page's functions block. I've called the method DisplayDetail
:
<TelerikGridEvents>
<EventsManager OnEdit="@((args) => DisplayDetail(args))"> />
</TelerikGridEvents>
By using a lambda expression, I've actually gone to more trouble here than I need to. I like the lambda-based syntax because it makes explicit that the grid will pass an argument to my DisplayDetail
method. However, I could have achieved the same goal with just OnEdit="DisplayDetail"
.
To give the user a tool for selecting a row, I then add a TelerikGridCommandColumn
as the first column in my <TelerikGridColumns>
element. Within that element, I can add multiple buttons to the column using the <TelerikGridCommandButton>
element – for this case, I only need one button. The Command
attribute on the button lets me tie the button to one of the events specified in the <TelerikGridEvents>
element (obviously, in this example, I'm going to use the “Edit” event). In addition, the TelerikGridCommandButton
has an Icon
attribute that lets me specify an icon to display in the button; I can also provide some text between the element's open and close tags to display as the button's caption. In this case, I stayed with the default edit icon and changed the caption text to “Select”:
<TelerikGridColumns>
<TelerikGridCommandColumn>
<TelerikGridCommandButton Command="Edit" Icon="edit">
Select
</TelerikGridCommandButton>
</TelerikGridCommandColumn>
<TelerikGridColumn Field="Id" Title="Customer Id" />
<!-- more columns -->
</TelerikGridColumns>
Now I need to write the method that will catch and process the event. I called my event DisplayDetail
and I need to have it accept the GridCommandEventArgs
object that the grid will pass to it. The skeleton for that method looks like this:
@functions
{
public void DisplayDetail(GridCommandEventArgs e)
{
// ...
}
// ...
}
The GridCommandEventArgs
object has an Item property that points to the object displayed in the row that the user selected. Since my grid is displaying Customer
objects, I can cast that Item property to a Customer
object. I'll put that Customer
object into a field to use in other parts of my UI:
private Customer currentCustomer;
public void DisplayDetail(GridCommandEventArgs e)
{
currentCustomer = (Customer) e.Item;
}
By default, the grid is also going to put the row I selected into an edit mode. To prevent that from happening, I set GridCommandEventArgs.IsCancelled
to true
, which prevents the selected row's display from changing. At this point, then, my DisplayDetail
method looks like this:
public void DisplayDetail(GridCommandEventArgs e)
{
currentCustomer = (Customer) e.Item;
e.IsCancelled = true;
}
My next step is to display the information from the row in a form rather than as a row in a grid. To handle that, I'm going to take advantage of the TelerikTabStrip
.
What I'm going to do is set up two tabs within the TelerikTabStrip
: one tab to display the grid, and one to display the data from the selected row.
Moving my grid into a tab is easy: I just wrap my grid's markup inside a <TelerikTab>
element inside a TelerikTabStrip
. I'll use the TelerikTabStrip
's TabPosition
attribute to have the tabs display across the top of my page. I'll also add a second tab to display the individual customer. With both tabs, I'll use their Title
attribute to set the text displayed in at the top of the tab:
<TelerikTabStrip ref="@tabstrip" TabPosition="TelerikTabPosition.Top">
<TelerikTab ref="@currenttab" Title="Customer List">
<TelerikGrid Data="@customers">
<!-- grid markup -->
</TelerikGrid>
</TelerikTab>
<TelerikTab Title="Current Customer">
<!-- markup to display the selected customer -->
</TelerikTab>
</TelerikTabStrip>
Initially, I want the “Current Customer” tab to be disabled and enabled only after a customer is selected. To handle enabling/disabling the tabs, I'll need to bind the tab's Disabled
attribute to some field or property I can set from code. I'll call this field currentCustomerTabDisable
:
private bool currentCustomerTabDisable = true;
Now, in my DisplayDetail
method's code, after retrieving the selected Customer
, I set the field to false
:
public void DisplayDetail(GridCommandEventArgs e)
{
Customer cust = (Customer) e.Item;
e.IsCancelled = true;
currentCustomerTabDisable = false;
StateHasChanged();
}
Of course, this won't do any good unless I also tie the Disabled
attribute on my tab to the field. That markup looks like this:
<TelerikTab Title="Full Customer"
Disabled="@currentCustomerTabDisable">
<!-- ... -->
</TelerikTab>
Having gone to all that trouble, the final step is to display the selected Customer
's information in the second tab. That's “plain old Blazor code”: I add some textboxes to the second tab and use the bind attribute to associate the textbox with my currentCustomer
field. That markup looks like this:
<TelerikTab Title="Full Customer"
Disabled="@currentCustomerTabDisable">
<input type="text" bind="@currentCustomer.FirstName" />
<input type="text" bind="@currentCustomer.LastName" />
</TelerikTab>
Because the bind attribute implements two-way databinding, changes made on the “current customer tab” are automatically reflected in the grid.
Of course, if I was a decent human being, I wouldn't just enable the “current customer tab”, I would also switch the user to that tab. I can do that by calling the SetActiveTab
method, passing a reference to the tab I want to display. However, to access the tabstrip (so that I can call its SetActiveTab
method) and the tab (so I can pass it to the SetActiveTab
method), I first need references to both. That's a three-step process.
First, I add the ref
attribute to the <TelerikTabStrip>
, tying it to a field in my function's block called I've called “tabStrip”:
<TelerikTabStrip ref="@tabStrip" TabPosition="TelerikTabPosition.Top">
<!-- ... -->
</TelerikTabStrip>
In the same way, this markup ties the tab to a field called currentCustomerTab
:
<TelerikTab ref="@currentCustomerTab"
Title="Full Customer"
Disabled="@currentCustomerTabDisable">
<!-- ... -->
</TelerikTab>
The second step is to add those fields to my functions block. A TelerikTab
implements the ITab
interface, which suggests that there may be multiple kinds of tabs in the future. To support that possibility, I declare my currentCustomerTab
as an ITab
:
@functions {
private TelerikTabStrip tabStrip;
private ITab currentCustomerTab;
// ...
}
With those references to the TelerikTabStrip
and TelerikTab
in place, I can update my DisplayDetail
method to switch the user to the second tab:
public void DisplayDetail(GridCommandEventArgs e)
{
currentCustomer = (Customer) e.Item;
e.IsCancelled = true;
currentCustomerTabDisable = false;
tabStrip.SetActiveTab(currentCustomerTab);
StateHasChanged();
}
One of the nice features of this design is that it's almost infinitely extensible: As you add more customer-related functionality to this page, you can just keep adding more tabs. And all it will cost you is some markup and about half a dozen lines of code.
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter also writes courses and teaches for Learning Tree International.