On a page there are 3 components :
They are NOT parent/child but side by side. I'd like from Component1
hide one or both Component2
via some buttons not present in the code below
Component1:
public partial class Component1 : ComponentBase
{
[Parameter]
public Component2 Component2A { get; set; }
[Parameter]
public Component2 Component2B { get; set; }
}
Component2
public partial class Component2 : ComponentBase
{
private bool IsVisible = false;
public void ToggleVisibility(bool isVisible)
{
IsVisible = isVisible;
}
}
In the page
<Component1 Component2A=rightComponent1 Component2B=component2B />
<Component2 @ref="component2A" />
<Component2 @ref="component2B" />
I get these errors :
The name 'component2A' does not exist in the current context
The name 'component2B' does not exist in the current context
Am I missed something ? I have to define @ref="component2A"
and @ref="component2A"
somewhere ?
Update 1 based on "MrC aka Shaun Curtis".
I'd like pass data (MyModel
) to components. When I change a value in the value propagate to all components.
I do this :
My Model
public class MyModel
{
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
}
Page
<CascadingValue Value=_context IsFixed>
<Component1 ComponentA=this.ComponentAUid ComponentB=this.ComponentBUid />
<Component2 Uid=this.ComponentAUid><div class="alert alert-danger m-2">Hello from component 1</div></Component2>
<Component2 Uid=this.ComponentBUid><div class="alert alert-success m-2">Hello from component 2</div></Component2>
</CascadingValue>
@code {
private XPageContext _context = new XPageContext(new MyModel { FirstName = "MyFirstName" });
private Guid ComponentAUid = Guid.NewGuid();
private Guid ComponentBUid = Guid.NewGuid();
}
Record
public record RegisteredComponent(Guid Uid, Func<bool, Task> Show, MyModel MyModel);
Component2
@if(_isVisible)
{
@this.ChildContent
}
<EditForm Model="MyModel">
<InputText @bind-Value=MyModel.FirstName />
</EditForm>
@code {
[Parameter] public Guid Uid { get; set; } = Guid.NewGuid();
[CascadingParameter] private XPageContext _context { get; set; } = default!;
[Parameter] ublic RenderFragment? ChildContent { get; set; }
[Parameter] public MyModel MyModel { get; set; }
private bool _isVisible = false;
protected override void OnInitialized()
{
ArgumentNullException.ThrowIfNull(_context);
MyModel = _context._myModel;
_context.Register(Uid, this.ToggleVisibilityAsync);
}
public Task ToggleVisibilityAsync(bool isVisible)
{
_isVisible = isVisible;
// Called as this is not a UI event and therefore doesn't cause an automatic render
StateHasChanged();
return Task.CompletedTask;
}
public void Dispose()
{
_context.DeRegister(Uid);
}
}
In the InputText
, I see the firstName value in the 2 Component2
but when I change in one there no impact on the second
Firstly, you shouldn't pass around component references: you have no control over their lifecycles and can easily have a reference to a "dead" component.
You need to create a shared comtext shared by all the components. This solution may seem a litle complex, but it demonstrates the principles of complex inter component communications. You can share a Scoped
service, but this doesn't match the scope of a page. Here I use a standard object and pass it down through a cascade.
The shared Context:
public class XPageContext
{
private List<RegisteredComponent> _registeredComponents = new();
public void Register(Guid uid, Func<bool, Task> show)
{
if (!_registeredComponents.Any(item => item.Uid == uid))
_registeredComponents.Add(new RegisteredComponent(uid, show));
}
public void DeRegister(Guid uid)
{
var component = _registeredComponents.FirstOrDefault(item => item.Uid == uid);
if(component != null)
_registeredComponents.Remove(component);
}
public async Task ShowComponentAsync(Guid uid, bool show)
{
var component = _registeredComponents.FirstOrDefault(item => item.Uid == uid);
if (component != null)
await component.Show.Invoke(show);
}
}
public record RegisteredComponent(Guid Uid, Func<bool, Task> Show);
Component2
@implements IDisposable
@if(_isVisible)
{
@this.ChildContent
}
@code {
[Parameter] public Guid Uid { get; set; } = Guid.NewGuid();
[CascadingParameter] private XPageContext _context { get; set; } = default!;
[Parameter] public RenderFragment? ChildContent { get; set; }
private bool _isVisible = false;
protected override void OnInitialized()
{
ArgumentNullException.ThrowIfNull(_context);
_context.Register(Uid, this.ToggleVisibilityAsync);
}
public Task ToggleVisibilityAsync(bool isVisible)
{
_isVisible = isVisible;
// Called as this is not a UI event and therefore doesn't cause an automatic render
StateHasChanged();
return Task.CompletedTask;
}
public void Dispose()
{
_context.DeRegister(Uid);
}
}
Component1
<h3>Component1</h3>
<button class="btn btn-dark" @onclick=this.ToggleComponentA>Toggle Component A</button>
<button class="btn btn-primary" @onclick=this.ToggleComponentB>Toggle Component B</button>
@code {
[CascadingParameter] private XPageContext _context { get; set; } = default!;
[Parameter, EditorRequired] public Guid ComponentA { get; set; }
[Parameter, EditorRequired] public Guid ComponentB { get; set; }
private bool _componentAState;
private bool _componentBState;
protected override void OnInitialized()
{
ArgumentNullException.ThrowIfNull(_context);
}
private async Task ToggleComponentA()
{
_componentAState = !_componentAState;
await _context.ShowComponentAsync(ComponentA, _componentAState);
}
private async Task ToggleComponentB()
{
_componentBState = !_componentBState;
await _context.ShowComponentAsync(ComponentB, _componentBState);
}
}
Index
@page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<CascadingValue Value=_context IsFixed>
<Component1 ComponentA=this.ComponentAUid ComponentB=this.ComponentBUid />
<Component2 Uid=this.ComponentAUid><div class="alert alert-danger m-2">Hello from component 1</div></Component2>
<Component2 Uid=this.ComponentBUid><div class="alert alert-success m-2">Hello from component 2</div></Component2>
</CascadingValue>
@code {
private XPageContext _context = new();
private Guid ComponentAUid = Guid.NewGuid();
private Guid ComponentBUid = Guid.NewGuid();
}