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

fix: FluentListBox not binding the selected option correctly? #3275

Open
PascalVorwerk opened this issue Jan 30, 2025 · 0 comments
Open

fix: FluentListBox not binding the selected option correctly? #3275

PascalVorwerk opened this issue Jan 30, 2025 · 0 comments
Labels
triage New issue. Needs to be looked at

Comments

@PascalVorwerk
Copy link

PascalVorwerk commented Jan 30, 2025

🐛 Bug Report

I have been trying to make a panel where a user selects a employee to pick from and then whenever the user presses the primary action the result should hold this selected employee to start the adding process. However, whenever i try to do this, the first one in the list always gets 'selected' even though the visuals appear to show differently.

💻 Repro or Code Sample

@implements IDialogContentComponent<SelectEmployeePanelData>
@using MCC.BC.Business.Samples.Models.Employees
@inherits MccComponentBase

<FluentMessageBarProvider Section="PANEL_SECTION_TOP" MaxMessageCount="1"/>

<FluentDialogHeader>
    <FluentLabel Typo="Typography.Header">Selecteer een medewerker</FluentLabel>
</FluentDialogHeader>

<FluentDialogBody>
    <FluentStack Orientation="Orientation.Vertical" VerticalGap="20">
        <FluentSearch @bind-Value="@SearchText" Immediate ImmediateDelay="200"/>
        <FluentListbox Width="100%" Height="400px"
                       Id="employee-listbox-panel"
                       TOption="GetEmployeeResponse"
                       @bind-SelectedOption="@Content.SelectedEmployee"
                       Items="@FilteredEmployees" 
                       OptionDisabled="@((o) => o.Projects.Any(x => x.Id == Content?.Project.Id))">
            <OptionTemplate>
                @context.FirstName @context.LastName, Uursalaris: @context.HourlyRate
            </OptionTemplate>
        </FluentListbox>
    </FluentStack>
</FluentDialogBody>
using MCC.BC.Business.Samples.Models.Employees;
using MCC.BC.Business.Samples.Models.Projects;
using Microsoft.AspNetCore.Components;

namespace MCC.BC.Portal.Samples.Client.Components.Panels;

public partial class SelectEmployeePanel : MccComponentBase
{
    [Parameter] public SelectEmployeePanelData Content { get; set; } = null!;

    private IQueryable<GetEmployeeResponse>? _employees;
    private IQueryable<GetEmployeeResponse> FilteredEmployees => _employees?
        .Where(x => x.FirstName.Contains(SearchText) || x.LastName.Contains(SearchText)) ?? Enumerable.Empty<GetEmployeeResponse>().AsQueryable();
    
    private string SearchText { get; set; } = string.Empty;
    protected override async Task OnInitializedAsync()
    {
        await GetAllEmployeesAsync();
    }
    
    private async Task GetAllEmployeesAsync()
    {
        var result = await GetAsync<List<GetEmployeeResponse>>("api/employees/all?includeProjects=true");

        _employees = result?.AsQueryable();
    }
}

public class SelectEmployeePanelData
{
    public GetProjectResponse Project { get; set; } = null!;
    public GetEmployeeResponse? SelectedEmployee { get; set; }
}
using MCC.BC.Business.Samples.Models.Projects;

namespace MCC.BC.Business.Samples.Models.Employees;

public class GetEmployeeResponse
{
    public Guid Id { get; set; }
    
    public string FirstName { get; set; } = null!;
    
    public string LastName { get; set; } = null!;
    
    public decimal HourlyRate { get; set; }
    
    public IEnumerable<GetSkillResponse> Skills { get; set; } = new List<GetSkillResponse>();
    public IEnumerable<GetProjectResponse> Projects { get; set; } = new List<GetProjectResponse>();
}
using MCC.BC.Business.Samples.Models.Employees;

namespace MCC.BC.Business.Samples.Models.Projects;

public class GetProjectResponse
{
    public Guid Id { get; set; }
    
    public string Name { get; set; } = null!;
    
    public DateTime StartDate { get; set; }
    
    public DateTime EndDate { get; set; }
    
    public decimal Budget { get; set; }
    
    public List<GetSkillResponse> RequiredSkills { get; set; } = new ();
    
    public List<GetEmployeeResponse> Employees { get; set; } = new ();
}
@page "/projects/{id:guid}"
@using MCC.BC.Business.Samples.Models.Employees
@inherits MCC.BC.Portal.Samples.Client.Components.MccComponentBase

<PageTitle>
    Project bewerken
</PageTitle>


@if (_project == null)
{
    <FluentStack Style="width: 100%" Orientation="Orientation.Horizontal">
        <FluentProgressRing/>
    </FluentStack>
}
else
{
    <FluentStack Orientation="Orientation.Vertical">
        <FluentLabel Typo="Typography.H1">@_project.Name</FluentLabel>
        <FluentStack>
            <FluentBreadcrumb>
                <FluentBreadcrumbItem Href="/projects">
                    Overzicht projecten
                </FluentBreadcrumbItem>
                <FluentBreadcrumbItem>
                    Bewerken
                </FluentBreadcrumbItem>
            </FluentBreadcrumb>
        </FluentStack>
        
        <FluentGrid>
            <FluentGridItem>
                <FluentLabel Typo="Typography.Subject" Color="Color.Accent">Project gegevens</FluentLabel>
                <div class="project-item">
                    <FluentEditForm Model="@_updateProjectRequest" OnValidSubmit="@OnSaveProjectAsync">
                        <FluentValidationValidator/>

                        <FluentStack Orientation="Orientation.Vertical" VerticalGap="5">
                            <FluentTextField Label="Project naam" @bind-Value="@_updateProjectRequest.Name"/>
                            <FluentValidationMessage For="@(() => _updateProjectRequest.Name)"/>

                            <FluentDatePicker Label="Startdatum" @bind-Value="@_updateProjectRequest.StartDate"/>
                            <FluentValidationMessage For="@(() => _updateProjectRequest.StartDate)"/>

                            <FluentDatePicker Label="Einddatum" @bind-Value="@_updateProjectRequest.EndDate"/>
                            <FluentValidationMessage For="@(() => _updateProjectRequest.EndDate)"/>

                            <FluentNumberField Label="Budget" @bind-Value="@_updateProjectRequest.Budget"/>
                            <FluentValidationMessage For="@(() => _updateProjectRequest.Budget)"/>

                            <FluentButton Type="ButtonType.Submit" Appearance="Appearance.Accent" IconStart="@(new Icons.Filled.Size20.Save())">
                                Opslaan
                            </FluentButton>

                        </FluentStack>
                    </FluentEditForm>
                </div>
            </FluentGridItem>
            
            <FluentGridItem>
                <FluentLabel Typo="Typography.Subject" Color="Color.Accent">Huidige medewerkers</FluentLabel>
                <div class="project-item">
                    <FluentStack Orientation="Orientation.Vertical" VerticalGap="5">
                        <FluentListbox TOption="GetEmployeeResponse"
                                       Id="employee-listbox"
                                       Items="@_project.Employees" 
                                       Height="400px" Width="100%">
                            <OptionTemplate>
                                <FluentIcon Icon="Icons.Regular.Size12.Dismiss" Slot="end" OnClick="@(() => RemoveEmployeeAsync(context))"/>
                                @context.FirstName @context.LastName, Uursalaris: $@context.HourlyRate
                            </OptionTemplate>
                        </FluentListbox>
                        <FluentButton OnClick="OpenEmployeesPanelAsync" Appearance="Appearance.Accent" IconStart="@(new Icons.Filled.Size20.Save())">
                            Voeg medewerker toe
                        </FluentButton>
                    </FluentStack>
                </div>
            </FluentGridItem>
            
            <FluentGridItem>
                
            </FluentGridItem>
        </FluentGrid>
 
    </FluentStack>
}
using MCC.BC.Business.Samples.Models.Employees;
using MCC.BC.Business.Samples.Models.Projects;
using MCC.BC.Portal.Samples.Client.Components;
using MCC.BC.Portal.Samples.Client.Components.Panels;
using Microsoft.AspNetCore.Components;
using Microsoft.FluentUI.AspNetCore.Components;

namespace MCC.BC.Portal.Samples.Client.Pages.Projects;

public partial class Edit : MccComponentBase
{
    [Parameter] public Guid Id { get; set; }

    private GetProjectResponse? _project;
    private UpdateProjectRequest _updateProjectRequest = new ();
    private IDialogReference? _dialog;
    
    protected override async Task OnInitializedAsync()
    {
        await GetProjectAsync();
    }
    
    
    private async Task GetProjectAsync()
    {
        var result = await GetAsync<GetProjectResponse>($"api/projects/{Id}");
        
        if (result != null)
        {
            _project = result;
            _updateProjectRequest = new UpdateProjectRequest
            {
                Name = _project.Name,
                StartDate = _project.StartDate,
                EndDate = _project.EndDate,
                Budget = _project.Budget,
            };
        }
    }
    
    private async Task OnSaveProjectAsync()
    {
        var success = await PutAsync($"api/projects/{Id}", _updateProjectRequest);

        if (success)
        {
            await GetProjectAsync();
        }
    }
    
    private async Task OnDeleteProjectAsync()
    {
        var success = await DeleteAsync($"api/projects/{Id}");
        
        if (success)
        {
            Navigation.NavigateTo("projects");
        }
    }

    private async Task RemoveEmployeeAsync(GetEmployeeResponse context)
    {
        if (_project != null)
        {
            var success = await DeleteAsync($"api/projects/{_project.Id}/employees/{context.Id}");

            if (success)
            {
                await GetProjectAsync();
            }
        }
    }

    private async Task OpenEmployeesPanelAsync()
    {
        if (_project != null)
        {
            MessageService.Clear();
            _dialog = await DialogService.ShowPanelAsync<SelectEmployeePanel>(new SelectEmployeePanelData()
                {
                    Project = _project,
                    SelectedEmployee = null
                },
                new DialogParameters<GetProjectResponse>()
                {
                    Content = _project,
                    PrimaryAction = "Voeg toe",
                    SecondaryAction = "Annuleer",
                });
            
            var result = await _dialog.Result;
            
            if (!result.Cancelled)
            {
                var panelData = result.Data as SelectEmployeePanelData;
                
                var success = await PostAsync($"api/projects/{_project.Id}/employees/{panelData.SelectedEmployee!.Id}", new object());

                if (success)
                {
                    await GetProjectAsync();
                }
            }
        }
    }
}

🤔 Expected Behavior

Whenever i select a 'Employee' in my panel and press the primary action button, i would expect this person to be returned because of the 2 way binding on the data.

😯 Current Behavior

However, it seems to always select the first one in the list! Even if that option is for example disabled. This is in debug mode right after i have selected Christina as my employee:

Image

As you can see, the first person in the list Shari, is always the person returned from the panel.

🌍 Your Environment

I work in Chrome, using .NET 9 and the latest packages of FluentUI (4.11.3) . My project is a Blazor web app, with global interactivity set to WebAssembly and i have prerendering disabled.

<Routes @rendermode="new InteractiveWebAssemblyRenderMode(prerender: false)"/>
@microsoft-github-policy-service microsoft-github-policy-service bot added the triage New issue. Needs to be looked at label Jan 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
triage New issue. Needs to be looked at
Projects
None yet
Development

No branches or pull requests

1 participant