Create master detail components in Blazor Server (List of Team Members)
In the preceding part
of this article series you created master CRUD components namely ListTeams,
ShowTeam, InsertTeam, and UpdateTeam. Continuing your development you will
complete the detail CRUD components in this part. By the end of this part you
will be able to see master-detail behavior and CRUD operations in action.
So, let's get started with the detail components.
Recollect from Part 1
that the Shared folder contains TeamMembers folder. And the folder contains four
Razor components namely ListTeamMembers.razor, ShowTeamMember.razor,
InsertTeamMember.razor, and UpdateTeamMember.razor.
If you haven't added these empty components already, add them just like you
added Team CRUD components in the previous part of this article series.
Then open ListTeamMembers.razor component. This components is displayed just
below the ListTeams component when Manage Members button for a Team is clicked.
This is shown in the following figure.
As you can see the selected Team is highlighted in the master table and its
team members are displayed in the details table. Each detail row also has Manage
Member button to view, modify, or delete that team member. An Insert button at
the top allows you to add a new team member to the selected team.
Inject AppDbContext in the ListTeamMembers.razor like this:
@inject AppDbContext db
Then add the following code in the @code block.
[Parameter]
public Team SelectedTeam { get; set; }
public List<TeamMember> Items { get; set; }
public TeamMember SelectedItem { get; set; }
public Type DynamicComponentType { get; set; }
public Dictionary<string, object>
DynamicComponentParams { get; set; }
public EventCallback<TeamMemberEventArgs>
DataButtonClickHandler{ get; set; }
This code is quite similar to ListTeams component properties. Notice the code
marked in bold letters.
The SelectedTeam parameter property will be set by the parent component (ListTeams.razor)
and will point to the Team object selected in the master table.
The Items property is a List of TeamMember objects that are displayed in the
detail table. The SelectedItem property points to a TeamMember object that is
selected for viewing, modifying, or deleting.
The purpose of DynamicComponentType and DynamicComponentParams is similar to
the ListTeams.razor component.
Notice that ListTeamMembers component declares an EventCallback named
DataButtonClickHandler instead of declaring an Action (as in ListTeams.razor).
Although both serve the similar purpose in our example. There are a few
differences between the two. While using EventCallback you don't need to
manually call StateHasChanged(). You can read more about EventCallback
here.
Also note that EventCallback uses TeamMemberEventArgs class. The
TeamMemberEventArgs class looks like this:
public class TeamMemberEventArgs
{
public DataButton Button { get; set; }
public TeamMember Item { get; set; }
}
The Button property indicates the DataButton value for the button that was
clicked (Insert, Cancel, Update etc.). The TeamMember property points to the
TeamMember object under consideration.
Now add the OnInitialized() life cycle method as shown below:
protected override void OnInitialized()
{
DynamicComponentType = typeof(DynamicPlaceHolder);
DataButtonClickHandler = EventCallback.Factory.Create
(this, (Action<TeamMemberEventArgs>)OnDataButtonClick);
this.StateHasChanged();
}
Initially when the ListTeamMembers is displayed you don't want to load any
other components (ShowTeammember, InsertTeamMember, or UpdateTeamMember). So,
you load DynamicPlaceHolder component in the <DynalicComponent>. You will add
the <DynamicComponent> later in this article.
Notice how the code uses EventCallback.Factory.Create() method to create
EventCallback based on OnDataButtonClick() method. You will add
OnDataButtonClick() shortly.
Add an empty OnDataButtonClick() event handler method as shown below:
public void OnDataButtonClick(TeamMemberEventArgs args)
{
}
This method is currently empty. You will add code to it as and when you
develop the other detail CRUD components (ShowTeamMember, InsertTeamMember, or
UpdateTeamMember).
When the parent component (ListTeams.razor) sets the SelectedTeam parameter
you need to find all the TeamMembers for that Team. So, you need to detect when
the SelectedTeam parameter is assigned a value by the parent. This is done by
overriding the OnParametersSet() method as shown below:
protected override void OnParametersSet()
{
if (SelectedTeam != null)
{
db.Entry(SelectedTeam).Collection
(c => c.Members).Load();
Items = SelectedTeam.Members;
}
}
You use the Load() method on the Collection to load the Members navigation
property (see your EF Core entity classes developed in the first part).
The ListMembers component has Insert button at the top. Clicking on it
displays the InsertTeam component for adding a new record. Although you haven't
completed InsertTeamMember component yet, add the following event handler method
that handles the click event of the Insert button.
public void OnInsertClick()
{
DynamicComponentType = typeof(InsertTeamMember);
DynamicComponentParams = new Dictionary<string, object>()
{
{"SelectedTeam",SelectedTeam },
{"DataButtonClick",DataButtonClickHandler}
};
}
This code should look familiar to you because you did something similar in
the ListTeams component. You set the DynamicComponentType to InsertTeamMember's
type. And also fill the DynamicComponentParams dictionary. You pass the
SelectedTeam and DataButtonClick parameters to the component being dynamically
loaded.
The ListTeamMembers component has Manage Member button for each row (see
figure at the beginning on this article). The click event of the Manage Member
button is handled using OnManageMemberClick() method as shown below:
public void OnManageMemberClick(TeamMember item)
{
SelectedItem = item;
DynamicComponentType = typeof(ShowTeamMember);
DynamicComponentParams = new Dictionary<string, object>()
{
{"SelectedTeamMember",item },
{"DataButtonClick",DataButtonClickHandler}
};
this.StateHasChanged();
}
This time you load ShowTeamMember component and pass SelectedTeamMember and
DataButtonClick parameters in the DynamicComponentParams dictionary.
This completes the @code block.
Now let's add the markup needed to render the TeamMembers table.
@if (Items != null)
{
<h3>List of Team Members</h3>
<br />
<button @onclick="OnInsertClick">Insert</button>
<br /><br />
<table border="1" cellpadding="10">
<tr>
<th>Team ID</th>
<th>Member ID</th>
<th>Name</th>
<th>Email</th>
<th></th>
</tr>
@foreach (var item in Items)
{
@if (item.TeamMemberID == SelectedItem?.TeamMemberID)
{
<tr class="SelectedRow">
<td>@item.TeamID</td>
<td>@item.TeamMemberID</td>
<td>@item.Name</td>
<td>@item.Email</td>
<td><button @onclick="
()=>OnManageMemberClick(item)">Manage Member</button></td>
</tr>
}
else
{
<tr>
<td>@item.TeamID</td>
<td>@item.TeamMemberID</td>
<td>@item.Name</td>
<td>@item.Email</td>
<td><button @onclick="
()=>OnManageMemberClick(item)">Manage Member</button></td>
</tr>
}
}
</table>
}
<DynamicComponent Type="@DynamicComponentType"
Parameters="@DynamicComponentParams" />
This code first checks whether there are any TeamMembers to be displayed.
This is done by the outer @if condition.
Notice the code marked in bold letters. While rendering the table rows you
want to highlight the selected row. This is achieved using the SelectedItem
property. And a SelectedRow CSS class is added to the selected <tr> element.
There is site.css file under the wwwroot > css folder. Open it and add the
SelectedRow class like this:
.SelectedRow {
background-color: silver;
font-weight: bold;
}
The @onclick event is wired to the OnManageMemberClick() event handler method
and the current TeamMember object is passed to it.
A <Dynamiccomponent> is placed at the bottom for loading the detail CRUD
components such as ShowTeamMember, InsertTeamMember, and UpdateTeamMember.
This completes the ListTeamMembers.razor component. You can now place it in
the parent - ListTeams.razor.
To do so, open the ListTeams component and add the following markup at its
end:
<ListTeamMembers SelectedTeam="@SelectedItem">
</ListTeamMembers>
Run the application and check whether you can see the TeamMembers for a
selected Team.
In the next part of this article series you will create ShowTeamMember,
InsertTeamMember, and UpdateTeamMember components.
That's it for now! Keep coding!!