0
votes

In a grocery list app, I have a parent component looping over grocery items, and a child component showing details for each grocery item using input elements (a checkbox and a text field). I've set up two-way binding directly to the GroceryItem members and it's working fine as far as updating the members with input data.

Where I have a problem is to notify the parent component to re-render itself. I know I have to invoke an EventCallBack when I detect a change, but I want to avoid duplicating my class members as local variables, just to be able to use the setter for detecting the change and invoke the EventCallBack. The other trick I found was to manually bind the data, but again this create duplication since I have to set up an event handler for each input element.

Is there a better way?

I wish there was a mechanism by which I could add more onchange event handlers to the input elements, in order to invoke EventCallBack... But because bind takes up the only slot available I can't.

// This is the child component

<li>
    <input type="checkbox" @bind=Item.Purchased />
    <input type="text" @bind=Item.Name />
</li>

@code
{
    [Parameter]
    public GroceryItem Item { get; set; }

    [Parameter]
    public EventCallback<GroceryItem> ItemChanged { get; set; }
}
2
Isn't Item.Name a read-only field ?Don't you provide a pre-defined list of items that can be selected (checked) or not selected (default, unchecked) - enet
@enet I apologize, but I'm not sure I understand your comment. Item.Name is not read-only, it's a member of the data model object GroceryItem. I'm passing it to the child component so that the component can display it, and also update it if the user decides to do so (user can check the box or change the name). The child component is rendered in a loop in the parent component. - sw1337
You are probably using @foreach loop in your parent component, and if so, one way to work around this is to forego the child component, and just write your <li>...</li> markup directly in the parent component, and then use @bind-value="@item.purchased", etc. Since this doesn't worry about callbacks and triggering state changes, the binding should work very well. Any reason why this couldn't work or you would prefer not? - Nik P
@NikProtsman Sure, that's the way I had it before but I need to split the logic. This is only a little test project as part of my Blazor learning journey, and I expect to have much more complex types to deal with later in real projects. - sw1337

2 Answers

0
votes

If you want to call a parent component method from a child one, you need to use EventCallback.
However there are a lot of ways to call a method in a child component from a parent component.
To find out more, see the Blazor LifeCycle and RenderTree.
parent component has a full control over its children but a child only calls the parents by callback.

0
votes

Being able to register an additional event handler for an event you've a binding for isn't supported yet: https://github.com/dotnet/aspnetcore/issues/14365

One way you can work around (I haven't tried this but I think this may work) may be to define helper properties in your component and bind directly to them instead. The below code demonstrates it for just one of these:

<li>
    <input type="text" @bind=Name />
</li>

@code
{
    [Parameter]
    public GroceryItem Item { get; set; }

    private string Name
    {
      get => this.Item.Name;
      set => this.Item.Name = value;
    }

    [Parameter]
    public EventCallback<GroceryItem> ItemChanged { get; set; }
}

If this doesn't work I'll remove it, so people don't get confused about a wrong answer. Just an attempt to help.

If this works, then you can fire your event in the setter of the helper property.