Blazor Walkthrough: Act II - rochellew/csci_1260_spring2025 GitHub Wiki
🧙 "Oh hello, intrepid developer! I see you have met my brother the
BLUE WIZARDand already started on this project! It's wonderful to have you here.In this section of the tutorial, you will learn to create a JSON file and populate a new view with the data. Are you ready? If so, let's get started!
Good luck!
In TomeBlazorProject, you should have a folder called wwwroot. This is intended to contain your .css files, images, and other resources. We are going to add a .json file here that contains some creatures.
Assuming you have followed the instructions in the previous guide, this should be a relatively simple process.
- Create a new folder in
/wwwrootcalleddata. - Create a new file in
/wwwroot/datacalledcreatures.json. - In this file, you will add at least five creatures; though, feel free to add more if you'd like.
- Use the empty JSON below as a template for creating a list of JSON objects.
[
{
"Name": "",
"ImageUrl": "",
"Description": "",
"Rarity": "",
"Habitat": "",
"IsHostile": true
},
{
"Name": "",
"ImageUrl": "",
"Description": "",
"Rarity": "",
"Habitat": "",
"IsHostile": true
},
{
"Name": "",
"ImageUrl": "",
"Description": "",
"Rarity": "",
"Habitat": "",
"IsHostile": true
},
{
"Name": "",
"ImageUrl": "",
"Description": "",
"Rarity": "",
"Habitat": "",
"IsHostile": true
},
{
"Name": "",
"ImageUrl": "",
"Description": "",
"Rarity": "",
"Habitat": "",
"IsHostile": true
}
]🧙 Wizard Wisdom: You will need to change the value of
IsHostiletotrueorfalsedepending on what kind of creature you create. Also, recall that in class on Monday, we said thatenumvalues needed to be stored in JSON as their numerical counterparts -- this is not necessarily true, as we'll see later in the tutorial. Use the name of theRarityenum(e.g., "Common", "Mythical", etc.) rather than a number.
Now that we have some data (a list of at least 5 JSON creatures) in creatures.json, we want to modify OrganismService to be able to return a .NET List<Creature> to a new Razor page.
Navigate to OrganismService.cs.
- We need to do some setup to make this work properly. Add the following attributes to the
OrganismServiceclass.- A
privatereadonlyHttpClientobject named_httpClient.private readonly HttpClient _httpClient;
- A
privatereadonlyJsonSerializerOptionsobject named_options. - A
privatereadonlyNavigationManagerobject named_nav.
- A
🧙 Wizard Wisdom: The code for creating a
privatefield is shown for the_httpClientdeclaration. Use this as a guide for the other two. The reason we're adding these in is twofold: one, we want the serializer to be able to read the values of theRarityenumrather than have to use the numbers (hence theJsonSerializerOptions) and two, we want to read file data from/wwwroot(hence theHttpClientandNavigationManager).
- Create a constructor with the following criteria.
- Has the
publicmodifier - Is named the same as the class (should be
OrganismService) - Has two parameters:
- An
HttpClientobject namedhttpClient(notice the lack of underscore differentiates this one from theprivateattribute you just made) - A
NavigationManagerobject namednav(notice the lack of underscore differentiates this one from theprivateattribute you just made)
- An
- Has the
The body of the constructor is where we will determine what happens every time a new OrganismService object is created.
-
In the constructor body, set the value of the private fields to the parameter variables.
_httpClient = httpClient;_nav = nav;
-
Copy the following code into the constructor body to set up the
JsonSerializerOptionsobject.
_options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
_options.Converters.Add(new JsonStringEnumConverter());Now that we have the constructor set up, along with the capability of reading our JSON file, let's add a method to read the data in from creatures.json and return a List<Creature> to a new Razor page (we'll make that later).
-
Create a new method called
GetCreaturesFromJsonthat meets the following criteria.- Has the
publicmodifier - Is
async - Returns a
Task<List<Creature>> - Has no parameters
- Has the
-
Copy the following code into
GetCreaturesFromJsonin order to provide the functionality we want.
var url = new Uri(new Uri(_nav.BaseUri), "data/creatures.json");
var stream = await _httpClient.GetStreamAsync(url);
List<Creature> creatures = JsonSerializer.Deserialize<List<Creature>>(stream, _options)!;
return creatures ?? new List<Creature>();🧙 Wizard Wisdom: While I provided much of the code for this change to
OrganismService, I would encourage you to take a look at it and try to figure out what's going on. Additionally, you should still have the previous two methods we made last time:GetSampleCreatureandGetSamplePlant.
Remember that we registered OrganismService in our Program.cs file so it can be used in our application. We want to change that registration as well as register one more service to make sure everything will work properly when we create our Razor page. Open Program.cs in TomeBlazorProject.
The first thing we want to do is register a new scoped service so the HttpClient will work properly when reading our JSON data. Assuming you named the variables properly in the previous steps, this should be a simple copy/paste job.
- Copy the following code and paste it above the line where
OrganismServiceis registered.
builder.Services.AddScoped(sp =>
{
var navManager = sp.GetRequiredService<NavigationManager>();
return new HttpClient { BaseAddress = new Uri(navManager.BaseUri) };
});- Change the lifetime of
OrganismServicefromSingletontoScoped- You can do this by changing the method from
AddScopedtoAddSingletonand nothing else needs to change.
- You can do this by changing the method from
Now that we have modified the service, and ensured that everything will work properly behind the scenes, let's add a Razor page to see if all that work paid off! Find the Components folder in the TomeBlazorProject root directory to get started.
- Add a new Razor component in
/Components/PagescalledCreatures.razor. - Replace the default contents of
Creatures.razorwith the following code.
@page "/creatures"
@using TomeLibraryProject
@inject TomeBlazorProject.Services.OrganismService OrganismService
<h1>Magical Creatures</h1>
@if (creatures == null)
{
<p>Loading creatures...</p>
}
else if (!creatures.Any())
{
<p>No creatures found.</p>
}
else
{
@foreach (var creature in creatures)
{
<div class="creature-card" @onclick="@(() => ToggleExpanded(creature))">
<div class="card-header">
<img src="@creature.ImageUrl" alt="@creature.Name" />
<div>
<h3>@creature.Name</h3>
<p class="rarity">@creature.Rarity</p>
</div>
</div>
@if (expandedCreature == creature)
{
<div class="card-details">
<p><strong>Description:</strong> @creature.Description</p>
<p><strong>Habitat:</strong> @creature.Habitat</p>
<p><strong>Hostile?</strong> @(creature.IsHostile ? "Yes" : "No")</p>
</div>
}
</div>
}
}
@code {
private List<Creature>? creatures;
private Creature? expandedCreature;
protected override async Task OnInitializedAsync()
{
creatures = await OrganismService.GetCreaturesFromJson();
}
void ToggleExpanded(Creature creature)
{
expandedCreature = expandedCreature == creature ? null : creature;
}
}- Add the following (do not replace anything) to
app.csto style this new page.
.creature-card {
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
margin: 1rem auto;
padding: 1rem;
max-width: 600px;
transition: box-shadow 0.2s ease;
cursor: pointer;
}
.creature-card:hover {
box-shadow: 0 6px 18px rgba(0,0,0,0.15);
}
.card-header {
display: flex;
align-items: center;
gap: 1rem;
}
.card-header img {
width: 100px;
height: auto;
border-radius: 8px;
}
.rarity {
font-weight: bold;
color: #6c5ce7;
margin-top: 0.25rem;
}
.card-details {
margin-top: 1rem;
border-top: 1px solid #eee;
padding-top: 1rem;
}At this point, you should be good to run the application. You will likely still see the home page from the last tutorial. Add /creatures to the end of the URL in your web browser to see the Creatures page you just made. Click around on the different creature cards to make sure everything works properly.
Here's where you can flex your web design skills! Add the following to the HTML and CSS of the application to finish this tutorial up.
- Create a navbar component that all of the pages share that includes a link to the Creatures page.
- I would recommend adding
NavbarComponentsomewhere in theComponentsfolder, then including it inMainLayout.razorbefore the@bodyline.
- I would recommend adding
- Create some CSS in
app.cssto style the navbar properly. - Modify the Razor and/or CSS to differentiate between different levels of
Rarityvisually (e.g., the Common label is blue, while the Mythical label is purple).
Below is a screenshot for what your webpage should look like, as well as an example navbar.
You did it! Congratulations on finishing up the second tutorial. The next steps will be the inclusion of lambda and LINQ as well as some debugging tips. See you next time.
❤️