Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
* Shared lib updated with Nullable context. Partially fixes issue dotnet-presentations#105.

* Server updated with Nullable context. Partially fixes issue dotnet-presentations#105.

* Razor projects updated with Nullable context. Partially fixes issue dotnet-presentations#105.

* 01 updated with Nullable context, including instructions (md). Partially fixes issue dotnet-presentations#105.

* Updated 02. Dependencies to use Nullable

* Updated code to include nullable considerations.

* Updated instructions to align with nullable.

* Updated dependencies to use nullable context

* Fixes issue dotnet-presentations#358 and Fixes issue dotnet-presentations#238

* Sync with previous lesson

* Removed guard on GetUserId to allow workshop to flow without authorization.

* Sync changes across lessons

* Updated 03 with nullability context

* Updated instructions with nullable code

* Cleanup whitespace

* Sync dependencies 04 with previous steps

* Fixed: Starting point 04's server code was actually from a later step 06 or above.

* Sync 04 client changes with previous steps

* Updated client with nullable context enabled

* Sync dependencies 05 with previous changes

* Fixed step 05 controller is from step 06

* Sync with client with previous step

* Updated 05 with nullable context enabled

* Sync with previous steps

* Updated 06 with nullable context enabled.

* Synced dependencies with previous lesson

* Sync client changes with previous step

* Updated 07 with nullable context enabled

* Synced dependencies with previous step

* Synced client with previous lesson

* Updated 08 with nullable conetext enabled

* Sync with prevous lesson

* Updated 09 with nullable context enabled

* Updated src with nullable context enabled

* Updated docs

* Notification interop doesn't work with required.

* Extra sln file

* Fixed link that moved for getting started

* Removed extra comments.

* Clairified the message about EditorRequired.

* Trimmed whitespace

* Supressed nullable warning on change event

* Added EditorRequired to step

* Refactored GetUserId. This code was duplicated in multiple places.

* Updated `!=` to `is not` null

* Fixes: SignoutSessionStateManager obsolete warning. See: https://learn.microsoft.com/en-us/dotnet/core/compatibility/aspnet-core/7.0/wasm-app-authentication

* Added Nullable to orderWithStatus

* Updated Razor Class Library verbage, it was reminicent of .NET Core 3.0/3.1

* Changed `!=` to `is not null`

* The app was updated to include a Minimal API implementation of NotificationsController. However the code in save-point/09 and instructions were not updated. This fixes the issue.

* Pulling up some missed changes from the final exercise

* Removed unused NavigationManager references

* Updated code on Map component
  • Loading branch information
EdCharbeneau authored Nov 29, 2023
1 parent caaca4e commit bb345a1
Show file tree
Hide file tree
Showing 345 changed files with 2,042 additions and 1,802 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -337,3 +337,6 @@ ASALocalRun/

# Visual Studio Code folder
.vscode/
*.db-shm
*.db-wal
/save-points/08-templated-components/BlazingPizza.Client/BlazingPizza.Client.sln
2 changes: 1 addition & 1 deletion docs/00-get-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ In this session, you'll setup your machine for Blazor development and build your

## Setup

To get started with Blazor, follow the getting instructions on [blazor.net](https://blazor.net).
To get started with Blazor, follow the getting instructions on [blazor.net](https://dotnet.microsoft.com/en-us/learn/aspnet/blazor-tutorial/intro).

## Build your first app

Expand Down
6 changes: 3 additions & 3 deletions docs/01-components-and-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Add a `@code` block to *Index.razor* with a list field to keep track of the avai

```csharp
@code {
List<PizzaSpecial> specials;
List<PizzaSpecial>? specials;
}
```

Expand All @@ -61,7 +61,7 @@ Override the `OnInitializedAsync` method in the `@code` block to retrieve the li

```csharp
@code {
List<PizzaSpecial> specials;
List<PizzaSpecial>? specials;

protected override async Task OnInitializedAsync()
{
Expand All @@ -81,7 +81,7 @@ Once the component is initialized it will render its markup. Replace the markup
```html
<div class="main">
<ul class="pizza-cards">
@if (specials != null)
@if (specials is not null)
{
@foreach (var special in specials)
{
Expand Down
92 changes: 51 additions & 41 deletions docs/02-customize-a-pizza.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ The `@` symbol is used in Razor files to indicate the start of C# code. Surround
Update the `@code` block in *Index.razor* to add some additional fields for tracking the pizza being customized and whether the pizza customization dialog is visible.

```csharp
List<PizzaSpecial> specials;
Pizza configuringPizza;
List<PizzaSpecial>? specials;
Pizza? configuringPizza;
bool showingConfigureDialog;
```

Expand Down Expand Up @@ -67,11 +67,13 @@ Add a *ConfigurePizzaDialog.razor* file under the *Shared* directory. Since this

> Note: In Visual Studio, you can right-click the *Shared* directory in Solution Explorer, then choose *Add* -> *New Item* to use the *Razor Component* item template to add a new Razor component.
The `ConfigurePizzaDialog` should have a `Pizza` parameter that specifies the pizza being configured. Component parameters are defined by adding a writable property to the component decorated with the `[Parameter]` attribute. Add a `@code` block to the `ConfigurePizzaDialog` with the following `Pizza` parameter:
The `ConfigurePizzaDialog` should have a `Pizza` parameter that specifies the pizza being configured. Component parameters are defined by adding a writable property to the component decorated with the `[Parameter]` attribute. Because the `Pizza` parameter requires a value for the component to function, the `[EditorRequired]` attribute is also added. By adding the `[EditorRequired]` attribute, if a parameter value isn't provided, editors or build tools may display warnings to the user.

Add a `@code` block to the `ConfigurePizzaDialog` with the following `Pizza` parameter:

```csharp
@code {
[Parameter] public Pizza Pizza { get; set; }
[Parameter, EditorRequired] public Pizza Pizza { get; set; } = new();
}
```

Expand All @@ -80,22 +82,22 @@ The `ConfigurePizzaDialog` should have a `Pizza` parameter that specifies the pi
Add the following basic markup for the `ConfigurePizzaDialog`:

```html
<div class="dialog-container">
<div class="dialog">
<div class="dialog-title">
<h2>@Pizza.Special.Name</h2>
@Pizza.Special.Description
</div>
<form class="dialog-body"></form>
<div class="dialog-buttons">
<button class="btn btn-secondary mr-auto">Cancel</button>
<span class="mr-center">
Price: <span class="price">@(Pizza.GetFormattedTotalPrice())</span>
</span>
<button class="btn btn-success ml-auto">Order</button>
<div class="dialog-container">
<div class="dialog">
<div class="dialog-title">
<h2>@Pizza.Special?.Name</h2>
@Pizza.Special?.Description
</div>
<form class="dialog-body"></form>
<div class="dialog-buttons">
<button class="btn btn-secondary mr-auto">Cancel</button>
<span class="mr-center">
Price: <span class="price">@(Pizza.GetFormattedTotalPrice())</span>
</span>
<button class="btn btn-success ml-auto">Order</button>
</div>
</div>
</div>
</div>
```

Update *Pages/Index.razor* to show the `ConfigurePizzaDialog` when a pizza special has been selected. The `ConfigurePizzaDialog` is styled to overlay the current page, so it doesn't really matter where you put this code block.
Expand Down Expand Up @@ -145,7 +147,7 @@ If you wanted to implement two-way binding manually, you could do so by combinin
max="@Pizza.MaximumSize"
step="1"
value="@Pizza.Size"
@onchange="@((ChangeEventArgs e) => Pizza.Size = int.Parse((string) e.Value))" />
@onchange="@((ChangeEventArgs e) => Pizza.Size = int.Parse((string?) e.Value))" />
```

In Blazor you can use the `@bind` directive attribute to specify a two-way binding with this same behavior. The equivalent markup using `@bind` looks like this:
Expand Down Expand Up @@ -180,13 +182,14 @@ The user should also be able to select additional toppings on `ConfigurePizzaDia
</div>

@code {
List<Topping> toppings;
// toppings is only null while loading
List<Topping> toppings = null!;

[Parameter] public Pizza Pizza { get; set; }
[Parameter, EditorRequired] public Pizza Pizza { get; set; } = default!;

protected async override Task OnInitializedAsync()
{
toppings = await HttpClient.GetFromJsonAsync<List<Topping>>("toppings");
toppings = await HttpClient.GetFromJsonAsync<List<Topping>>("toppings") ?? new();
}
}
```
Expand All @@ -196,7 +199,7 @@ Add the following markup in the dialog body for displaying a drop down list with
```html
<div>
<label>Extra Toppings:</label>
@if (toppings == null)
@if (toppings is null)
{
<select class="custom-select" disabled>
<option>(loading...)</option>
Expand All @@ -221,11 +224,14 @@ Add the following markup in the dialog body for displaying a drop down list with
<div class="toppings">
@foreach (var topping in Pizza.Toppings)
{
<div class="topping">
@topping.Topping.Name
<span class="topping-price">@topping.Topping.GetFormattedPrice()</span>
<button type="button" class="delete-topping" @onclick="@(() => RemoveTopping(topping.Topping))">x</button>
</div>
if (topping?.Topping is not null)
{
<div class="topping">
@topping.Topping.Name
<span class="topping-price">@topping.Topping.GetFormattedPrice()</span>
<button type="button" class="delete-topping" @onclick="@(() => RemoveTopping(topping.Topping))">x</button>
</div>
}
}
</div>
```
Expand All @@ -235,15 +241,16 @@ Also add the following event handlers for topping selection and removal:
```csharp
void ToppingSelected(ChangeEventArgs e)
{
if (int.TryParse((string)e.Value, out var index) && index >= 0)
if (int.TryParse((string?)e.Value, out var index) && index >= 0)
{
AddTopping(toppings[index]);
}
}

void AddTopping(Topping topping)
{
if (Pizza.Toppings.Find(pt => pt.Topping == topping) == null)
if (toppings is null) return;
if (Pizza.Toppings.Find(pt => pt.Topping == topping) is null)
{
Pizza.Toppings.Add(new PizzaTopping() { Topping = topping });
}
Expand All @@ -267,8 +274,8 @@ The Cancel and Order buttons don't do anything yet. We need some way to communic
Add two parameters to the `ConfigurePizzaDialog` component: `OnCancel` and `OnConfirm`. Both parameters should be of type `EventCallback`.

```csharp
[Parameter] public EventCallback OnCancel { get; set; }
[Parameter] public EventCallback OnConfirm { get; set; }
[Parameter, EditorRequired] public EventCallback OnCancel { get; set; }
[Parameter, EditorRequired] public EventCallback OnConfirm { get; set; }
```

Add `@onclick` event handlers to the `ConfigurePizzaDialog` that trigger the `OnCancel` and `OnConfirm` events.
Expand Down Expand Up @@ -308,8 +315,8 @@ Run the app and verify that the dialog now disappears when the Cancel button is
When the `OnConfirm` event is fired, the customized pizza should be added to the user's order. Add an `Order` field to the `Index` component to track the user's order.

```csharp
List<PizzaSpecial> specials;
Pizza configuringPizza;
List<PizzaSpecial>? specials;
Pizza? configuringPizza;
bool showingConfigureDialog;
Order order = new Order();
```
Expand All @@ -326,9 +333,12 @@ In the `Index` component add an event handler for the `OnConfirm` event that add
```csharp
void ConfirmConfigurePizzaDialog()
{
order.Pizzas.Add(configuringPizza);
configuringPizza = null;

if (configuringPizza is not null)
{
order.Pizzas.Add(configuringPizza);
configuringPizza = null;
}

showingConfigureDialog = false;
}
```
Expand All @@ -344,11 +354,11 @@ Create a new `ConfiguredPizzaItem` component for displaying a configured pizza.
```html
<div class="cart-item">
<a @onclick="OnRemoved" class="delete-item">x</a>
<div class="title">@(Pizza.Size)" @Pizza.Special.Name</div>
<div class="title">@(Pizza.Size)" @Pizza.Special?.Name</div>
<ul>
@foreach (var topping in Pizza.Toppings)
{
<li>+ @topping.Topping.Name</li>
<li>+ @topping.Topping?.Name</li>
}
</ul>
<div class="item-price">
Expand All @@ -357,8 +367,8 @@ Create a new `ConfiguredPizzaItem` component for displaying a configured pizza.
</div>

@code {
[Parameter] public Pizza Pizza { get; set; }
[Parameter] public EventCallback OnRemoved { get; set; }
[Parameter, EditorRequired] public Pizza Pizza { get; set; } = new();
[Parameter, EditorRequired] public EventCallback OnRemoved { get; set; }
}
```

Expand Down
18 changes: 9 additions & 9 deletions docs/03-show-order-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ Then add a `@code` block that makes an asynchronous request for the data we need

```csharp
@code {
IEnumerable<OrderWithStatus> ordersWithStatus;
IEnumerable<OrderWithStatus>? ordersWithStatus;

protected override async Task OnParametersSetAsync()
{
Expand All @@ -124,7 +124,7 @@ It's simple to express this using `@if/else` blocks in Razor code. Update the ma

```html
<div class="main">
@if (ordersWithStatus == null)
@if (ordersWithStatus is null)
{
<text>Loading...</text>
}
Expand Down Expand Up @@ -257,9 +257,9 @@ Now you can implement the polling. Update your `@code` block as follows:
@code {
[Parameter] public int OrderId { get; set; }

OrderWithStatus orderWithStatus;
OrderWithStatus? orderWithStatus;
bool invalidOrder;
CancellationTokenSource pollingCancellationToken;
CancellationTokenSource? pollingCancellationToken;

protected override void OnParametersSet()
{
Expand All @@ -278,7 +278,7 @@ Now you can implement the polling. Update your `@code` block as follows:
try
{
invalidOrder = false;
orderWithStatus = await HttpClient.GetFromJsonAsync<OrderWithStatus>($"orders/{OrderId}");
orderWithStatus = await HttpClient.GetFromJsonAsync<OrderWithStatus>($"orders/{OrderId}")?? throw new NullReferenceException();
StateHasChanged();

if (orderWithStatus.IsDelivered)
Expand Down Expand Up @@ -321,7 +321,7 @@ OK, so we're getting the order details, and we're even polling and updating that
<h2>Nope</h2>
<p>Sorry, this order could not be loaded.</p>
}
else if (orderWithStatus == null)
else if (orderWithStatus is null)
{
<text>Loading...</text>
}
Expand Down Expand Up @@ -363,15 +363,15 @@ Create a new file, `OrderReview.razor` inside the `Shared` directory, and have i
<p>
<strong>
@(pizza.Size)"
@pizza.Special.Name
@pizza.Special?.Name
(£@pizza.GetFormattedTotalPrice())
</strong>
</p>

<ul>
@foreach (var topping in pizza.Toppings)
{
<li>+ @topping.Topping.Name</li>
<li>+ @topping.Topping?.Name</li>
}
</ul>
}
Expand All @@ -384,7 +384,7 @@ Create a new file, `OrderReview.razor` inside the `Shared` directory, and have i
</p>

@code {
[Parameter] public Order Order { get; set; }
[Parameter, EditorRequired] public Order Order { get; set; } = new();
}
```

Expand Down
9 changes: 6 additions & 3 deletions docs/04-refactor-state-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public class OrderState
{
public bool ShowingConfigureDialog { get; private set; }

public Pizza ConfiguringPizza { get; private set; }
public Pizza? ConfiguringPizza { get; private set; }

public Order Order { get; private set; } = new Order();
}
Expand Down Expand Up @@ -86,8 +86,11 @@ public void CancelConfigurePizzaDialog()

public void ConfirmConfigurePizzaDialog()
{
Order.Pizzas.Add(ConfiguringPizza);
ConfiguringPizza = null;
if (ConfiguringPizza is not null)
{
Order.Pizzas.Add(ConfiguringPizza);
ConfiguringPizza = null;
}

ShowingConfigureDialog = false;
}
Expand Down
Loading

0 comments on commit bb345a1

Please sign in to comment.