Unverified Commit 2a112597 authored by Daniel Roth's avatar Daniel Roth Committed by GitHub
Browse files

Update readme and add session docs (#30)

parent 072e32b3
# blazor-workshop
Blazor workshop
# Blazor - app building workshop
Welcome to the Blazor app building workshop!
Sessions
Blazor is an experimental single-page app framework for building client-side web apps using .NET and WebAssembly. In this workshop we will build a complete Blazor app and learn about the various Blazor framework features along the way.
1. Intro
- Who are we?
- Machine setup
- Syllabus
- What is Blazor/WebAssembly?
- Roadmap explanation
1. Components + layout
- Clone repo with ready made backend
- Setup store branding
- Create layout and home page
- Fetch specials list from backend
- Display list of pizza names
- Pizza card component (no templates yet)
- Parameters: PizzaSpecial object
Go ahead and [get started](/docs/00-get-started)!
Lunch
## Sessions
1. Handling UI events & data binding
- Make special pizza cards clickable
- Clicking on a special brings up the new customize dialog
- Index needs to handle the hide/show of the dialog
- Index needs to pass in the Pizza object as well as two 'command' delegates
- Using `bind` and `onclick` on the customize dialog to update prices in real time
- explain the difference between `bind` and `bind-value-oninput` on the slider
- cancel button should close the dialog
- confirm button should close the dialog and add to order
- now add the markup for sidebar which will display orders
- add a ConfiguredPizzaItem component
- hook up the order button to do an HTTP POST and clear the order
- (no way to see existing orders yet)
1. Build the order status screen
- Add a new page MyOrders with `@page orders`
- Add a new NavLink to the layout that links to this URL
- At this point you can appreciate how this page will share layout because that's specified in imports
- MyOrders should retrieve list of orders and show the past orders
- Add a new page OrderDetails to show the status of an individual order
- It should be possible to click from MyOrders->OrderDetails
- The OrderDetails should poll for updates to the order fromthe backend
- Go back to the index and make placing an order navigate you to the MyOrders page
1. DI and the AppState pattern
- Notice that we lose track of any pizzas when you switch between MyOrders and Index, we can fix this by storing the state at a higher level
- Create the OrderState class
- Add to DI in Startup (Scoped)
- Move most of our properties / methods in Index and ConfigurePizza to the OrderState
- Add a StateChanged event to OrderState
- Subscribe to StateChanged from Index in OnInit
- Add an implementation of IDisposable to unsubscribe
1. JS interop
- Add order status
- Real status (map location, time to delivery) via polling
- JS interop for the map
- Add payment via browser payment API
| Session | Topics |
| ----- | ---- |
| [Session #0](/docs/00-get-started.md) | Get bits installed and build your first Blazor app |
| [Session #1](/docs/01-components-and-layout.md) | Get started with components, create the app layout |
| [Session #2](/docs/02-events-and-data-binding.md) | Add customized pizzas to orders |
| [Session #3](/docs/03-dependency-injection.md) | Using dependency injection |
| [Session #4](/docs/04-javascript-interop.md) | Track order status on a real time map |
| [Session #5](/docs/05-templated-components.md) | Create and use components with template parameters |
| [Session #6](docs/06-authentication-and-authorizationg.md) | Authenticate users and authorize access to order status |
| [Session #7](/docs/07-publish-and-deploy.md) | Deploy your app to Azure |
| [Session #8](/docs/08-advanced-components.md) | Component libraries, component lifecycle events, and component internals |
End Day 1
1. Templated components
- Refactor the specials page
- Generic components
1. Authentication & authorization
- See status after leaving site
- Cascading parameters
- Use some form of thing-that-you-do auth
- Maybe talk to updated guidance for SPA token based auth?
Lunch
1. Publish & deployment
- Publish to Azure
1. Razor Components
- See if we can make flipping it to server-side reasonably trivial
1. Component libraries
- Make a Google Maps components so others can use it
- Consider using SignalR library?
1. Component internals
- Lifecycle events
- Render tree
- StateHasChanged
- Configuring the linker (i.e. how to turn it off, pointer to docs)
1. Q&A
Today we will build:
- Pizza store
- Order status and map
- Specials page
- Ordering page
- No cart, just one pizza per order
- Use browser payment apis
- Login
# Get started
In this session, you'll setup your machine for Blazor development and build your first Blazor app.
## Setup
Install the following:
1. [.NET Core 2.1 SDK](https://go.microsoft.com/fwlink/?linkid=873092) (2.1.500 or later).
1. [Visual Studio 2017](https://go.microsoft.com/fwlink/?linkid=873093) (15.9 or later) with the *ASP.NET and web development* workload selected.
1. The latest [Blazor Language Services extension](https://go.microsoft.com/fwlink/?linkid=870389) from the Visual Studio Marketplace.
1. The Blazor templates on the command-line:
```console
dotnet new -i Microsoft.AspNetCore.Blazor.Templates
```
## Build and run your first Blazor app
To create a Blazor project in Visual Studio:
1. Select **File** > **New** > **Project**. Select **Web** > **ASP.NET Core Web Application**. Name the project "BlazorApp1" in the **Name** field. Select **OK**.
![New ASP.NET Core project](https://raw.githubusercontent.com/aspnet/Blazor.Docs/gh-pages/docs/tutorials/build-your-first-blazor-app/_static/new-aspnet-core-project.png)
1. The **New ASP.NET Core Web Application** dialog appears. Make sure **.NET Core** is selected at the top. Also select **ASP.NET Core 2.1**. Choose the **Blazor** template and select **OK**.
![New Blazor app dialog](https://raw.githubusercontent.com/aspnet/Blazor.Docs/gh-pages/docs/tutorials/build-your-first-blazor-app/_static/new-blazor-app-dialog.png)
1. Once the project is created, press **Ctrl-F5** to run the app *without the debugger*. Running with the debugger (**F5**) isn't supported at this time.
> [!NOTE]
> If not using Visual Studio, create the Blazor app at a command prompt on Windows, macOS, or Linux:
>
> ```console
> dotnet new blazor -o BlazorApp1
> cd BlazorApp1
> dotnet run
> ```
>
> Navigate to the app using the localhost address and port provided in the console window output after `dotnet run` is executed. Use **Ctrl-C** in the console window to shutdown the app.
The Blazor app runs in the browser:
![Blazor app Home page](https://user-images.githubusercontent.com/1874516/39509497-5515c3ea-4d9b-11e8-887f-019ea4fdb3ee.png)
Congrats! You just built and ran your first Blazor app!
If you have more time, try out the rest of the [introductory Blazor tutorial](https://blazor.net/docs/tutorials/build-your-first-blazor-app.html#build-components)
# Components and layout
In this session, you'll get started building a pizza store app using Blazor. The app will enable users to order pizzas, customize them, and then track the order deliveries.
## Pizza store starting point
We've setup the initial solution for you for the pizza store app in this repo. Go ahead and clone this repo to your machine. You'll find the [starting point](/docs/00-starting-point.md) in the *save-points* folder along with the end state for each session.
The solution already contains four projects:
![image](https://user-images.githubusercontent.com/1874516/51783836-f2418500-20f4-11e9-95d4-4e9b34e6b2ca.png)
- **BlazingPizza.Client**: This is the Blazor project. It contains the UI components for the app.
- **BlazingPizza.Server**: This is the ASP.NET Core project hosting the Blazor app and also the backend services for the app.
- **BlazingPizza.Shared**: Shared model types for the app.
- **BlazingPizza.ComponentsLibrary**: A library of components and helper code to be used by the app in later sessions.
Run the app by hitting `Ctrl-F5`. Currently the app only contains a simple home page.
![image](https://user-images.githubusercontent.com/1874516/51783774-afcb7880-20f3-11e9-9c22-2f330380ff1e.png)
Open *Pages/Index.cshtml* to see the code for the home page.
```
@page "/"
<h1>Blazing Pizzas</h1>
```
The home page is implemented as a single component. The `@page` directive specifies that the Index component is a routable page with the specified route.
## Display the list of pizza specials
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.cshtml* with a list field to keep track of the available specials:
```csharp
@functions {
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.
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.
```
@page "/"
@inject HttpClient HttpClient
```
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:
```csharp
@functions {
List<PizzaSpecial> specials;
protected async override Task OnInitAsync()
{
specials = await HttpClient.GetJsonAsync<List<PizzaSpecial>>("/specials");
}
}
```
The `/specials` API is defined by the `SpecialsController` in the Server project.
Once the component is initialized it will render its markup. Replace the markup in the `Index` component with the following to list the pizza specials:
```html
<div class="main">
<ul class="pizza-cards">
@if (specials != null)
{
@foreach (var special in specials)
{
<li style="background-image: url('@special.ImageUrl')">
<div class="pizza-info">
<span class="title">@special.Name</span>
@special.Description
<span class="price">@special.GetFormattedBasePrice()</span>
</div>
</li>
}
}
</ul>
</div>
```
![Pizza specials list](https://user-images.githubusercontent.com/1874516/51797486-8b0ef800-21fc-11e9-9d2e-a703d6574537.png)
## Create the layout
Next we'll setup the layout for app.
Layouts in Blazor are also components. They inherit from `BlazorLayoutComponent`, which defines a `Body` property that can be used to specify where the body of the layout should be rendered. The layout component for our pizza store app is defined in *Shared/MainLayout.cshtml*.
```html
@inherits BlazorLayoutComponent
<div class="content">
@Body
</div>
```
To apply a layout use the `@layout` directive. Typically this is done in a `_ViewImports.cshtml` file, which then gets inherited hierarchically. See *Pages/_ViewImports.cshtml*.
```
@layout MainLayout
```
Update the `MainLayout` component to define a top bar with a branding logo and a nav link for the home page:
```html
@inherits BlazorLayoutComponent
<div class="top-bar">
<img class="logo" src="img/logo.svg" />
<NavLink href="" class="nav-tab" Match="NavLinkMatch.All">
<img src="img/pizza-slice.svg" />
<div>Get Pizza</div>
</NavLink>
</div>
<div class="content">
@Body
</div>
```
The `NavLink` component is provided by Blazor. Components can be used from components, which is done by specifying an element with the component's type name along with attributes for any component parameters.
The `NavLink` component is the same as an anchor tag that an active class if the current URL matches the link address. `NavLinkMatch.All` means that the link should be active only when it matches the entire current URL (not just a prefix).
With our new layout our pizza store app now looks like this:
![Pizza store layout](https://user-images.githubusercontent.com/1874516/51797487-9feb8b80-21fc-11e9-8c91-52dfc86d057f.png)
# Show order status
\ No newline at end of file
# Refactor state management
\ No newline at end of file
# Templated components
# Authentication and authorization
# Publish and deploy
\ No newline at end of file
# Outline
Day 1
0. Intro
- Who are we?
- Machine setup
- Syllabus
- What is Blazor/WebAssembly?
- Roadmap explanation
1. Components + layout
- Clone repo with ready made backend
- Setup store branding
- Create layout and home page
- Fetch specials list from backend
- Display list of pizza names
- Pizza card component (no templates yet)
- Parameters: Pizza object
Lunch
2. Handling UI events & data binding
- Make special pizza cards clickable
- Clicking on a special brings up the new customize dialog
- Index needs to handle the hide/show of the dialog
- Index needs to pass in the Pizza object as well as two 'command' delegates
- Using `bind` and `onclick` on the customize dialog to update prices in real time
- explain the difference between `bind` and `bind-value-oninput` on the slider
- cancel button should close the dialog
- confirm button should close the dialog and add to order
- now add the markup for sidebar which will display orders
- add a ConfiguredPizzaItem component
- hook up the order button to do an HTTP POST and clear the order
- (no way to see existing orders yet)
3. Build the order status screen
- Add a new page MyOrders with `@page orders`
- Add a new NavLink to the layout that links to this URL
- At this point you can appreciate how this page will share layout because that's specified in imports
- MyOrders should retrieve list of orders and show the past orders
- Add a new page OrderDetails to show the status of an individual order
- It should be possible to click from MyOrders->OrderDetails
- The OrderDetails should poll for updates to the order fromthe backend
- Go back to the index and make placing an order navigate you to the MyOrders page
4. DI and AppState pattern
- Notice that we lose track of any pizzas when you switch between MyOrders and Index, we can fix this by storing the state at a higher level
- Create the OrderState class
- Add to DI in Startup (Scoped)
- Move most of our properties / methods in Index and ConfigurePizza to the OrderState
- Add a StateChanged event to OrderState
- Subscribe to StateChanged from Index in OnInit
- Add an implementation of IDisposable to unsubscribe
5. JS interop
- Add order status
- Real status (map location, time to delivery) via polling
- JS interop for the map
- Add payment via browser payment API
Day 2
6. Templated components
- Refactor the specials page
- Generic components
7. Authentication & authorization
- See status after leaving site
- Cascading parameters
- Use some form of thing-that-you-do auth
- Maybe talk to updated guidance for SPA token based auth?
Lunch
8. Publish & deployment
- Publish to Azure
9. Advanced components
- Component libraries
- Component lifecycle events
- Render tree
- StateHasChanged
- Configuring the linker (i.e. how to turn it off, pointer to docs)
10. Q&A
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment