Reputation: 6659
EDIT: Repo is here - https://github.com/cedwards-telis/HowToBind
In the example below when the "Same as Billing Address" box is checked updating the billing address will update the shipping address inside the ShippingAddress component but not the Shipping address in the parent, i.e. won't appear updated after the Order Summary which is what I want to happen. To do this it would have to go from Child1 to Parent to Child2 to Parent again.
I realise this example isn't at all minimal but at least it's illustrative!
I can't use the Async ShippingAddressTextChanged.InvokeAsync inside a property setter and probably shouldn't anyway. I thought about using OnParametersSetAsync but I think I'm probably missing something more fundamental.
Page
@page "/"
<h1>Order</h1>
<BillingAddress @bind-BillingAddressText=@BillingAddressText />
<ShippingAddress @bind-ShippingAddressText=@ShippingAddressText BillingAddressText=@BillingAddressText />
<h1>Order Summary</h1>
<p>Billing Addreess is:-</p>
<address>@BillingAddressText</address>
<p>Shipping Addreess is:-</p>
<address>@ShippingAddressText</address>
@code {
private string BillingAddressText;
private string ShippingAddressText;
}
BillingAddress.razor
<h3>Billing Address</h3>
<textarea @bind="@BillingAddressText" @bind:event="oninput" @onchange="(args)=>Update(args)" />
<address>@BillingAddressText</address>
@code {
[Parameter]
public string BillingAddressText { get; set; }
[Parameter]
public EventCallback<string> BillingAddressTextChanged { get; set; }
private async Task Update(ChangeEventArgs args)
{
await BillingAddressTextChanged.InvokeAsync(args.Value.ToString());
}
}
ShippingAddress.razor
<h3>Shipping Address</h3>
<input type="checkbox" value="@sameAsBillingAddress" @onchange="SameAsBillingAddressChanged" /> Same as Billing Address
<br />
<textarea @bind="@ShippingAddressText" @bind:event="oninput" @onchange="(args)=>Update(args)" disabled="@sameAsBillingAddress" />
<address>@ShippingAddressText</address>
@code {
string shippingAddressText;
[Parameter]
public string ShippingAddressText
{
get
{
return shippingAddressText;
}
set
{
shippingAddressText = value;
if (sameAsBillingAddress)
{
shippingAddressText = billingAddressText;
}
}
}
string billingAddressText;
[Parameter]
public string BillingAddressText
{
get
{
return billingAddressText;
}
set
{
billingAddressText = value;
if (sameAsBillingAddress)
{
shippingAddressText = billingAddressText;
// Line Below won't work but will hopefully help you divine my intent
// await ShippingAddressTextChanged.InvokeAsync(billingAddressText);
}
}
}
[Parameter]
public EventCallback<string> ShippingAddressTextChanged { get; set; }
bool sameAsBillingAddress;
private async Task SameAsBillingAddressChanged(ChangeEventArgs args)
{
sameAsBillingAddress = (bool)args.Value;
if (sameAsBillingAddress)
{
await ShippingAddressTextChanged.InvokeAsync(billingAddressText);
}
}
private async Task Update(ChangeEventArgs args)
{
await ShippingAddressTextChanged.InvokeAsync(args.Value.ToString());
}
}
Upvotes: 0
Views: 94
Reputation: 30016
Here's a different approach to the one above to your question:
Define a Order Service.
using System;
namespace Blazor.Starter.Services
{
public class OrderService
{
public string ShippingAddress
{
get => _ShippingAddress;
set
{
if (!_ShippingAddress.Equals(value))
{
NotifyOrderChanged();
_ShippingAddress = value;
}
}
}
private string _ShippingAddress = string.Empty;
public string BillingAddress
{
get => _BillingAddress;
set
{
if (!_BillingAddress.Equals(value))
{
NotifyOrderChanged();
_BillingAddress = value;
}
}
}
private string _BillingAddress = string.Empty;
public event EventHandler OrderChanged;
public void NotifyOrderChanged()
=> OrderChanged.Invoke(this, EventArgs.Empty);
}
}
Register it in startup/program
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<OrderService>();
}
BillingAddress.razor
<h3>Billing Address</h3>
<textarea @bind="@OrderService.BillingAddress" />
<address>@OrderService.BillingAddress</address>
@code {
[Inject] OrderService OrderService { get; set; }
}
ShippingAddress.razor
Shipping Address</h3>
<input type="checkbox" @bind-value="@sameAsBillingAddress" /> Same as Billing Address
<br />
<textarea @bind="@OrderService.ShippingAddress" disabled="@sameAsBillingAddress" />
<address>@OrderService.ShippingAddress</address>
@code {
[Inject] OrderService OrderService { get; set; }
private bool sameAsBillingAddress
{
get => _sameAsBillingAddress;
set
{
if (value != _sameAsBillingAddress)
{
if (value)
OrderService.ShippingAddress = OrderService.BillingAddress;
StateHasChanged();
_sameAsBillingAddress = value;
}
}
}
private bool _sameAsBillingAddress;
}
Order.razor
@page "/order"
@implements IDisposable
<h1>Order</h1>
<BillingAddress/>
<ShippingAddress/>
<h1>Order Summary</h1>
<p>Billing Addreess is:-</p>
<address>@OrderService.BillingAddress</address>
<p>Shipping Addreess is:-</p>
<address>@OrderService.ShippingAddress</address>
@code {
[Inject] OrderService OrderService { get; set; }
protected override Task OnInitializedAsync()
{
this.OrderService.OrderChanged += this.OnOrderChanged;
return base.OnInitializedAsync();
}
private void OnOrderChanged(object sender, EventArgs e)
=> InvokeAsync(StateHasChanged);
public void Dispose()
=> this.OrderService.OrderChanged -= this.OnOrderChanged;
}
There are further improvements, but this should help you get started if you want to go in this direction.
Upvotes: 2
Reputation: 14533
I changed the binding on both of your text area's and used the BillingAddressText
setter in ShippingAddress.razor
.
BillingAddress.razor
<textarea value="@BillingAddressText" @oninput="Update" />
<address>@BillingAddressText</address>
@code {
[Parameter]
public string BillingAddressText { get; set; }
[Parameter]
public EventCallback<string> BillingAddressTextChanged { get; set; }
private async Task Update(ChangeEventArgs args)
{
await BillingAddressTextChanged.InvokeAsync(args.Value.ToString());
}
}
ShippingAddress.razor
<h3>Shipping Address</h3>
<input type="checkbox" value="@sameAsBillingAddress" @onchange="SameAsBillingAddressChanged" /> Same as Billing Address
<br />
<textarea value="@ShippingAddressText" @oninput="Update" disabled="@sameAsBillingAddress" />
<address>@ShippingAddressText</address>
@code {
[Parameter]
public string ShippingAddressText { get; set; }
string billingAddressText;
[Parameter]
public string BillingAddressText
{
get => billingAddressText;
set
{
if (billingAddressText == value) return;
billingAddressText = value;
if (sameAsBillingAddress)
{
ShippingAddressText = billingAddressText;
ShippingAddressTextChanged.InvokeAsync(ShippingAddressText);
}
}
}
[Parameter]
public EventCallback<string> ShippingAddressTextChanged { get; set; }
bool sameAsBillingAddress;
private async Task SameAsBillingAddressChanged(ChangeEventArgs args)
{
sameAsBillingAddress = (bool)args.Value;
if (sameAsBillingAddress)
{
await ShippingAddressTextChanged.InvokeAsync(billingAddressText);
}
}
private async Task Update(ChangeEventArgs args)
{
ShippingAddressText = args.Value.ToString();
await ShippingAddressTextChanged.InvokeAsync(ShippingAddressText);
}
}
Upvotes: 2