asp.net-coreautocompleteblazormudblazor

Mublazor Autocomplete Render Custom HTML


I have to display cities of a country in a Group form, for example my data source is a dictionary like

Country1

City1

City2

City3

Country2

City1

City4

City5

City6

If user search for City2, I need to show Country1 as a heading and then City1 under that heading and If user search for City1, I need to show Country1 and then City and then Country2 and City1

I have done similar thing in Bootstrap and it rendered like

and then I can add css to adjust spaces.

So question is, how can I render customer HTML in Blazor Autocpmplete?

This is what I want to achieve but in Mudblazor autocomplete

 _renderMenu: function (ul, items) {
    var that = this,
        currentCountry = "";
    $.each(items, function (index, item) {
        var li;
        if (item.country != currentCountry) {
            ul.append("<li class='ui-autocomplete-country pl-2 pt-2 font-weight-bold'>" + item.country + "</li>");
            currentCountry = item.country;
        }
        li = that._renderItemData(ul, item);
    });
},
_renderItem: function (ul, item) {
    return $("<li></li>")
        .data("item.autocomplete", item)
        .append('<div class="place-name font-weight-lighter pb-2 pl-3">' + item.city+
            '</br><span class="font-weight-normal" style="font-size:12px;"></span></div>')
        .appendTo(ul);
}

Update 1: Thank you (Fengzhi Zhou) for the answer, I had to make a minor change but I got it working. I added AfterItemsTemplate

<MudAutocomplete T="City" Label="Search City" @bind-Value="selectedCity"
             SearchFunc="@SearchCities" ToStringFunc="@(c => c == null ? null : $"{c.Country} - {c.Name}")">
<ItemTemplate Context="city">
    <MudText>
        @if (previousCountry == null || previousCountry != city.Country)
        {
            <div class="country-heading">@city.Country</div>
            previousCountry = city.Country;
        }
        <div class="city-name">@city.Name</div>
    </MudText>
</ItemTemplate>
<AfterItemsTemplate>
    @{
        previousCountry = null;
    }
</AfterItemsTemplate>

enter image description here

Now the City1 looks like a part of "Country", can I render country as a Separate item and make it disabled?

for example enter image description here

Update 2: I have managed to disable items based on a logic, ItemDisabledFunc="@isDisabled" and then

private bool isDisabled(City city)

{ return true or false based on your logic }


Solution

  • If user search for City2, I need to show Country1 as a heading and then City1 under that heading and If user search for City1, I need to show Country1 and then City and then Country2 and City

    Based on your description and if I am understanding correctly that you mean "Country1-city2" and then "Country1-city1/Country2-city1" ,MudBlazor has provided the customizing html design.

    1. Follow the document to enable MudBlazor in your project.

    https://mudblazor.com/getting-started/installation#prerequisites

    2. Using ItemTemplate to render customized html elements

    @page "/"
    
    <MudGrid>
    
        <MudItem xs="12" sm="6" md="4">
            <MudAutocomplete T="City" Label="Search City" @bind-Value="selectedCity"
                             SearchFunc="@SearchCities" ToStringFunc="@(c => c == null ? null : $"{c.Country} - {c.Name}")">
                <ItemTemplate Context="city">
                    <MudText>
                        @if (previousCountry == null || previousCountry != city.Country)
                        {
                            <div class="country-heading">@city.Country</div>
                        }
                        <div class="city-name">@city.Name</div>
                    </MudText>
                </ItemTemplate>
            </MudAutocomplete>
        </MudItem>
    </MudGrid>
    
    @code {
        private City selectedCity;
        private string previousCountry = null;
    
        private List<City> Cities = new List<City>
        {
            new City { Name = "City1", Country = "Country1" },
            new City { Name = "City2", Country = "Country1" },
            new City { Name = "City3", Country = "Country1" },
            new City { Name = "City1", Country = "Country2" },
            new City { Name = "City4", Country = "Country2" },
            new City { Name = "City5", Country = "Country2" },
            new City { Name = "City6", Country = "Country2" },
        };
    
        private Task<IEnumerable<City>> SearchCities(string value, CancellationToken token)
        {
            if (string.IsNullOrEmpty(value))
                return Task.FromResult<IEnumerable<City>>(Cities);
    
            var result = Cities.Where(city => city.Name.Contains(value, StringComparison.InvariantCultureIgnoreCase))
                               .OrderBy(city => city.Country)
                               .ThenBy(city => city.Name);
            return Task.FromResult<IEnumerable<City>>(result);
        }
    
        public class City
        {
            public string Name { get; set; }
            public string Country { get; set; }
        }
    }
    

    Here is the doc https://mudblazor.com/components/autocomplete#presentation .

    3. Test

    enter image description here enter image description here