Search code examples
blazorblazor-client-siderazor-components

How to make this Bootstrap Carousel into reusable Razor component to use in Blazor app


I am building a razor component for use in a Blazor client-side app. I am wondering how i can make the images used in the component dynamic, and set the images in the page code-block where i call the component.

Here is my Carousel.razor component

@inject IJSRuntime JsRuntime
@namespace BlazorFrontend.Components

<div id="carouselExampleIndicators" class="carousel slide" data-ride="carousel">
    <ol class="carousel-indicators">
        <li data-target="#carouselExampleIndicators" data-slide-to="0"  class="active"></li>
        <li data-target="#carouselExampleIndicators" data-slide-to="1" ></li>
        <li data-target="#carouselExampleIndicators" data-slide-to="2" ></li>
    </ol>
    <div class="carousel-inner">
        <div class="carousel-item active">
            <img class="d-block w-100" src="pictures/foo.jpg" alt="First slide">
        </div>
        <div class="carousel-item">
            <img class="d-block w-100" src="pictures/bar.jpg" alt="Second slide">
        </div>
        <div class="carousel-item">
            <img class="d-block w-100" src="pictures/foobar.jpg" alt="Third slide">
        </div>
    </div>
    <a class="carousel-control-prev" role="button" id="carouselExampleIndicators-prev" data-slide="prev">
        <span class="carousel-control-prev-icon" aria-hidden="true"></span>
        <span class="sr-only">Previous</span>
    </a>
    <a class="carousel-control-next" id="carouselExampleIndicators-next" role="button" data-slide="next">
        <span class="carousel-control-next-icon" aria-hidden="true"></span>
        <span class="sr-only">Next</span>
    </a>
</div>

@code{
    protected override async Task OnInitializedAsync()
        {
            foreach (System.Collections.DictionaryEntry de in Environment.GetEnvironmentVariables())
            {
                System.Console.WriteLine("ENVVAR: key=" + de.Key + " value=" + de.Value);
                System.Console.WriteLine(Environment.GetEnvironmentVariable("ASSETS"));
            }
        }
        private string AssetsEndpoint = Environment.GetEnvironmentVariable("ASSETS");
        bool firstRender = true;

        protected async override Task OnAfterRenderAsync(bool firstRender)
        {
            if (firstRender)
            {
                await JsRuntime.InvokeAsync<object>("initializeCarousel");
                firstRender = false;
            }
        }
}

Ideally, i would have liked to do something like this:

  <img class="d-block w-100" src=@slide1 alt="First slide">

code{
    string slide1;
}

and in the code block of the page i am using the component in:

<Carousel></Carousel>

code{
    slide1 = "pictures/foo.jpg"
}

for example, but of course, that isn't going to work. What is the right/Blazor way of doing this?


Solution

  • BlazorStrap basically does this already, and their Carousels sample page also shows a fairly Blazor-esque way of doing it. From the first example:

    <BSCarousel NumberOfItems="@items.Count">
        <BSCarouselIndicators NumberOfItems="@items.Count">
        <div class="carousel-inner">
            @for (int i = 0; i <<> items.Count; i++)
            {
                <BSCarouselItem src="@items[i].Source" alt="@items[i].Alt">
            }
        </div>
        <BSCarouselControl CarouselDirection="CarouselDirection.Previous" NumberOfItems="@items.Count">
        <BSCarouselControl CarouselDirection="CarouselDirection.Next" NumberOfItems="@items.Count">
    </BSCarousel>
    

    Let's simplify that a little further first:

    <BSCarousel NumberOfItems="@items.Count">
        <div class="carousel-inner">
            @for (int i = 0; i <<> items.Count; i++)
            {
                <BSCarouselItem src="@items[i].Source" alt="@items[i].Alt">
            }
        </div>
    </BSCarousel>
    

    The key aspects to note here:

    • there's a Blazor component BSCarousel that semantically acts as the container, and can also interact with its child components
    • there's another Blazor component BSCarouselItem for each item added. This approach makes you fairly flexible and might be what you were asking. More tiles in the carousel? Just add another item. The parent BSCarousel component, then, needs to deal with arranging them.
    • Razor syntax like @for and @items.Count is used to easily intersperse C# where needed, but really, C# is used here more as a template language
    • in contrast, a @code { } block isn't needed for this at all (note, though, that everything outside that block eventually gets transformed into C# anyway)

    I'm guessing your main question was: how do I put multiple items in a component? By making another component that handles a single item. :-)