Sunday 15 November 2020

blazor force full render instead of differential render

I am trying to display images using this masonry js package but I am having difficulties.

I have an image upload form. The user clicks a button to bind images to Model.Images, and then they get displayed on the page. The user can bind multiple batches of images before submitting the form. Each time the user binds another batch of images to Model.Images, the previous batch is persisted - that is to say, I do not truncate Model.Images before binding the images from the next batch.

The problem I am seeing is: the masonry js script needs to be re-triggered every time Model.Images gets added to, because the js only applies to the most recently-rendered view. However, it seems every time I force StateHasChanged(); the images from the most recently-added batch overlap the previous images, so I see this:

after first batch of images is bound after first batch of images is bound

after second batch of images is bound after second batch of images is bound

I think this has something to do with differential rendering. If I move the images to a helper object, truncate Model.Images, force StateHasChanged(), and then repopulate Model.Images, the images get displayed side by side properly. I most likely am just approaching this incorrectly. Can someone advise me how to achieve what I want without my hacky implementation? I pasted a simplified version of my code below - this the implementation that is producing the bad results pictured above.

razor component (the TriggerMasonry() event gets called after Model.Images gets appended to.)

@for (int i = 0; i < Model.Images.Count; j++)
{
    var iCopy = i; //see https://stackoverflow.com/a/56426146/323447
    var uri = $"data:{Model.Images[iCopy].ImageMimeType};base64,{Convert.ToBase64String(Model.Images[iCopy].ImageDataLarge.ToArray())}";

    <div class="grid-item" @ref=MasonryElement>
        <div class="card m-2 shadow-sm" style="position:relative;">
            <img src="@uri" class="card-img-top" alt="..." loading="lazy">
        </div>
    </div>
}


@code {
    ElementReference MasonryElement;

    private async void TriggerMasonry()
    {
        if (Model.Images.Where(x => x.ImageData != null).Any())
        {
            StateHasChanged();
            await JSRuntime.InvokeVoidAsync("initMasonryDelay", MasonryElement);
            StateHasChanged();
        }
    }
}

js

function initMasonryDelay() {

    setTimeout(function () {

        // init Masonry
        var $grid = $('.grid').masonry({
            itemSelector: '.grid-item',
            percentPosition: true,
            columnWidth: '.grid-sizer'
        });

        // layout Masonry after each image loads
        $grid.imagesLoaded().progress(function () {
            $grid.masonry();
        });

    }, 150); //milliseconds
}

css

* {
    box-sizing: border-box;
}

.grid:after {
    content: '';
    display: block;
    clear: both;
}

.grid-sizer,
.grid-item {
    width: 33.333%;
}

@media (max-width: 575px) {
    .grid-sizer,
    .grid-item {
        width: 100%;
    }
}

@media (min-width: 576px) and (max-width: 767px) {
    .grid-sizer,
    .grid-item {
        width: 50%;
    }
}

/* To change the amount of columns on larger devices, uncomment the code below */
@media (min-width: 768px) and (max-width: 991px) {
    .grid-sizer,
    .grid-item {
        width: 33.333%;
    }
}

@media (min-width: 992px) and (max-width: 1199px) {
    .grid-sizer,
    .grid-item {
        width: 25%;
    }
}

@media (min-width: 1200px) {
    .grid-sizer,
    .grid-item {
        width: 20%;
    }
}


.grid-item {
    float: left;
}

    .grid-item img {
        display: block;
        max-width: 100%;
    }


from blazor force full render instead of differential render

No comments:

Post a Comment