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

Blazor doesn't re-render html when property is used in CSS classes #31585

Closed
techgems opened this issue Apr 7, 2021 · 6 comments
Closed

Blazor doesn't re-render html when property is used in CSS classes #31585

techgems opened this issue Apr 7, 2021 · 6 comments
Labels
area-blazor Includes: Blazor, Razor Components ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. Status: Resolved

Comments

@techgems
Copy link

techgems commented Apr 7, 2021

So I have this code:

<div>
        <div class="@ (Base64Images.Count == 0 ? "" : "hidden")">
            <label for="file-upload">
                <span>Upload a file</span>
                <InputFile OnChange="HandleChange" id="file-upload" name="file-upload" />
            </label>
        </div>
        <div class="@ (Base64Images.Count > 0 ? "block" : "hidden")">
            @ foreach(var image in Base64Images)
            {
                <img src="@image" />
            }
        </div>
</div>
``` @code { public IReadOnlyList BrowserFiles { get; protected set; } = new List();
private List<string> Base64Images { get; set; } = new List<string>();

private async Task<bool> HandleChange(InputFileChangeEventArgs e)
{
    IReadOnlyList<IBrowserFile> fileList;

    BrowserFiles = new List<IBrowserFile> { e.File };

    await BrowserFilesToBase64Images();

    return true;
}

private async Task<bool> BrowserFilesToBase64Images()
{
    foreach(var image in BrowserFiles)
    {
        if(image != null)
        {
            var format = "image/png";
            var buffer = new byte[image.Size];
            await image.OpenReadStream().ReadAsync(buffer);
            Base64Images.Add($"data:{format};base64,{Convert.ToBase64String(buffer)}");
        }
    }

    return true;
}

What is happening is that Base64Images is changing and so is the count, but the component won't re-render nor change the classes of the HTML elements. If I turn the conditional class statements into if statements to change the DOM it works, but in my case I only need to change the classes for it to work with my CSS and to avoid an error that happens when you try to upload a file, but the InputFile is no longer visible.

I attempted to create a BlazorFiddle for this, but the app is not working properly. Using Asp.net core 5 with hosted Blazor WASM.

Thanks.

@javiercn javiercn added the area-blazor Includes: Blazor, Razor Components label Apr 7, 2021
@javiercn
Copy link
Member

javiercn commented Apr 7, 2021

@techgems thanks for contacting us.

I would suggest you use @key on each image element so that the change tracking system can get a better understanding of the changes you are making.

Besides that, we would strongly discourage you from using base64 encoded images and to serve those from an endpoint instead. By using base64 encoded images your application is likely going to allocate large memory buffers and that is likely going to have a negative impact on the performance of your app.

As for the code in question I don't see any reason why it shouldn't cause a re-render, so we need to take a look at it.

@captainsafia
Copy link
Member

@techgems Can you try removing the additional spaces in your conditional statements. From:

<div class="@ (Base64Images.Count == 0 ? "" : "hidden")">

to

<div class="@(Base64Images.Count == 0 ? "" : "hidden")">

@captainsafia captainsafia added the ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. label Apr 7, 2021
@ghost ghost added the Status: Resolved label Apr 7, 2021
@techgems
Copy link
Author

techgems commented Apr 7, 2021

@javiercn

@techgems thanks for contacting us.

I would suggest you use @key on each image element so that the change tracking system can get a better understanding of the changes you are making.

It turns out the problem wasn't just a re-rendering issue exactly. It was a bit more complex than that. The code I sent here is a very simplified version of what I am actually using, and once I ran the simplified version, the rendering worked with no issues.

In my code I use a component for the CreateForm and another one for the UpdateForm. Both had the same id, name and labels in the fields because the fields are the same. Because of that, I figure InputFile didn't know which input to add the images to. I do know InputFile does a JS Interop under the hood.

Because of the structure of my CSS (Tailwind) I do something unusual in which the input is hidden by default, but I trigger the InputFile when the user clicks the label. It was a big mix of two labels pointing at the same ID, but the ID existed twice in the DOM.

I added a guid part to the id and name of the InputFile and that solved the issues with rendering and matching.

I can post a complete sample if it's of any use, as this issue was very counter intuitive and hard to debug. The rendering wasn't the problem, but what was happening is that the component didn't render because of the root cause and internal conflict in the usage of InputFile.

Blazor didn't complain from what I was doing and all I got was 2 console logs from the browser telling me to make sure my Ids were unique in the HTML. I don't think this is a common problem, but it's probably something Blazor should warn you about, not just the browser.

TLDR: Using 2 InputFile components with the same id creates a race condition that hinders proper rendering and there is no warning or error given by the runtime when this happens.

@techgems
Copy link
Author

techgems commented Apr 7, 2021

@captainsafia

@techgems Can you try removing the additional spaces in your conditional statements. From:

<div class="@ (Base64Images.Count == 0 ? "" : "hidden")">

to

<div class="@(Base64Images.Count == 0 ? "" : "hidden")">

The spaces there are only because github thinks I am tagging a user if I don't leave the space, since in markdown the @ is for tagging users.

@techgems
Copy link
Author

techgems commented Apr 7, 2021

@javiercn

Besides that, we would strongly discourage you from using base64 encoded images and to serve those from an endpoint instead. By using base64 encoded images your application is likely going to allocate large memory buffers and that is likely going to have a negative impact on the performance of your app.

As for the code in question I don't see any reason why it shouldn't cause a re-render, so we need to take a look at it.

The base64 part is only being used right now to display the image before it is sent to the server, the idea with the component is that it can render both images from the network as well as previews from before it is sent to the server.

Is there a better way to achieve this?

@ghost
Copy link

ghost commented Apr 8, 2021

This issue has been resolved and has not had any activity for 1 day. It will be closed for housekeeping purposes.

See our Issue Management Policies for more information.

@ghost ghost closed this as completed Apr 8, 2021
@ghost ghost locked as resolved and limited conversation to collaborators May 8, 2021
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. Status: Resolved
Projects
None yet
Development

No branches or pull requests

3 participants