Commit 45ac7894 authored by Ed Charbeneau's avatar Ed Charbeneau Committed by Ryan Nowak
Browse files

Migrated full workshop to Preview 8 (#134)

* Updated code to preview 8 and part way through docs

* Fix for missing js and css

* Updated docs
replaced @functions => @code
Made all [Parameter] public
Made refs wildcard version => Version="$(AspNetCoreVersion)

* Completed migration to preivew 8 for all starting points.

* Fix for missing js and css

* More steps migrated to p8

* Update BlazingPizza.Server.csproj
parent 4b8a617b
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<AspNetCoreVersion>3.0.0-preview6.19307.2</AspNetCoreVersion> <AspNetCoreVersion>3.0.0-preview8.19405.7</AspNetCoreVersion>
<EntityFrameworkVersion>3.0.0-preview6.19304.10</EntityFrameworkVersion> <EntityFrameworkVersion>3.0.0-preview8.19405.11</EntityFrameworkVersion>
</PropertyGroup> </PropertyGroup>
</Project> </Project>
...@@ -35,15 +35,15 @@ The home page is implemented as a single component. The `@page` directive specif ...@@ -35,15 +35,15 @@ The home page is implemented as a single component. The `@page` directive specif
First we'll update the home page to display the list of available pizza specials. The list of specials will be part of the state of the `Index` component. First we'll update the home page to display the list of available pizza specials. The list of specials will be part of the state of the `Index` component.
Add a `@functions` block to *Index.razor* with a list field to keep track of the available specials: Add a `@code` block to *Index.razor* with a list field to keep track of the available specials:
```csharp ```csharp
@functions { @code {
List<PizzaSpecial> specials; List<PizzaSpecial> specials;
} }
``` ```
The code in the `@functions` block is added to the generated class for the component. The `PizzaSpecial` type is already defined for you in the Shared project. The code in the `@code` block is added to the generated class for the component. The `PizzaSpecial` type is already defined for you in the Shared project.
To get the available list of specials we need to call an API on the backend. Blazor provides a preconfigured `HttpClient` through dependency injection that is already setup with the correct base address. Use the `@inject` directive to inject an `HttpClient` into the `Index` component. To get the available list of specials we need to call an API on the backend. Blazor provides a preconfigured `HttpClient` through dependency injection that is already setup with the correct base address. Use the `@inject` directive to inject an `HttpClient` into the `Index` component.
...@@ -54,13 +54,13 @@ To get the available list of specials we need to call an API on the backend. Bla ...@@ -54,13 +54,13 @@ To get the available list of specials we need to call an API on the backend. Bla
The `@inject` directive essentially defines a new property on the component where the first token specified the property type and the second token specifies the property name. The property is populated for you using dependency injection. The `@inject` directive essentially defines a new property on the component where the first token specified the property type and the second token specifies the property name. The property is populated for you using dependency injection.
Override the `OnInitAsync` method in the `@functions` block to retrieve the list of pizza specials. This method is part of the component lifecycle and is called when the component is initialized. Use the `GetJsonAsync<T>()` method to handle deserializing the response JSON: Override the `OnInitializedAsync` method in the `@code` block to retrieve the list of pizza specials. This method is part of the component lifecycle and is called when the component is initialized. Use the `GetJsonAsync<T>()` method to handle deserializing the response JSON:
```csharp ```csharp
@functions { @code {
List<PizzaSpecial> specials; List<PizzaSpecial> specials;
protected async override Task OnInitAsync() protected async override Task OnInitializedAsync()
{ {
specials = await HttpClient.GetJsonAsync<List<PizzaSpecial>>("specials"); specials = await HttpClient.GetJsonAsync<List<PizzaSpecial>>("specials");
} }
......
...@@ -27,7 +27,7 @@ Run the app and check that the pizza name is written to the browser console when ...@@ -27,7 +27,7 @@ Run the app and check that the pizza name is written to the browser console when
The `@` symbol is used in Razor files to indicate the start of C# code. Surround the C# code with parens if needed to clarify where the C# code begins and ends. The `@` symbol is used in Razor files to indicate the start of C# code. Surround the C# code with parens if needed to clarify where the C# code begins and ends.
Update the `@functions` block in *Index.razor* to add some additional fields for tracking the pizza being customized and whether the pizza customization dialog is visible. 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 ```csharp
List<PizzaSpecial> specials; List<PizzaSpecial> specials;
...@@ -35,7 +35,7 @@ Pizza configuringPizza; ...@@ -35,7 +35,7 @@ Pizza configuringPizza;
bool showingConfigureDialog; bool showingConfigureDialog;
``` ```
Add a `ShowConfigurePizzaDialog` method to the `@functions` block for handling when a pizza special is clicked. Add a `ShowConfigurePizzaDialog` method to the `@code` block for handling when a pizza special is clicked.
```csharp ```csharp
void ShowConfigurePizzaDialog(PizzaSpecial special) void ShowConfigurePizzaDialog(PizzaSpecial special)
...@@ -66,11 +66,11 @@ Add a *ConfigurePizzaDialog.razor* file under the *Shared* directory. Since this ...@@ -66,11 +66,11 @@ Add a *ConfigurePizzaDialog.razor* file under the *Shared* directory. Since this
> Note: There currently is no option for adding a new file with a .razor extension. Simply use the Razor View (.cshtml) file template, and manually name the file with a .razor extension. > Note: There currently is no option for adding a new file with a .razor extension. Simply use the Razor View (.cshtml) file template, and manually name the file with a .razor extension.
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 `@functions` 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. Add a `@code` block to the `ConfigurePizzaDialog` with the following `Pizza` parameter:
```csharp ```csharp
@functions { @code {
[Parameter] Pizza Pizza { get; set; } [Parameter] public Pizza Pizza { get; set; }
} }
``` ```
...@@ -177,10 +177,10 @@ The user should also be able to select additional toppings on `ConfigurePizzaDia ...@@ -177,10 +177,10 @@ The user should also be able to select additional toppings on `ConfigurePizzaDia
... ...
</div> </div>
@functions { @code {
List<Topping> toppings; List<Topping> toppings;
[Parameter] Pizza Pizza { get; set; } [Parameter] public Pizza Pizza { get; set; }
protected async override Task OnInitAsync() protected async override Task OnInitAsync()
{ {
...@@ -354,9 +354,9 @@ Create a new `ConfiguredPizzaItem` component for displaying a configured pizza. ...@@ -354,9 +354,9 @@ Create a new `ConfiguredPizzaItem` component for displaying a configured pizza.
</div> </div>
</div> </div>
@functions { @code {
[Parameter] Pizza Pizza { get; set; } [Parameter] public Pizza Pizza { get; set; }
[Parameter] EventCallback OnRemoved { get; set; } [Parameter] public EventCallback OnRemoved { get; set; }
} }
``` ```
......
...@@ -80,10 +80,10 @@ Switch back to the `MyOrders` component code. Once again we're going to inject a ...@@ -80,10 +80,10 @@ Switch back to the `MyOrders` component code. Once again we're going to inject a
@inject HttpClient HttpClient @inject HttpClient HttpClient
``` ```
Then add a `@functions` block that makes an asynchronous request for the data we need: Then add a `@code` block that makes an asynchronous request for the data we need:
```csharp ```csharp
@functions { @code {
List<OrderWithStatus> ordersWithStatus; List<OrderWithStatus> ordersWithStatus;
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
...@@ -121,7 +121,7 @@ It's simple to express this using `@if/else` blocks in Razor code. Update your c ...@@ -121,7 +121,7 @@ It's simple to express this using `@if/else` blocks in Razor code. Update your c
} }
</div> </div>
@functions { @code {
List<OrderWithStatus> ordersWithStatus; List<OrderWithStatus> ordersWithStatus;
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
...@@ -201,8 +201,8 @@ Once again we'll add a component to handle this. In the `Pages` directory, creat ...@@ -201,8 +201,8 @@ Once again we'll add a component to handle this. In the `Pages` directory, creat
TODO: Show details for order @OrderId TODO: Show details for order @OrderId
</div> </div>
@functions { @code {
[Parameter] int OrderId { get; set; } [Parameter] public int OrderId { get; set; }
} }
``` ```
...@@ -238,11 +238,11 @@ Before we can implement the polling, we'll need to add the following directives ...@@ -238,11 +238,11 @@ Before we can implement the polling, we'll need to add the following directives
You've already seen `@inject` used with `HttpClient`, so you know what that is for. Plus, you'll recognize `@using` from the equivalent in regular `.cs` files, so this shouldn't be much of a mystery either. Unfortunately, Visual Studio does not yet add `@using` directives automatically in Razor files, so you do have to write them in yourself when needed. You've already seen `@inject` used with `HttpClient`, so you know what that is for. Plus, you'll recognize `@using` from the equivalent in regular `.cs` files, so this shouldn't be much of a mystery either. Unfortunately, Visual Studio does not yet add `@using` directives automatically in Razor files, so you do have to write them in yourself when needed.
Now you can implement the polling. Update your `@functions` block as follows: Now you can implement the polling. Update your `@code` block as follows:
```cs ```cs
@functions { @code {
[Parameter] int OrderId { get; set; } [Parameter] public int OrderId { get; set; }
OrderWithStatus orderWithStatus; OrderWithStatus orderWithStatus;
bool invalidOrder; bool invalidOrder;
...@@ -362,8 +362,8 @@ Create a new file, `OrderReview.razor` inside the `Shared` directory, and have i ...@@ -362,8 +362,8 @@ Create a new file, `OrderReview.razor` inside the `Shared` directory, and have i
</strong> </strong>
</p> </p>
@functions { @code {
[Parameter] Order Order { get; set; } [Parameter] public Order Order { get; set; }
} }
``` ```
...@@ -417,7 +417,7 @@ Now if you try to compile the application, the compiler will complain: ...@@ -417,7 +417,7 @@ Now if you try to compile the application, the compiler will complain:
error CS0535: 'OrderDetails' does not implement interface member 'IDisposable.Dispose()' error CS0535: 'OrderDetails' does not implement interface member 'IDisposable.Dispose()'
``` ```
Resolve this by adding the following method inside the `@functions` block: Resolve this by adding the following method inside the `@code` block:
```cs ```cs
void IDisposable.Dispose() void IDisposable.Dispose()
......
...@@ -13,11 +13,11 @@ Start by adding a new page component, `Checkout.razor`, with a `@page` directive ...@@ -13,11 +13,11 @@ Start by adding a new page component, `Checkout.razor`, with a `@page` directive
<div class="checkout-cols"> <div class="checkout-cols">
<div class="checkout-order-details"> <div class="checkout-order-details">
<h4>Review order</h4> <h4>Review order</h4>
<OrderReview Order="@OrderState.Order" /> <OrderReview Order="OrderState.Order" />
</div> </div>
</div> </div>
<button class="checkout-button btn btn-warning" @onclick="@PlaceOrder"> <button class="checkout-button btn btn-warning" @onclick="PlaceOrder">
Place order Place order
</button> </button>
</div> </div>
...@@ -26,7 +26,7 @@ Start by adding a new page component, `Checkout.razor`, with a `@page` directive ...@@ -26,7 +26,7 @@ Start by adding a new page component, `Checkout.razor`, with a `@page` directive
To implement `PlaceOrder`, copy the method with that name from `Index.razor` into `Checkout.razor`: To implement `PlaceOrder`, copy the method with that name from `Index.razor` into `Checkout.razor`:
```cs ```cs
@functions { @code {
async Task PlaceOrder() async Task PlaceOrder()
{ {
var newOrderId = await HttpClient.PostJsonAsync<int>("orders", OrderState.Order); var newOrderId = await HttpClient.PostJsonAsync<int>("orders", OrderState.Order);
...@@ -57,8 +57,8 @@ We've now got a good place to put some UI for entering a delivery address. As us ...@@ -57,8 +57,8 @@ We've now got a good place to put some UI for entering a delivery address. As us
Create a new component in the `BlazingPizza.Client` project's `Shared` folder called `AddressEditor.razor`. It's going to be a general way to edit `Address` instances, so have it receive a parameter of this type: Create a new component in the `BlazingPizza.Client` project's `Shared` folder called `AddressEditor.razor`. It's going to be a general way to edit `Address` instances, so have it receive a parameter of this type:
```cs ```cs
@functions { @code {
[Parameter] Address Address { get; set; } [Parameter] public Address Address { get; set; }
} }
``` ```
...@@ -68,47 +68,47 @@ The markup here is going to be a bit tedious, so you probably want to copy and p ...@@ -68,47 +68,47 @@ The markup here is going to be a bit tedious, so you probably want to copy and p
<div class="form-field"> <div class="form-field">
<label>Name:</label> <label>Name:</label>
<div> <div>
<input @bind="@Address.Name" /> <input @bind="Address.Name" />
</div> </div>
</div> </div>
<div class="form-field"> <div class="form-field">
<label>Line 1:</label> <label>Line 1:</label>
<div> <div>
<input @bind="@Address.Line1" /> <input @bind="Address.Line1" />
</div> </div>
</div> </div>
<div class="form-field"> <div class="form-field">
<label>Line 2:</label> <label>Line 2:</label>
<div> <div>
<input @bind="@Address.Line2" /> <input @bind="Address.Line2" />
</div> </div>
</div> </div>
<div class="form-field"> <div class="form-field">
<label>City:</label> <label>City:</label>
<div> <div>
<input @bind="@Address.City" /> <input @bind="Address.City" />
</div> </div>
</div> </div>
<div class="form-field"> <div class="form-field">
<label>Region:</label> <label>Region:</label>
<div> <div>
<input @bind="@Address.Region" /> <input @bind="Address.Region" />
</div> </div>
</div> </div>
<div class="form-field"> <div class="form-field">
<label>Postal code:</label> <label>Postal code:</label>
<div> <div>
<input @bind="@Address.PostalCode" /> <input @bind="Address.PostalCode" />
</div> </div>
</div> </div>
@functions { @code {
[Parameter] Address Address { get; set; } [Parameter] public Address Address { get; set; }
} }
``` ```
...@@ -207,12 +207,12 @@ One of the most important built-in UI components for data entry is the `EditForm ...@@ -207,12 +207,12 @@ One of the most important built-in UI components for data entry is the `EditForm
```html ```html
<div class="main"> <div class="main">
<EditForm Model="@OrderState.Order.DeliveryAddress"> <EditForm Model="OrderState.Order.DeliveryAddress">
<div class="checkout-cols"> <div class="checkout-cols">
... leave unchanged ... ... leave unchanged ...
</div> </div>
<button class="checkout-button btn btn-warning" @onclick="@PlaceOrder"> <button class="checkout-button btn btn-warning" @onclick="PlaceOrder">
Place order Place order
</button> </button>
</EditForm> </EditForm>
...@@ -239,7 +239,7 @@ If you ran your application now, you could still submit a blank form (and the se ...@@ -239,7 +239,7 @@ If you ran your application now, you could still submit a blank form (and the se
Next, instead of triggering `PlaceOrder` directly from the button, you need to trigger it from the `EditForm`. Add the following `OnValidSubmit` attribute onto the `EditForm`: Next, instead of triggering `PlaceOrder` directly from the button, you need to trigger it from the `EditForm`. Add the following `OnValidSubmit` attribute onto the `EditForm`:
```html ```html
<EditForm Model="@OrderState.Order" OnValidSubmit="@PlaceOrder"> <EditForm Model="OrderState.Order" OnValidSubmit="PlaceOrder">
``` ```
As you can probably guess, the `<button>` no longer triggers `PlaceOrder` directly. Instead, the button just asks the form to be submitted. And then the form decides whether or not it's valid, and if it is, *then* it will call `PlaceOrder`. As you can probably guess, the `<button>` no longer triggers `PlaceOrder` directly. Instead, the button just asks the form to be submitted. And then the form decides whether or not it's valid, and if it is, *then* it will call `PlaceOrder`.
...@@ -258,7 +258,7 @@ Start by removing the `<ValidationSummary>` component entirely. Then, switch ove ...@@ -258,7 +258,7 @@ Start by removing the `<ValidationSummary>` component entirely. Then, switch ove
<div class="form-field"> <div class="form-field">
<label>Name:</label> <label>Name:</label>
<div> <div>
<input @bind="@Address.Name" /> <input @bind="Address.Name" />
<ValidationMessage For="@(() => Address.Name)" /> <ValidationMessage For="@(() => Address.Name)" />
</div> </div>
</div> </div>
...@@ -294,7 +294,7 @@ Go back to `AddressEditor.razor` once again. Replace each of the `<input>` eleme ...@@ -294,7 +294,7 @@ Go back to `AddressEditor.razor` once again. Replace each of the `<input>` eleme
<div class="form-field"> <div class="form-field">
<label>Name:</label> <label>Name:</label>
<div> <div>
<InputText @bind-Value="@Address.Name" /> <InputText @bind-Value="Address.Name" />
<ValidationMessage For="@(() => Address.Name)" /> <ValidationMessage For="@(() => Address.Name)" />
</div> </div>
</div> </div>
......
...@@ -195,10 +195,10 @@ To fix this, let's make the UI prompt the user to log in (if necessary) as part ...@@ -195,10 +195,10 @@ To fix this, let's make the UI prompt the user to log in (if necessary) as part
In the `Checkout` page component, add some logic to `OnInitAsync` to check whether the user is currently authenticated. If they aren't, send them off to the login endpoint. In the `Checkout` page component, add some logic to `OnInitAsync` to check whether the user is currently authenticated. If they aren't, send them off to the login endpoint.
```cs ```cs
@functions { @code {
[CascadingParameter] Task<AuthenticationState> AuthenticationStateTask { get; set; } [CascadingParameter] Task<AuthenticationState> AuthenticationStateTask { get; set; }
protected override async Task OnInitAsync() protected override async Task OnInitializedAsync()
{ {
var authState = await AuthenticationStateTask; var authState = await AuthenticationStateTask;
if (!authState.User.Identity.IsAuthenticated) if (!authState.User.Identity.IsAuthenticated)
...@@ -244,7 +244,7 @@ We'll fix the bug by persisting the order state in the browser's `localStorage`. ...@@ -244,7 +244,7 @@ We'll fix the bug by persisting the order state in the browser's `localStorage`.
@inject IJSRuntime JSRuntime @inject IJSRuntime JSRuntime
``` ```
Then, inside `OnInitAsync`, add the following line just above the `UriHelper.NavigateTo` call: Then, inside `OnInitializedAsync`, add the following line just above the `UriHelper.NavigateTo` call:
```cs ```cs
await LocalStorage.SetAsync(JSRuntime, "currentorder", OrderState.Order); await LocalStorage.SetAsync(JSRuntime, "currentorder", OrderState.Order);
......
...@@ -14,7 +14,7 @@ Open *Map.razor* and take a look at the code: ...@@ -14,7 +14,7 @@ Open *Map.razor* and take a look at the code:
<div id="@elementId" style="height: 100%; width: 100%;"></div> <div id="@elementId" style="height: 100%; width: 100%;"></div>
@functions { @code {
string elementId = $"map-{Guid.NewGuid().ToString("D")}"; string elementId = $"map-{Guid.NewGuid().ToString("D")}";
[Parameter] double Zoom { get; set; } [Parameter] double Zoom { get; set; }
...@@ -56,7 +56,7 @@ Add the `Map` component to the `OrderDetails` page by adding the following just ...@@ -56,7 +56,7 @@ Add the `Map` component to the `OrderDetails` page by adding the following just
```html ```html
<div class="track-order-map"> <div class="track-order-map">
<Map Zoom="13" Markers="@orderWithStatus.MapMarkers" /> <Map Zoom="13" Markers="orderWithStatus.MapMarkers" />
</div> </div>
``` ```
......
...@@ -30,7 +30,8 @@ It looks like: ...@@ -30,7 +30,8 @@ It looks like:
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components" Version="3.0.0-preview6.19307.2" /> <PackageReference Include="Microsoft.AspNetCore.Components" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="$(AspNetCoreVersion)" />
</ItemGroup> </ItemGroup>
</Project> </Project>
...@@ -94,8 +95,8 @@ Next, to give this dialog some conditional behavior, let's add a parameter of ty ...@@ -94,8 +95,8 @@ Next, to give this dialog some conditional behavior, let's add a parameter of ty
} }
@functions { @functions {
[Parameter] RenderFragment ChildContent { get; set; } [Parameter] public RenderFragment ChildContent { get; set; }
[Parameter] bool Show { get; set; } [Parameter] public bool Show { get; set; }
} }
``` ```
...@@ -204,7 +205,7 @@ Now that we've defined by a generic type parameter we can use it in a parameter ...@@ -204,7 +205,7 @@ Now that we've defined by a generic type parameter we can use it in a parameter
@functions { @functions {
List<TItem> items; List<TItem> items;
[Parameter] Func<Task<List<TItem>>> Loader { get; set; } [Parameter] public Func<Task<List<TItem>>> Loader { get; set; }
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
...@@ -241,9 +242,9 @@ Now, these are our three states of the dialog, and we'd like accept a content pa ...@@ -241,9 +242,9 @@ Now, these are our three states of the dialog, and we'd like accept a content pa
Here's an example of the three parameters to add: Here's an example of the three parameters to add:
```C# ```C#
[Parameter] RenderFragment LoadingContent { get; set; } [Parameter] public RenderFragment LoadingContent { get; set; }
[Parameter] RenderFragment EmptyContent { get; set; } [Parameter] public RenderFragment EmptyContent { get; set; }
[Parameter] RenderFragment<TItem> ItemContent { get; set; } [Parameter] public RenderFragment<TItem> ItemContent { get; set; }
``` ```
note: naming a `RenderFragment` parameter with the suffix *Content* is just a convention. note: naming a `RenderFragment` parameter with the suffix *Content* is just a convention.
...@@ -282,11 +283,11 @@ Let's add another `string` parameter, and finally the functions block of `Templa ...@@ -282,11 +283,11 @@ Let's add another `string` parameter, and finally the functions block of `Templa
@functions { @functions {
List<TItem> items; List<TItem> items;
[Parameter] Func<Task<List<TItem>>> Loader { get; set; } [Parameter] public Func<Task<List<TItem>>> Loader { get; set; }
[Parameter] RenderFragment LoadingContent { get; set; } [Parameter] public RenderFragment LoadingContent { get; set; }
[Parameter] RenderFragment EmptyContent { get; set; } [Parameter] public RenderFragment EmptyContent { get; set; }
[Parameter] RenderFragment<TItem> ItemContent { get; set; } [Parameter] public RenderFragment<TItem> ItemContent { get; set; }
[Parameter] string ListGroupClass { get; set; } [Parameter] public string ListGroupClass { get; set; }
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
...@@ -323,11 +324,11 @@ else ...@@ -323,11 +324,11 @@ else
@functions { @functions {
List<TItem> items; List<TItem> items;
[Parameter] Func<Task<List<TItem>>> Loader { get; set; } [Parameter] public Func<Task<List<TItem>>> Loader { get; set; }
[Parameter] RenderFragment LoadingContent { get; set; } [Parameter] public RenderFragment LoadingContent { get; set; }
[Parameter] RenderFragment EmptyContent { get; set; } [Parameter] public RenderFragment EmptyContent { get; set; }
[Parameter] RenderFragment<TItem> ItemContent { get; set; } [Parameter] public RenderFragment<TItem> ItemContent { get; set; }
[Parameter] string ListGroupClass { get; set; } [Parameter] public string ListGroupClass { get; set; }
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
......
...@@ -22,7 +22,7 @@ This is a rough guide of what topics are best to introduce with each section. ...@@ -22,7 +22,7 @@ This is a rough guide of what topics are best to introduce with each section.
- Introduce @inject and DI - can show how that's a shorthand for a property in @functions - Introduce @inject and DI - can show how that's a shorthand for a property in @functions
- Introduce http + JSON in Blazor (`GetJsonAsync`) - Introduce http + JSON in Blazor (`GetJsonAsync`)
- Talk about async and the interaction with rendering - Talk about async and the interaction with rendering
- Introduce `OnInitAsync` and the common pattern of starting async work - Introduce `OnInitializedAsync` and the common pattern of starting async work
- Introduce @layout - mention that `_Imports.razor` is the most common way to hook it up - Introduce @layout - mention that `_Imports.razor` is the most common way to hook it up
- Introduce NavLink and talk about various `NavLinkMatch` options - Introduce NavLink and talk about various `NavLinkMatch` options
...@@ -46,10 +46,10 @@ This is a rough guide of what topics are best to introduce with each section. ...@@ -46,10 +46,10 @@ This is a rough guide of what topics are best to introduce with each section.
- @page and routing (again)