The TelerikWindow UI component of Telerik UI for Blazor makes it easy for you to implement an intuitive UI design pattern, allowing the user to select an item from a grid at the top of the page in order to display (or update) data at the bottom of the page.
A common UI pattern is the List+Details page: The top of the page has a grid (or, perhaps, just a drop-down list); the bottom of the page has the detail for the item selected at the top of the page. An example of this pattern is a page with a list of customers at the top and a list of the currently selected customer's salesorders at the bottom.
The
in Telerik UI for Blazor makes this pattern easy to implement. Using the window effectively separates your page into two views (the top and the bottom), allowing you to develop the bottom portion displayed inside the window independently of the top portion. The window component allows you to hide or reveal the bottom of the page, depending on what's going on in the top part. Your biggest design decision is deciding how much UI you want to put inside the window.TelerikWindow
UI component
As an example, I'll start with a <TelerikGrid>
element at the top of my page that displays a list of customers (that list is held in a field called customers in my functions block). Inside the grid, I use the <TelerikGridCommandButton>
element to put a select button on each row and tie that button to method called DisplayDetail using the <TelerikGridEvents>
and <EventsManager>
elements. The markup to create my grid looks like this:
<TelerikGrid Data="@customers" Sortable="true">
<TelerikGridEvents>
<EventsManager OnEdit="@((args) => DisplayDetail(args))"></EventsManager>
</TelerikGridEvents>
<TelerikGridColumns>
<TelerikGridCommandColumn>
<TelerikGridCommandButton Command="Edit" Icon="edit">
Select
</TelerikGridCommandButton>
</TelerikGridCommandColumn>
<!-- ... -->
</TelerikGridColumns>
</TelerikGrid>
Following the close tag for the <TelerikGridCommandColumn>
element, I put the markup that defines the few columns of key information that allow the user to identify the customer they want to see details for:
<TelerikGridColumn Field="Id" Title="Customer Id" />
<TelerikGridColumn Field="LastName" Title="Last Name" />
The next step is to load the grid with customer data when the page first displays (I'm retrieving the data from a service endpoint in this code):
@functions {
private IEnumerable<Customer> customers = null;
protected override Task OnInitAsync()
{
HttpClient hc = new HttpClient();
HttpResponseMessage rm = await hc.GetAsync("https://localhost:5001/customers");
customers = await rm.Content.ReadAsAsync<IEnumerable<Customer>>();
StateHasChanged();
return base.OnInitAsync();
}
}
With the top (List) part of the pattern in place, I can use the TelerikWindow
to implement the bottom (detail) part.
Initially (and until the user selects a customer in the grid), the window that displays the details is unavailable, so I've started by setting the TelerikWindow
's Visible
attribute to false:
<TelerikWindow Visible="false">
<!-- ... -->
</TelerikWindow>
You can add additional attributes to the TelerikWindow
element to control the window's location and size. The Size
attribute, for example, will accept a value from the Size enumeration to use one of three standard sizes (Large
, Medium
, and Small
). Alternatively, you can create a custom size by adding the Height
and Width
attributes to the element. In the absence of any of those attributes, the window is automatically sized to be just big enough to hold its content.
You can also set the TelerikWindow
's Centered
attribute to true to have the window centered in the browser window (not within the window's container). For custom positioning, the TelerikWindow
's Top
and Left
attributes allow you to put the window wherever you want (within the browser's window, of course). Without the Centered
, Top
, and Left
attributes, the window will display where it would appear in the “normal flow” of the page.
The next step is, in my DisplayDetail
method, to activate the window and retrieve the data to be displayed in the window when the user selects a row. I first need to add two using statements at the top of the page to make the code work:
@using Telerik.Blazor
@using Telerik.Blazor.Components.Window
My DisplayDetail
method, called from the grid, will automatically be passed a GridCommandEventArgs
parameter that has two key properties: Item
and IsCancelled
. The Item
property holds a reference to the Customer
object displayed in the selected row; the IsCancelled
property allows me to suppress the default behavior for the button (which is to switch the row out of display mode and into edit mode).
In the DisplayDetail
method, I just want to move the selected Customer
object into a field or property so that I can bind UI elements inside the window to properties on the Customer
object. I also need to set the IsCancelled
property to true to keep the row from being put into edit mode. That code looks like this:
@functions {
private Customer currentCustomer;
public void DisplayDetail(GridCommandEventArgs e)
{
currentCustomer = (Customer) e.Item;
e.IsCancelled = true;
// ...
}
}
Of course, rather than display more information from the Customer
object, I could retrieve other, related information – the sales orders for the customer or a list of shipments, for example.
Now that there's data to display, I need to make the window visible. The first step in that process is to tweak the TelerikWindow
's Visible
attribute so that it's bound to a field or property in my functions block. The enhanced markup looks like this:
<TelerikWindow Visible="@windowVisible">
<!-- ... -->
</TelerikWindow>
Inside my page's functions block, I need to define the windowsVisible
field:
@functions {
private bool windowVisible;
private Customer currentCustomer;
// ...
}
Now, to make the window visible, I just need to set that windowVisable
field to true and call Blazor's StateHasChanged
method to let Blazor know that the page needs to be updated. I'll do that at the end of my DisplayDetail
method:
public void DisplayDetail(GridCommandEventArgs e)
{
currentCustomer = (Customer) e.Item;
e.IsCancelled = true;
windowVisible = true;
StateHasChange();
}
With the top and bottom of my page defined (and their interaction), all that's left is to add content to the window. I add that content inside <TelerikWindowContent>
elements inside the <TelerikWindow>
element.
The following markup adds a textbox element that, using Blazor's bind
attribute, implements two-way databinding between the textbox and the FirstName
property on my currentCustomer
object (i.e. changes to the property change the value in the textbox and changes to the value in the textbox automatically update the property). The content also includes a button that the user can click to save the changes made to that customer object through the textbox:
<TelerikWindow Visible="@windowVisible">
<TelerikWindowContent>
First Name: <input type="text" bind="@currentCustomer.FirstName" />
<!-- more input elements to display/update Customer properties -->
<input type="button" onclick="SaveCustomer" value="Save Changes" />
</TelerikWindowContent>
</TelerikWindow>
I could also have added the <TelerikWindowTitle>
element inside the <TelerikWindow>
element to define a header for my window.
In the SaveChanges
method called from my button, I need to do a few things. First, of course, I need to send the updated currentCustomer
to the service endpoint I retrieved it from in order to update the data at the service. After that, I set the field that controls my window's visibility to false and set the currentCustomer
field to null
. The grid will take care of displaying the changed data so I don't need to call StateHasChanged
to update the display with the changed data:
public async void SaveCustomer()
{
HttpClient hc = new HttpClient();
HttpResponseMessage rm = await hc.PutAsJsonAsync("https://localhost:5001/customers",
currentCustomer);
currentCustomer = null;
windowVisible = false;
}
From this point on, it's just a matter of how complicated I want the bottom part of my page to be. If I want to have multiple, related views of detail data, I might add the TelerikTab control inside of my window to organize those views. If different kinds of data in my grid required different views, I might add multiple windows to my page and choose which one to make visible in my DisplayDetails
method.
The key takeway is that, with this structure in place, extending this UI pattern just depends on what content you want to put in your windows and how many windows you want to include in your page.
The full download of this project is available here.
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.