Tap the power of breath, mantra, mudra, and dhyana.
Online course in Advanced Ajapa Japa and Shambhavi Mudra Meditation by Bipin Joshi.


Create master detail components in Blazor Server (Project Structure)

In the previous part of  this series you were introduced to the UI and overall functioning of the master-detail Blazor Server app. You also created Team and TeamMembers tables and EF Core model. You have already created the Blazor Server project. Now it's time to kick start the component development.

To build the master detail UI you could have but all the markup and the code in a single Razor component. However, for the sake of better code organization you will divide the overall UI into a set of components. There will be a container component that will load the master and detail tables. The CRUD operations are taken care by individual Razor components. The container component is made available at URL - /masterdetail. 

The following figure shows the Razor components and auxiliary classes you will add as you progress through the example.

Observe the above figure carefully. The Pages folder contains the container Razor component MasterDetailContainer.razor. Since this component is assigned some URL it's put in the Pages folder.

The Shared folder contains two subfolders namely Teams and TeamMembers. The Teams folder contains four Razor components namely ListTeam.razor, ShowTeam.razor, InsertTeam.razor, and UpdateTeam.razor. These four components are responsible for CRUD operations on the Teams table.

The TeamMembers folder also conatins four Razor components namely ListTeamMembers.razor, ShowTeamMember.razor, InsertTeamMember.razor, and UpdateTeamMember.razor. These four components are responsible for CRUD operations on the TeamMembers table.

If you see the master detail UI, there are several buttons such as Insert, Edit, Update, Delete, Save, and Cancel. In your code you need to detect which of these buttons were clicked by the user. Depending on the button clicked your course of action changes. For example, if a user clicks on Edit button then you need to load the component that renders the edit UI whereas if a user clicks on Cancel button, you need to cancel the ingoing operation and discard the changes. To help you in this process you create DataButton enumeration.

Upon clicking on any of the data buttons mentioned above you need to handle that button's click event from the parent Razor component. You accomplish this by creating custom events and callbacks in your components. The Team related Razor components use Action based callbacks whereas TeamMember related components use EventCallbacks. You can stick to either of the techniques but I am showing both for the sake of your learning and understanding. The TeamMemberEventArgs class shown in the figure is used by the EventCallback technique (it will be clear when you develop TeamMember related components).

There is also DynamicPlaceHolder.razor component. The CRUD components discussed above are loaded dynamically depending on the user's action. Initially when no action is taken by the user (app has just started) you need to load this empty placeholder component.

Now that you are aware of the overall project structure and various pieces need by the app, let's create these pieces one-by-one.

Add a new enumeration named DataButtons (you can place it in the project's root folder) as shown below:

public enum DataButton
{
    Insert,
    Edit,
    Update,
    Delete,
    CancelReadMode,
    CancelEditMode,
    CancelInsertMode
}

The DataButton enumeration contains a set of items that indicate a button being clicked by the user. They include Insert, Edit, Update, Delete, and Cancel button from read / edit / insert mode UI respectively.

Now add Teams subfolder under Shared folder and add four Razor components namely ListTeams.razor, ShowTeam.razor, InsertTeam.razor, and UpdateTeam.razor in it.

The ListTeams component displays a list of teams in a table like this:

Since you need to fetch the teams data from the Teams table you need to inject the AppDbContext in the component (you could have created a repository or service but to keep things simple you will inject AppDbContext directly). So, write the following code at the top of the ListTeams.razor component.

@inject AppDbContext db

Then add Items and SelectedItem properties in the @code block as shown below:

@code {
    public List<Team> Items { get; set; }
    public Team SelectedItem { get; set; }
}

These properties are assigned in the OnInitialized life cycle method as shown below:

protected override void OnInitialized()
{
    Items = db.Teams.ToList();

    this.StateHasChanged();
}

The SelectedItem property is used while managing the TeamMembers and it is assigned in the click event handler of Manager Members button.

To render the Teams table you need to iterate through the Items and render the table rows as shown below:

<h3>List of Teams</h3>

<br />

<button @onclick="OnInsertClick">Insert</button>

<br /><br />

<table border="1" cellpadding="10">
    <tr>
        <th>Team ID</th>
        <th>Name</th>
        <th>Description</th>
        <th colspan="2" align="center"></th>
    </tr>
    @foreach(var item in Items)
    {
        @if(item.TeamID == SelectedItem?.TeamID)
        {
            <tr class="SelectedRow">
                 <td>@item.TeamID</td>
                 <td>@item.Name</td>
                 <td>@item.Description</td>
                 <td><button @onclick="()=>
OnManageTeamClick(item)">Manage Team</button></td>
                 <td><button @onclick="()=>
OnManageMembersClick(item)">Manage Members</button></td>
        </tr>
        }
        else
        {
            <tr>
                 <td>@item.TeamID</td>
                 <td>@item.Name</td>
                 <td>@item.Description</td>
                 <td><button @onclick="()=>
OnManageTeamClick(item)">Manage Team</button></td>
                 <td><button @onclick="()=>
OnManageMembersClick(item)">Manage Members</button></td>
        </tr>
        }
    }
</table>

As you can see, at the top there is Insert button and its click event is handled by the OnInsertClick() method. You will write this method shortly.

A foreach loop iterates through the Items and displays TeamID, Name, and Description. Two buttons Manage Team and Manage Members are also rendered. The click event of the Manage Team button is handled by the OnManageTeamClick() method. and the click event of the Manage Members is handled by the OnManageMembersClick() method. The relevant Team object is also passed to these event handlers so that the event handlers know which Team is under consideration.

Notice that the code checks the item's TeamID with the SelectedTeam's TeamID. If they match that table row is displayed with a highlight (using SelectedRow CSS class).

The ShowTeam, InsertTeam, and UpdateTeam components are loaded dynamically rather than placing them in ListTeams at the development time. This is done using <DynamicComponent> component of Blazor. The <DynamicComponent> is placed below the table by adding this markup:

<DynamicComponent 
    Type="@DynamicComponentType" 
    Parameters="@DynamicComponentParams" />

The <DynamicComponent> allows you to load a component dynamically by specifying a component type using the Type property. In this example the Type comes from DynamicComponentType property of ListTeams component. You will write this property shortly. A dynamically loaded component may need parameters for its functioning. These parameters are passed using the Parameters dictionary. In this example, you will create a DynamicComponentParams property that serves this purpose.

 The DynamicComponentType and DynamicComponentParams properties are shown below:

@code {
  public List<Team> Items { get; set; }
  public Team SelectedItem { get; set; }
  public Type DynamicComponentType { get; set; }
  public Dictionary<string, object> 
DynamicComponentParams { get; set; }
  ...
  ...
}

When you click on any of the DataButtons such as Insert, Edit, Save, Delete, or Cancel an event is raised by the dynamically loaded component that is handled by ListTeams component. To handle that event ListTeams contains a function with this signature:

public void OnDataButtonClick(DataButton button, Team item)
{
}

The first parameter is DataButton that tells you which button was clicked (Insert, Update, Cancel etc.) and the second parameter tells you which Team object is  to be used for the corresponding operation. So, add this function inside the code block of ListTeams. Keep it empty for time being. You will add code to it later.

This OnDataButtonClick() handler function is to be passed as a parameter to the dynamically loaded component. So, we create a property as follows:

public Action<DataButton,Team> DataButtonClickHandler
{ get; set; }

And you initialize it to point to OnDataButtonClick() in the OnInitialized life cycle method:

protected override void OnInitialized()
{
    Items = db.Teams.ToList();
    DataButtonClickHandler = new Action<DataButton, Team>
(OnDataButtonClick);

    this.StateHasChanged();
}

When the application runs for the first time, no DataButton is clicked and hence no dynamic component will be loaded. However, you can't keep <DynamicComponent> uninitialized. So, you will add an empty placeholder component called DynamicPlaceHolder.razor inside the Shared folder.

Then you load this empty placeholder in the OnInitialized() method.

protected override void OnInitialized()
{
    DynamicComponentType = typeof(DynamicPlaceHolder);
    Items = db.Teams.ToList();
    DataButtonClickHandler = new Action<DataButton, Team>
(OnDataButtonClick);
    this.StateHasChanged();
}

Next, you need to write the three click event handlers namely OnInsertClick(), OnManageTeamClick(), OnManageMembersClick().

The OnInsertClick() event handler is called when you click on the Insert button and is shown below:

public void OnInsertClick()
{
    DynamicComponentType = typeof(InsertTeam);
    DynamicComponentParams = new Dictionary<string, object>()
    {
        {"DataButtonClick",DataButtonClickHandler }
    };
}

Inside the OnInsertClick() event handler you want to load InsertTeam component that displays a UI for adding a new Team. Therefore, you set the DynamicComponentType property to the type of InsertTeam. Upon saving the newly added Team in the database the InsertTeam would want to notify ListTeams that the operation has been completed. To accomplish this the InsertTeam component has DataButtonClick event. So, you also need to pass the DataButtonClick handler function to the InsertTeam component using DynamicComponentParams dictionary. Recollect that you have added InsertTeam component already but it's empty as of now. You will complete it as you proceed with this example.

The OnManageTeamClick() event handler does something similar but there are a few differences as shown below:

public void OnManageTeamClick(Team item)
{
    DynamicComponentType = typeof(ShowTeam);
    DynamicComponentParams = new Dictionary<string, object>()
    {
        {"SelectedTeam",item },
        {"DataButtonClick",DataButtonClickHandler}
    };

    this.StateHasChanged();
}

As you can see, OnManageTeamClick() receives a Team object for which the button has been clicked. Inside, you set the DynamicComponentType property to the type of ShowTeam. The ShowTeam component is responsible for displaying the Team in read only table. You can then edit or delete that Team. The DynamicComponentParams dictionary contains two items - SelectedTeam and DataButtonClick. You pass SelectedTeam so that ShowTeam can display its details.

The OnManageMembersClick() is quite straightforward and simply sets the SelectedItem property as shown below:

public void OnManageMembersClick(Team item)
{
    SelectedItem = item;
    this.StateHasChanged();
}

This completes ListTeams.razor component.

In the next part of this series you will develop ShowTeam, InsertTeam, and UpdateTeam components.

That's it for now! Keep coding!!


Bipin Joshi is an independent software consultant and trainer by profession specializing in Microsoft web development technologies. Having embraced the Yoga way of life he is also a meditation teacher and spiritual guide to his students. He is a prolific author and writes regularly about software development and yoga on his websites. He is programming, meditating, writing, and teaching for over 27 years. To know more about his ASP.NET online courses go here. More details about his Ajapa Japa and Shambhavi Mudra online course are available here.

Posted On : 06 May 2022