Mewriick / Blazor.FlexGrid

GridView component for Blazor
MIT License
199 stars 36 forks source link

conditionally style cells in a column #58

Closed githubfanster closed 5 years ago

githubfanster commented 5 years ago

hello! nice project!

we have a requirement to conditionally style the cells within a column to indicate, for example, different statuses. the styling would involve assigning color values to foreground and background colors depending on the results of some calculations involving a row model's properties. ideally, we'd like to be able to dynamically execute a function (or a lambda) to set these values as a row model's property values change.

could you please let me know if this is possible right now, and if yes, what's the recommended way of doing it?

(right now we are focused only on server-side blazor)

thank you so much for any help and tips!

Mewriick commented 5 years ago

Hello @githubfanster

I think that custom component should solve your problem. FlexGrid allows you to render custom component in cell.

Example of usage rendering BlazorButton from BlazorStrap nuget:

@inject BlazorComponentColumnCollection<WeatherForecast> Collection

@{
    RenderFragment<WeatherForecast> weatherTemp = (weather) => @<BlazorButton>@weather.Summary</BlazorButton>;
    Collection.AddColumnValueRenderFunction(w => w.Summary, weatherTemp);
}

You have to provide into BlazorComponentColumnCollection<TItem> RenderFragmet and FlexGrid will render into column cell your component.

You can aslo define your RenderFragmet in GridConfiguration during startup

RenderFragment<Customer> customerEmailComponent = (Customer customer) => delegate (RenderTreeBuilder rendererTreeBuilder)
{
    var internalBuilder = new BlazorRendererTreeBuilder(rendererTreeBuilder);
    internalBuilder
        .OpenElement(HtmlTagNames.H4)
        .AddContent(customer.Email)
        .CloseElement();
};

builder.Property(c => c.Email)
    .HasBlazorComponentValueRender(customerEmailComponent);

I hope that this way can help you solve your problem. If not we can together find better solution.

githubfanster commented 5 years ago

i tried the following and it seems to work:

in the Server-Side Blazor project (Blazor.Components.Demo.FlexGrid), I made the following change in GridConfigurations.WeatherForecastGridConfiguration:

public void Configure(EntityTypeBuilder builder) { string hot = "hot"; string mild = "mild"; ... // skip for brevity

        builder.Property(e => e.Summary)
            .HasCaption("MySummary")
            .HasOrder(1)
            .HasCompositeValueFormatter(f => $"<span class={(f.TemperatureC > 14 ? hot : mild)}>{f.Summary}</span>");
            //.HasValueFormatter(s => $"{s}!");
            //.HasCompositeValueFormatter(f => $"{f.Summary} <button>{f.TemperatureC}</button> {f.TemperatureF}");

...//skip }

then in the site.css, i added the following classes:

.hot { background-color: red; color: white }

.mild { background-color: green; color: black }

it seemed to work! when i edit the TemperatureC values, the correct CSS values are computed!

do u see any downside to what i did?

githubfanster commented 5 years ago

by the way, in the code i touched, what is the meaning / importance of HasOrder(1) in:

builder.Property(e => e.Summary) .HasCaption("MySummary") .HasOrder(1)

when should I use HasOrder??

thanks again for your help!

Mewriick commented 5 years ago

@githubfanster

yes also HasCompositeValueFormatter it is ok when it is simple scenario like this. For more robust scenarios is better to use custom component.

The .HasOrder(1) is for telling FlexGrid that this property should be renderer as first column. So basically you can define your columns order by this builder method

githubfanster commented 5 years ago

ok. i think i got it! please take a look at below changes and let me know if i missed something. thanks!

the cell i'm interested in rendering in GridConfigurations.WeatherForecastGridConfiguration is declared to be simply:

builder.Property(e => e.Summary) .HasCaption("MySummary") .HasOrder(1);

then in Grid.razor, i have the changed segment:

... @using BlazorStrap @inject WeatherForecastService WeatherService @inject BlazorComponentColumnCollection Collection @page "/grid"

Weather forecast

@**@

the RenderFragment is as the code you've shown above, except that the Class attribute of the BlazorButton is bound to my MyCssFunc below. @{
RenderFragment weatherTemp = (weather) => @<BlazorButton Class=@MyCssFunc(weather)>@weather.Summary</BlazorButton>;

    Collection.AddColumnValueRenderFunction(w => w.Summary, weatherTemp);
}

@code { CollectionTableDataAdapter dataAdapter;

protected Func<WeatherForecast, string> MyCssFunc = (WeatherForecast w) =>
{
    return w.TemperatureC > 14 ? "hot" : "mild";
};

//... skipped }

githubfanster commented 5 years ago

closing this ... thanks for helping!