How do I properly use .NET Core 5.x and Blazor to allow a user to perform a search, then display results efficiently, appropriately, and Blazor-like?

Viewed 296

I am VERY new at C#, .NET Core, and Blazor and I am trying to get a site to work while "properly" utilizing Blazor's functionality.

How I need this to work is like this:

  1. User navigates to the site: 192.168.1.100
  2. Enters a query into an input field
  3. My code contacts a Swagger-powered API to retrieve the data
  4. A "new" page is generated with the user's requested data

Simple, yes ... and I have done it hundreds of times with standard HTML, PHP, JavaScript, Java, but never with Blazor.

I have a section at the top of the site that I want to remain "static" no matter what page the user goes to. Like this:

User Starts Here

Main Search Page


Results Displayed Here

Search Results  Page


My _Hosts.cshtml page calls MainLayout.razor, which is structured like this:

@inherits LayoutComponentBase
<div>
    <div>
        <TopBanners />
    </div>
    <div>
        <QuickStatusIndicators />
    </div>
    <div>
        <div>
            @Body
        </div>
    </div>
</div>

@Body loads Index.razor which is structured like this:

@page "/"
<div class="text-center">
    <HeaderLarge />
    <form method="POST" action="#">
        <DefaultSearch />
        <SearchOptions />
    </form>
</div>

@code {

}

And, DefaultSearch.razor looks like this:

<div id="mainSearchContainer">
    <div id="mainSearchFormContainer">
        <input id="mainSearchInputField" name="queryString" type="text" />
    </div>
</div>

@code {

}

I have been doing research on how to hit an API endpoint with a POST request in Blazor, and the example I have doesn't seem to use a "normal" form. This works well, and generally, looks like this:

@page "/search"
...
@inject IHttpClientFactory clientFactory
<h3>Search</h3>
<div>
    <input type="text" id="queryString" name="queryString" @bind="queryString" />
    <button @onclick="GetSearchResults">Get It</button>
</div>
<div id="search_response_area">
    @if (fetchedResults != null)
    {
        @foreach (var result in fetchedResults.documents)
        {
            // ...
        }
    }
</div>

@code {
    try
    {
        HttpClient client = new HttpClient();
        client.BaseAddress = new Uri(@"http://192.168.1.234:8080");
        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);
    }
}

So, my question is this. Do I go the <FORM> route OR stick with the way I have found that works successfully using a method like this:

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

@code {
    try
    {
        HttpClient client = new HttpClient();
        client.BaseAddress = new Uri(@"http://192.168.1.234:8080");
        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);
    }
}

And have the new SearchResultsPage.razor contain the @foreach (var result in fetchedResults.documents) code?

If I continue on with the <FORM>, how do I go about proceeding?

2 Answers

The goal is to get back JSON serialized data, so you can deserialize it to an object for presentation. I don't see any particular benefit in trying to package and process a form post, like you might in a normal (read: not Blazor) page.

I think you're on the right track with the latter example. It feels much more "Blazor-y" to me.

@Brian, like in your latter code snippet, something like this perhaps:

@page "/search"
    @inject HttpClient Http
    
    <div>
        <input type="text" id="queryString" name="queryString" @bind="queryString" />
        <button @onclick="GetSearchResults">Get It</button>
    </div>

   <div id="search_response_area">
    @if (fetchedResults != null)
    {
        @foreach (var result in fetchedResults.documents)
        {
            // ...
        }
    }
    </div>

    @code {
        private FetchedResultsModel fetchedResults = null;
        private async Task GetSearchResults()
        {
            ...
            fetchedResults = await Http.GetJsonAsync<FetchedResultsModel>(<...url to get search results...>);
        }
    }

In addition to your existing code, I'd suggest "separating" UI and "serverside", i.e

  • you have your search.razor page - serves as UI with input fields and search results,

  • your search.razor page "inherits" via @inherits SearchBase statement,

  • where SearchBase is something like

    public class SearchBase:ComponentBase {

       [Inject] HttpClient Http { get; set; }
    
       [Inject] IJSRuntime JSRuntime { get; set; }
    
       ....
       ....
    

    }

Related