Brian
Brian

Reputation: 1989

How do I call a method in one Blazor (parent) component from another Blazor ("child") component?

I am working on building my first "real" Blazor application. Currently, I am working on a search function that will let the users query an API and get answers back. I feel like I am really close on this, but I need just a little bit of direction.

On the DefaultSearch.razor page, there is a text input bound to a String which is passed as a parameter to the "main" Blazor component (which is Index.razor).

Where I am lost is how to trigger the process from the button I have on the DefaultSearch.razor page. There I have an @onclick(doSomething), but I don't know wat to do in the doSomething method to start the process.

Index.razor:

@page "/"
@inject IHttpClientFactory clientFactory
<div class="text-center">
        @if (fetchedResults == null)
        {
            <HeaderLarge />
            <DefaultSearch />
        } else {
           <HeaderSmall />
           <SearchResultsPage FetchedResults="fetchedResults" />
        }
</div>

@code {
    [Parameter]
    public String queryString { get; set; }
    private async Task GetSearchResults()
    {
        try
        {
            HttpClient client = new HttpClient();
            client.BaseAddress = new Uri(@"http://192.168.1.234:8080");
            Search search = new Search(queryString);
            string searchstr = JsonConvert.SerializeObject(search);
            StringContent content = new StringContent(searchstr, Encoding.UTF8, "application/json");
            using HttpResponseMessage httpResponse = await client.PostAsync(("/api/...", content);
            httpResponse.EnsureSuccessStatusCode();
            if (httpResponse.StatusCode == System.Net.HttpStatusCode.OK)
            {
                string response = await httpResponse.Content.ReadAsStringAsync();
                fetchedResults = JsonConvert.DeserializeObject<SearchResultSet>(response);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

DefaultSearch.razor:

<div id="SearchContainer">
    <div id="SearchFormContainer">
        <input id="mainSearchInputField" name="queryString" type="text"  @bind="queryString" /><button @onclick()>Search</button>
    </div>
</div>

@code {
    public String queryString;

    public void doSomething(){
        // What do I do here?
    }
}

SearchResultsPage.razor:

<div>
    @if (FetchedResults != null)
    {
        @foreach (var result in FetchedResults.documents)
        {
            <div>
                <table class="message">
                    <tr>
                        <td>Subject</td>
                        <td>@(result.subject)</td>
                    </tr>
                    <tr>
                        <td>ID</td>
                        <td>@result.docId</td>
                    </tr>
                    <tr>
                        <td class="message_body" colspan="2">
                            <span>@(result.messageBody)</span>
                        </td>
                    </tr>
                </table>
            </div>
        }
    }
</div>

@code {
    [Parameter]
    public SearchResultSet FetchedResults { get; set; }
}

HeaderLarge.razor:

<h1> My Large Header </h1>

HeaderSmalal.razor:

<h5> My Small Header </h5>

Upvotes: 3

Views: 6236

Answers (2)

Bennyboy1973
Bennyboy1973

Reputation: 4208

It seems to me that the entire code body of your Index page is doing the search, and should be moved to the DefaultSearch.razor control. So DoSomething() should actually be GetSearchResults(). Make sure to add a variable to DefaultSearch.razor to actually collect your results, and an event to pass them back to the parent:

[Parameter]
SearchResultSet fetchedResults {get; set;}

[Parameter]
public EventCallback<SearchResultSet> fetchedResultsChanged { get; set; }

And at the end of your search function, you call your custom event:

. . .
fetchedResults = JsonConvert.DeserializeObject<SearchResultSet>(response);
fetchedResultsChanged.InvokeAsync(fetchedResults);

On Index.razor:

<DefaultSearch @bind-fetchedResults=FetchedResults />

@code {
     SearchResultSet FetchedResults {get; set;} // not a Parameter
}

Comments:

  1. To bind a variable, the event must be named EXACTLY the variable name + "Changed"
  2. I find that Visual Studio often gives me an error about not being able to convert EventCallback<T> to EventCallback. I believe this is an Intellisense bug. If you get that particular error, try running anyway.

Upvotes: 1

DotNetDublin
DotNetDublin

Reputation: 798

Hope the below helps. Note the OnClick parameter added to the DefaultSearch parameter

Index.razor changes

<DefaultSearch OnClick="SubmitSearch" />
public void SubmitSearch(string action)
{
    if (action == "PerformSearch")
    {
        //any required code
    }
}

DefaultSearch.razor changes

[Parameter]
public EventCallback<string> OnClick { get; set; }

public void DoSomething()
{
    OnClick.InvokeAsync("PerformSearch");
}

Upvotes: 0

Related Questions