Event that passes a List<string> from child to parent

Viewed 630

I'm trying to pass a List<string> from the child to the parent. I have a Action<List<string>> defined in the parent and I'm passing it as parameter to the child component. When an event happens within the child component, the expected result is that an updated List<string> is sent to the parent using the callback.

I'm getting compiler error Argument 2: cannot convert from 'method group' to 'EventCallback' in the parent component on <TagComponent UpDateNewTags="UpdateTags"></TagComponent>. If change the UpdateTags to UpdateTags() I get a different error. I also tried passing UpdateTags method as @(()=>UpdateTags) but that didn't work either. Now, I can go about putting the info that I want to pass to the parent in the session local storage and signal the parent to pick it up from there but that's admitting defeat.

How do I pass a List<string> from child to the parent on demand using event callback?

    //Parent 
      <TagComponent  UpDateNewTags="UpdateTags"></TagComponent>

    // Parent function
    public void UpdateTags (List<string> tags)
      {
        if (cpdto.NewTagTitles == null)
          {
            cpdto.NewTagTitles = new List<string>();
          }

        cpdto.NewTagTitles = tags;
      }
    

    //child component.  
    [Parameter]
    public Action<List<string>> UpDateNewTags { get; set; }
2 Answers

The following code sample describes how to create a TagList object which contains a list of Tag objects, each of which contains a string field named Title, how to bind the TagList object to a property of a Child component, how to enable editing of the list of Tag objects in the Child component, and how to propagate the changes back to the parent component.

TagList.cs

public class TagList
    {
        public List<Tag> Tags { get; set; } = new List<Tag>() { new Tag { Title = "tag1" },
                                                            new Tag { Title = "tag2" },
                                                            new Tag { Title = "tag3" }};
    }

Tag.cs

public class Tag
{
   public string Title { get; set; }
} 

Index.razor

<Child @bind-TagList="@tagList.Tags"/>
<div>
    <p>List of tags</p>

   
    @foreach (var tag in tagList.Tags)
    {
        <p>@tag.Title</p>

    }

</div>

@code
    {

    private TagList tagList { get; set; } = new TagList();
}

Child.razor

<div>
    <p>List of tags to update</p>

    @foreach (var tag in TagList)
    {
        <p><input type="text" value="@tag.Title" @onchange="@((ChangeEventArgs args) => OnTagChanged(args, tag))"/></p> @*@onchange= "OnTagChanged"*@

     }

</div>

@code 
{
    [Parameter]
    public List<Tag> TagList { get; set; }

    [Parameter]
    public EventCallback<List<Tag>> TagListChanged { get; set; }

 private void OnTagChanged(ChangeEventArgs args, Tag tag)
    {
        tag.Title = args.Value.ToString();
        TagListChanged.InvokeAsync(TagList);
    }
}

Note that each Tag object is bound to an input text element... When you update a given input element and tab out, the OnTagChanged event handler is triggered, in which the Title field of the current Tag object is updated to reflect the new changes, after which the TagListChanged EventCallback is invoked with a parameter TagList. When this occurs, the TagList object defined in the Index page is updated, and then the StateHasChanged method is automatically called to re-render the page.

Note: The Action delegate is not suitable in the current requirement because Action is a delegate that encapsulate methods that return void, but you want to return a list of string, so you can't use it. If anything, the Func delegate is what you need. But the correct way to code in Blazor is to use the EventCallback 'delegate', which is really not a delegate but a struct that produces the correct delegate for you, and additionally automatically invoke the StateHasChanged method.

Make sure you are not missing a using directive. If I remember correctly you need:

@using Microsoft.AspNetCore.Components 

Also, don’t use Action but EventCallback:

[Parameter]
Public EventCallBack<List<string>> OnListUpdated {get; set;}

EventCallBack Blazor Documentation

To expose events across components, use an EventCallback. A parent component can assign a callback method to a child component's EventCallback.

Related