Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dropdown component toggle methods (ToggleAsync etc.) not working as expected #1025

Open
MarcinWatroba opened this issue Feb 5, 2025 · 4 comments

Comments

@MarcinWatroba
Copy link

MarcinWatroba commented Feb 5, 2025

Description
Dropdown component toggle methods such as ToggleAsync and ShowAsync always display dropdown menu of the first dropdown component in the page. This occurs in global server side rendering (haven't tested other types of rendering).

Example: You have Dropdown A and Dropdown B, with their respective menus, Menu A and Menu B.
When calling ToggleAsync method on either Dropdown A or Dropdown B, Dropdown A menu appears. Dropdown B menu never appears.

To Reproduce
Use sample code provided

Expected behavior
Menu A should appear when toggling Dropdown A, Menu B should appear when toggling Dropdown B

Screenshots

Image

Versions:

  • .NET Version: .NET8
  • BlazorBootstrap: 3.3.1
  • Blazor WebAssembly / Server: Server
  • Blazor Interactive Render Mode: Server
  • Blazor Interactivity Location: global

Sample code
I replaced DropdownToggleButton with button in this example but it does not matter where the toggle method is called.
I tried assigning custom unique ids and names to each component too and nothing helped.

Same issue persists when first custom toggle button is swapped for DropdownToggleButton
When instead the second custom toggle button is swapped for DropdownToggleButton the issue no longer occurs.

Tried uploading code block but markdown completely messed it up, so here's screenshot:

Image

GitHub repo
Can create one if needed

Desktop (please complete the following information):

  • OS: Windows 11, macOS 15
  • Browser: Chrome, Safari
  • Version Chrome 132, Safari 18.3
@gvreddy04
Copy link
Contributor

@MarcinWatroba Thank you for using BlazorBootstrap.

There is no need to write the ToggleAsync on buttons. Instead, use DropdownToggleButton or a combination of DropdownToggleButton and DropdownToggleButton.

Please follow the demos at BlazorBootstrap Dropdown Demos.

Example 1:

Image

<div class="d-flex gap-2 mb-4">
    <Dropdown Color="DropdownColor.Primary">
        <DropdownToggleButton>Primary</DropdownToggleButton>
        <DropdownMenu>
            <DropdownItem To="#" Type="DropdownItemType.Link">Action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Another action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Something else here</DropdownItem>
        </DropdownMenu>
    </Dropdown>

    <Dropdown Color="DropdownColor.Secondary">
        <DropdownToggleButton>Secondary</DropdownToggleButton>
        <DropdownMenu>
            <DropdownItem To="#" Type="DropdownItemType.Link">Action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Another action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Something else here</DropdownItem>
        </DropdownMenu>
    </Dropdown>

    <Dropdown Color="DropdownColor.Success">
        <DropdownToggleButton>Success</DropdownToggleButton>
        <DropdownMenu>
            <DropdownItem To="#" Type="DropdownItemType.Link">Action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Another action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Something else here</DropdownItem>
        </DropdownMenu>
    </Dropdown>

    <Dropdown Color="DropdownColor.Info">
        <DropdownToggleButton>Info</DropdownToggleButton>
        <DropdownMenu>
            <DropdownItem To="#" Type="DropdownItemType.Link">Action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Another action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Something else here</DropdownItem>
        </DropdownMenu>
    </Dropdown>

    <Dropdown Color="DropdownColor.Warning">
        <DropdownToggleButton>Warning</DropdownToggleButton>
        <DropdownMenu>
            <DropdownItem To="#" Type="DropdownItemType.Link">Action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Another action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Something else here</DropdownItem>
        </DropdownMenu>
    </Dropdown>

    <Dropdown Color="DropdownColor.Danger">
        <DropdownToggleButton>Danger</DropdownToggleButton>
        <DropdownMenu>
            <DropdownItem To="#" Type="DropdownItemType.Link">Action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Another action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Something else here</DropdownItem>
        </DropdownMenu>
    </Dropdown>
</div>

Example 2: Split button

Image

<div class="d-flex gap-2 mb-4">
    <Dropdown Color="DropdownColor.Primary" Split="true">
        <DropdownActionButton>Primary</DropdownActionButton>
        <DropdownToggleButton />
        <DropdownMenu>
            <DropdownItem To="#" Type="DropdownItemType.Link">Action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Another action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Something else here</DropdownItem>
        </DropdownMenu>
    </Dropdown>

    <Dropdown Color="DropdownColor.Secondary" Split="true">
        <DropdownActionButton>Secondary</DropdownActionButton>
        <DropdownToggleButton />
        <DropdownMenu>
            <DropdownItem To="#" Type="DropdownItemType.Link">Action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Another action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Something else here</DropdownItem>
        </DropdownMenu>
    </Dropdown>

    <Dropdown Color="DropdownColor.Success" Split="true">
        <DropdownActionButton>Success</DropdownActionButton>
        <DropdownToggleButton />
        <DropdownMenu>
            <DropdownItem To="#" Type="DropdownItemType.Link">Action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Another action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Something else here</DropdownItem>
        </DropdownMenu>
    </Dropdown>

    <Dropdown Color="DropdownColor.Info" Split="true">
        <DropdownActionButton>Info</DropdownActionButton>
        <DropdownToggleButton />
        <DropdownMenu>
            <DropdownItem To="#" Type="DropdownItemType.Link">Action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Another action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Something else here</DropdownItem>
        </DropdownMenu>
    </Dropdown>

    <Dropdown Color="DropdownColor.Warning" Split="true">
        <DropdownActionButton>Warning</DropdownActionButton>
        <DropdownToggleButton />
        <DropdownMenu>
            <DropdownItem To="#" Type="DropdownItemType.Link">Action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Another action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Something else here</DropdownItem>
        </DropdownMenu>
    </Dropdown>

    <Dropdown Color="DropdownColor.Danger" Split="true">
        <DropdownActionButton>Danger</DropdownActionButton>
        <DropdownToggleButton />
        <DropdownMenu>
            <DropdownItem To="#" Type="DropdownItemType.Link">Action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Another action</DropdownItem>
            <DropdownItem To="#" Type="DropdownItemType.Link">Something else here</DropdownItem>
        </DropdownMenu>
    </Dropdown>
</div>

I hope the demos will help. Let me know if you are still facing any issues.

@gvreddy04 gvreddy04 added area-dropdown-buttons ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. labels Feb 6, 2025
@MarcinWatroba
Copy link
Author

@gvreddy04 Thanks for speedy reply, however, this doesn't solve the problem.

Let's take the dropdown form demo you have on your website as an example. When you copy the code and run it for yourself there's an issue right out of the box. The form has validation, however user doesn't get to see validation messages because as soon as the submit button is clicked, or user clicks anywhere in the dropdown menu for that matter, the dropdown closes.

To fix this I add the AutoCloseBehavior=Outside parameter, now dropdown doesn't close down when user clicks the submit button.

Next step is to close the dropdown on submit only if validation passes and form is submitted successfully. For this to work you need to use the manual dropdown hide method. So you add the Dropdown variable, reference it, and call HideAsync() method in HandleOnValidSubmit().

This works great if you have just one dropdown, try adding another dropdown before this form dropdown, the HideAsync will no longer work because it will trigger on the first dropdown instead on the one that's referenced.

@MarcinWatroba
Copy link
Author

MarcinWatroba commented Feb 6, 2025

Here's example, your edit form sample code combined with the extra bits I added, try expanding Dropdown A, then expand Dropdown form and click on the Submit button, Dropdown A will close instead of Dropdown form:

@page "/"

@using System.ComponentModel.DataAnnotations
@rendermode InteractiveServer

<style>
    .valid.modified:not([type=checkbox]) {
        outline: 1px solid #26b050;
    }

    .invalid {
        outline: 1px solid red;
    }

    .validation-message {
        color: red;
    }
</style>

<Dropdown AutoClose="false">
    <DropdownToggleButton>Dropdown A</DropdownToggleButton>
    <DropdownMenu >
        <DropdownItem Class="hoverHighlight" Type="DropdownItemType.Button">A1</DropdownItem>
    </DropdownMenu>
</Dropdown>

<Dropdown  @ref="_dropdown1" Color="DropdownColor.Secondary" AutoCloseBehavior="DropdownAutoCloseBehavior.Outside">
    <DropdownToggleButton>Dropdown form</DropdownToggleButton>
    <DropdownMenu Class="px-4 py-3" Style="width:480px;">
        <EditForm EditContext="@editContext" OnValidSubmit="HandleOnValidSubmit">
            <DataAnnotationsValidator />

            <div class="mb-3">
                <label class="form-label">Item Price: <span class="text-danger">*</span></label>
                <NumberInput TValue="decimal?" Value="invoice.Price" ValueExpression="() => invoice.Price" ValueChanged="(value) => PriceChanged(value)" Placeholder="Enter price" />
                <ValidationMessage For="@(() => invoice.Price)" />
            </div>

            <div class="mb-3">
                <label class="form-label">Item Discount:</label>
                <NumberInput TValue="decimal?" Value="invoice.Discount" ValueExpression="() => invoice.Discount" ValueChanged="(value) => DiscountChanged(value)" Placeholder="Enter discount" />
                <ValidationMessage For="@(() => invoice.Discount)" />
            </div>

            <div class="mb-3">
                <label class="form-label">Total Amount: <span class="text-danger">*</span></label>
                <NumberInput TValue="decimal?" @bind-Value="invoice.Total" Disabled="true" Placeholder="Enter total" />
                <ValidationMessage For="@(() => invoice.Total)" />
            </div>

            <div class="row">
                <div class="col-md-12 text-right">
                    <Button Type="ButtonType.Button" Color="ButtonColor.Secondary" Class="float-end" @onclick="ResetForm">Reset</Button>
                    <Button Type="ButtonType.Submit" Color="ButtonColor.Success" Class="float-end me-2">Submit</Button>
                </div>
            </div>
        </EditForm>
    </DropdownMenu>
</Dropdown>

@code {
    private Invoice invoice = new();
    private EditContext editContext = default!;
    private Dropdown _dropdown1 = default;

    protected override void OnInitialized()
    {
        editContext = new EditContext(invoice);
        base.OnInitialized();
    }

    protected override void OnParametersSet()
    {
        CalculateToatl();
        base.OnParametersSet();
    }

    private void PriceChanged(decimal? value)
    {
        invoice.Price = value;
        CalculateToatl();
    }

    private void DiscountChanged(decimal? value)
    {
        invoice.Discount = value;
        CalculateToatl();
    }

    private void CalculateToatl()
    {
        var price = invoice.Price.HasValue ? invoice.Price.Value : 0;
        var discount = invoice.Discount.HasValue ? invoice.Discount.Value : 0;
        invoice.Total = price - discount;
    }

    public async Task HandleOnValidSubmit()
    {
        Console.WriteLine($"Price: {invoice.Price}");
        Console.WriteLine($"Discount: {invoice.Discount}");
        Console.WriteLine($"Total: {invoice.Total}");
        await _dropdown1.HideAsync();
    }

    private void ResetForm()
    {
        invoice = new();
        editContext = new EditContext(invoice);
    }

    public class Invoice
    {
        [Required(ErrorMessage = "Price required.")]
        [Range(60, 500, ErrorMessage = "Price should be between 60 and 500.")]
        public decimal? Price { get; set; } = 232M;

        [Range(0, 50, ErrorMessage = "Discount should be between 0 and 50.")]
        public decimal? Discount { get; set; }

        [Required(ErrorMessage = "Amount required.")]
        [Range(10, 500, ErrorMessage = "Total should be between 60 and 500.")]
        public decimal? Total { get; set; }
    }
}

@gvreddy04 gvreddy04 removed the ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. label Feb 6, 2025
@gvreddy04
Copy link
Contributor

@MarcinWatroba Thank you for the detailed information. I will take a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants