Checkout.razor
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@page "/checkout"
@inject OrderState OrderState
@inject HttpClient HttpClient
@inject NavigationManager NavigationManager
<div class="main">
<EditForm Model="@OrderState.Order.DeliveryAddress" OnValidSubmit="@PlaceOrder">
<div class="checkout-cols">
<div class="checkout-order-details">
<h4>Review order</h4>
<OrderReview Order="@OrderState.Order" />
</div>
<div class="checkout-delivery-address">
<h4>Deliver to...</h4>
<AddressEditor Address="@OrderState.Order.DeliveryAddress" />
</div>
</div>
<button type="submit" class="checkout-button btn btn-warning">
Place order
</button>
<DataAnnotationsValidator />
</EditForm>
</div>
@code {
async Task PlaceOrder()
{
var newOrderId = await HttpClient.PostJsonAsync<int>("orders", OrderState.Order);
OrderState.ResetOrder();
NavigationManager.NavigateTo($"myorders/{newOrderId}");
}
}
Code explained:
- The EditForm component represents a form.
- It is bound to the OrderState.Order.DeliveryAddress of Address type.
- This form has 2 child components: OrderReview and AddressEditor
- The PlaceOrder method is assigned to OnValidSubmit.
- The data annotations validator (DataAnnotationsValidator component†) attaches validation support using data annotations.
- To handle form submission, this form uses the OnValidSubmit to assign the PlaceOrder event handler to run when the form with valid fields is submitted.
- The PlaceOrder event handler calls the Orders controller. Notice the Route(“orders”) and PlaceOrder action method returns an integer.
1 2 3 4 5 6 7 8 9 10 11
[Route("orders")] [ApiController] // [Authorize] public class OrdersController : Controller ... [HttpPost] public async Task<ActionResult<int>> PlaceOrder(Order order) { .... return order.OrderId; }
AddressEditor.razor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<div class="form-field">
<label>Name:</label>
<div>
<InputText @bind-Value="Address.Name" />
<ValidationMessage For="@(() => Address.Name)" />
</div>
</div>
<div class="form-field">
<label>Line 1:</label>
<div>
<InputText @bind-Value="Address.Line1" />
<ValidationMessage For="@(() => Address.Line1)" />
</div>
</div>
<div class="form-field">
<label>Line 2:</label>
<div>
<InputText @bind-Value="Address.Line2" />
<ValidationMessage For="@(() => Address.Line2)" />
</div>
</div>
<div class="form-field">
<label>City:</label>
<div>
<InputText @bind-Value="Address.City" />
<ValidationMessage For="@(() => Address.City)" />
</div>
</div>
<div class="form-field">
<label>Region:</label>
<div>
<InputText @bind-Value="Address.Region" />
<ValidationMessage For="@(() => Address.Region)" />
</div>
</div>
<div class="form-field">
<label>Postal code:</label>
<div>
<InputText @bind-Value="Address.PostalCode" />
<ValidationMessage For="@(() => Address.PostalCode)" />
</div>
</div>
@code {
[Parameter] public Address Address { get; set; }
}
Code explained
- The InputText component is an input component for editing string values. The @bind-Value directive attribute binds the Address.Name model property to the InputText component’s Value property.
- This component has a component parameter which is used to pass data from the parent component to this component and is defined using public C# properties on the component class with the [Parameter] attribute