Blazor Walkthrough: Act IV - rochellew/csci_1260_spring2025 GitHub Wiki
😈 "You've finally made it through the wizards' challenges and met me, the
MYSTERIOUS TRAVELER. I have seen many wondrous things, and thank you for helping me to catalogue them in this digital tome.In this final tutorial, you will craft one more view that uses a
Dictionarystructure to render all of the compiled data from the previous tutorials into one sortable page.Best of luck, developer..."
😈 "Unlike those benevolent wizards, I'm not going to give you as much code for free. It will be up to you to fill in the blanks (denoted by comments in the provided code) to make what I want. Let's get started by updating the
OrganismServiceclass inTomeBlazorProject."
- In
TomeBlazorProject, navigate toOrganismService.cs. - Copy the following method, and paste it into
OrganismService.cssomewhere below the constructor.
public async Task<Dictionary<Rarity, List<IOrganism>>> GetOrganismsGroupedByRarity()
{
// TODO: asynchronously get the List<Plant> from the appropriate method and store the reference in a variable called 'plants'
// TODO: asynchronously get the List<Creature> from the appropriate method and store the reference in a variable called 'creatures'
// TODO: Combine both the List<Plant> and List<Creature> into one List<IOrganism> called 'organisms'
return organisms
.GroupBy(o => o.Rarity)
.ToDictionary(group => group.Key, group => group.ToList());
}😈 Infernal Insight: Make sure that you're naming your
Listobjects as specified in the instructions to make sure the provided code works as intended.The provided code converts the
organismsListto aDictionary<Rarity, List<IOrganism>>. Take a moment to look at how LINQ and lambda expressions are being used to do this.
😈 "We now have the means to generate the data, represented with a
Dictionaryand returned via the method inOrganismService. Let's now add a.razorpage to generate a view. I'll give you most of the code here, but will have you complete one line yourself for the hell of it."
- In
/components/pages, create a new Razor component calledOrganisms.razor. - Update your navbar component to include a link to
/organisms. - Copy and paste the following code, replacing the default contents of
Organisms.razor, if necessary.
@page "/organisms"
@inject TomeBlazorProject.Services.OrganismService OrganismService
<h2>Mysterious Traveler's Field Guide</h2>
@if (GroupedOrganisms == null)
{
<p>Loading organisms...</p>
}
else
{
<div class="controls-bar">
<div class="rarity-buttons">
@foreach (var rarity in GroupedOrganisms.Keys.OrderBy(r => r))
{
<button @onclick="() => SelectRarity(rarity)"
class="@(SelectedRarity == rarity ? "active-button" : "")">
@rarity
</button>
}
</div>
<div class="filter-sort-controls">
<input @bind="SearchText" placeholder="Filter by name..." />
<select @bind="SortOption">
<option value="name">Sort by Name</option>
<option value="type">Sort by Type</option>
</select>
</div>
</div>
@if (SelectedRarity != null)
{
// TODO: Use the method that returns filtered and sorted organisms, and store them in a variable called 'filtered'
<h3>@SelectedRarity Organisms (@filtered.Count)</h3>
@if (filtered.Count == 0)
{
<p><em>No matches found.</em></p>
}
else
{
<div class="organism-list">
@foreach (var organism in filtered)
{
<div class="organism-card">
<img src="@organism.ImageUrl" alt="@organism.Name" />
<h4>@organism.Name</h4>
<p>@organism.Description</p>
@if (organism is Plant plant)
{
<p><strong>Bloom Season:</strong> @plant.BloomSeason</p>
<p><strong>Medicinal:</strong> @(plant.IsMedicinal ? "Yes" : "No")</p>
<p><strong>Poisonous:</strong> @(plant.IsPoisonous ? "Yes" : "No")</p>
}
else if (organism is Creature creature)
{
<p><strong>Habitat:</strong> @creature.Habitat</p>
<p><strong>Hostile:</strong> @(creature.IsHostile ? "Yes" : "No")</p>
}
</div>
}
</div>
}
}
}@code {
private Dictionary<Rarity, List<IOrganism>>? GroupedOrganisms;
private Rarity? SelectedRarity;
private string SearchText = "";
private string SortOption = "name";
protected override async Task OnInitializedAsync()
{
GroupedOrganisms = await OrganismService.GetOrganismsGroupedByRarity();
SelectedRarity = GroupedOrganisms.Keys.FirstOrDefault();
}
private void SelectRarity(Rarity rarity)
{
SelectedRarity = rarity;
SearchText = ""; // Optional: clear search on group change
}
private List<IOrganism> GetFilteredAndSortedOrganisms()
{
if (GroupedOrganisms is null || SelectedRarity is null)
return new();
var organisms = GroupedOrganisms[SelectedRarity];
if (!string.IsNullOrWhiteSpace(SearchText))
{
organisms = organisms
.Where(o => o.Name.Contains(SearchText, StringComparison.OrdinalIgnoreCase))
.ToList();
}
organisms = SortOption switch
{
"type" => organisms.OrderBy(o => o is Creature ? "Creature" : "Plant")
.ThenBy(o => o.Name)
.ToList(),
_ => organisms.OrderBy(o => o.Name).ToList()
};
return organisms;
}
}- Navigate to
app.cssin/wwwroot. - Add styling for the following CSS classes:
controls-bar-
filter-sort-controlsfilter-sort-controls inputfilter-sort-controls select
- Navigate to
creatures.jsonandplants.jsonin/wwwroot/data(or wherever your.jsonfiles are located). - Add at least five more creatures to
creatures.json.- Make sure that each field has a value (e.g., each creature needs a unique image URL)
- Add at least five more plants to
plants.json.- Make sure that each field has a value (e.g., each plant needs a unique image URL)
😈 "Thank you for your help, intrepid developer. Hopefully, this guide has served you well in your journey as a programmer. Keep up the good work, and remember, I'll be seeing you again!