Master/Detail Components

Attribution

This tutorial is a derivative of the Angular Tour Of Heroes App and Tutorial under CC BY 4.0..

At the moment, the Heroes component displays both the list of heroes and the selected hero's details.

Keeping all features in one component as the application grows will not be maintainable. You'll want to split up large components into smaller sub-components, each focused on a specific task or workflow.

In this page, you'll take the first step in that direction by moving the hero details into a separate, reusable HeroDetail component.

The Heroes component will only present the list of heroes. The HeroDetail component will present details of a selected hero.

Make the HeroDetail component

  1. Select the Pages folder and press Ctrl+Shift+A and select Razor Component from the Add Items dialog.
  2. Name the file HeroDetail.razor
  3. Replace the existing code with the following:
    @if (hero != null)
    {
        <h2>@hero.Name.ToUpper() Details</h2>
        <div><span>Id: </span>@hero.Id</div>
        <div>
            <label>
                Name:
                <input @[email protected] />
            </label>
        </div>
    }
    
    @code{
        [Parameter]
        public Hero hero { get; set; }
    }
    
    This is essentially the same as the conditional block in the Heroes component. It refers to the hero property rather than selectedHero. The hero property in this component is declared with the public accessor and decorated with the [Parameter] attribute, thereby making the property a component parameter. That means that external code (e.g. other components) can pass instances of the Hero class to the HeroDetail component just like passing arguments to method parameters in standard C# code.
  4. Replace the conditional block in the Heroes component that displays the selected hero's details with the highlighted line below:
    <h2>My Heroes</h2>
    <ul class="heroes">
        @foreach (var hero in heroes)
        {
            <li @onclick=@(() => onSelect(hero)) class="@(hero == selectedHero ? "selected" : "")">
                <span class="badge">@hero.Id</span> @hero.Name
            </li>
        }
    </ul>
    
    <HeroDetail hero=@selectedHero></HeroDetail>
    

The two components will have a parent/child relationship. The parent Heroes component will control the child HeroDetail component by sending it a new hero to display whenever the user selects a hero from the list. There is a one way data binding from the selectedHero property of the Heroes component to the hero property of the target element, which maps to the hero property of the HeroDetail component.

Now when the user clicks a hero in the list, the selectedHero changes. When the selectedHero changes, the property binding updates hero and the HeroDetail component displays the new hero.

What changed?

As before, whenever a user clicks on a hero name, the hero detail appears below the hero list. Now the HeroDetail component is presenting those details instead of the Heroes component.

Refactoring the original Heroes component into two components yields benefits, both now and in the future:

  • You simplified the Heroes component by reducing its responsibilities.
  • You can evolve the HeroDetail component into a rich hero editor without touching the parent Heroes component.
  • You can evolve the Heroes component without touching the hero detail view.
  • You can re-use the HeroDetail component in the template of some future component.

Summary

  • You created a separate, reusable HeroDetail component.
  • You used a property binding to give the parent Heroes component control over the child HeroDetail component.
  • You used the [Parameter] attribute and a public accessor to make the hero property available for binding by the external Heroes component.

Next: Services

Last updated: 15/02/2023 09:04:22

Latest Updates

© 2023 - 2024 - Mike Brind.
All rights reserved.
Contact me at Outlook.com