Search code examples
javascriptlaraveltemplatesvuejs3vue3-carousel

Vue3 Carousel as ES module in Laravel blade template


I am using Laravel 11 and using blade templates and I am trying to move away from compiling all my vuejs components into one file and have multiple smaller files that are relevant to the page.

I am trying to convert my carousel component but I'm not sure how to do it properly.

I'm getting Undefined constant "item" error which makes sense to me but I'm not sure the proper way to write the code.

This is my blade template

<div id="release-carousel">
  <Carousel v-bind="carouselConfig">
    <Slide v-for="(item, index) in releases" :key="index">
      <div class="text-center p-2 bg-white">
        <div class="mb-4">
          <a href="{{ route('releases.show', ['id' => item.encoded_id, 'artist' => item.name_slug, 'title' => item.title_slug]) }}">
            <picture>
              <source srcset="{{ asset('images/releases/thumbs/' . item.filename . '.webp') }}" type="image/webp" />
              <source srcset="{{ asset('images/releases/thumbs/' . item.filename . '.jpg') }}" type="image/jpeg" />
              <img 
                class="mx-auto img-fluid p-1 lazy"
                data-src="{{ asset('images/releases/thumbs/' . item.filename . '.jpg') }}"
                alt="{{ item.name }} - {{ item.title }}"
                width="300" height="300"
            />
            </picture> 
          </a>         
        </div>
      </div>
    </slide>
  </carousel>
</div>

@push('css')
<link rel="stylesheet" href="{{ asset('css/carousel.css') }}">
@endpush

@push('scripts')
<script type="module">
  import { createApp } from 'vue'
  import vue3Carousel from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm'
  import ReleaseCarousel from './release-carousel.js'

  createApp(ReleaseCarousel)
    .mount('#release-carousel')
</script>
@endpush

and this is my vue module

import { Carousel, Slide } from 'vue3Carousel'

export default {
  setup() {
    const carouselConfig = {
      wrapAround: true,
      itemsToShow: 3,
      snapAlign: 'center',
      autoplay: '2000',
      breakpoints: {
        600: {
          itemsToShow: 1,
          snapAlign: 'start'
        },
        1000: {
          itemsToShow: 2,
          snapAlign: 'start'
        },
        1200: {
          itemsToShow: 3,
          snapAlign: 'start'
        }
      }
    }

    return { carouselConfig }
  },
  components: {
    Carousel,
    Slide
  }
}

Again, I'm not sure if this is the proper way to do it. Any help or advice much appreciated


Solution

  • You are on the right track. The main issue I see here is you are trying to use vue variables inside Blade expressions {{ }}

    I suggest you use v-bind for this task. Pass the releases data from Laravel to Vue and then you can dynamically access them.

    Below attaching an example:

    <div id="release-carousel">
      <carousel v-bind="carouselConfig">
        <slide v-for="(item, index) in releases" :key="index">
          <div class="text-center p-2 bg-white">
            <div class="mb-4">
              <a :href="'/releases/' + item.encoded_id + '/' + item.name_slug + '/' + item.title_slug">
                <picture>
                  <source :srcset="'/images/releases/thumbs/' + item.filename + '.webp'" type="image/webp" />
                  <source :srcset="'/images/releases/thumbs/' + item.filename + '.jpg'" type="image/jpeg" />
                  <img
                    class="mx-auto img-fluid p-1 lazy"
                    :data-src="'/images/releases/thumbs/' + item.filename + '.jpg'"
                    :alt="item.name + ' - ' + item.title"
                    width="300"
                    height="300"
                  />
                </picture>
              </a>
            </div>
          </div>
        </slide>
      </carousel>
    </div>
    
    @push('css')
    <link rel="stylesheet" href="{{ asset('css/carousel.css') }}">
    @endpush
    
    @push('scripts')
    <script type="module">
      import { createApp } from 'vue'
      import vue3Carousel from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm'
      import ReleaseCarousel from './release-carousel.js'
    
      const releasesData = @json($releases);
    
      createApp(ReleaseCarousel, { releases: releasesData }).mount('#release-carousel')
    </script>
    @endpush

    And then on your vue component you would do something like this:

    import { Carousel, Slide } from 'vue3Carousel'
    
    export default {
      props: {
        releases: {
          type: Array,
          required: true
        }
      },
      setup(props) {
        const carouselConfig = {
          wrapAround: true,
          itemsToShow: 3,
          snapAlign: 'center',
          autoplay: 2000,
          breakpoints: {
            600: {
              itemsToShow: 1,
              snapAlign: 'start'
            },
            1000: {
              itemsToShow: 2,
              snapAlign: 'start'
            },
            1200: {
              itemsToShow: 3,
              snapAlign: 'start'
            }
          }
        }
    
        return { carouselConfig, releases: props.releases }
      },
      components: {
        Carousel,
        Slide
      }
    }

    Also very important to check if your controller on laravel is actually passing the release data to the view. your controller would look something like this:

    public function index()
    {
        $releases = Release::all()->map(function ($release) {
            return [
                'encoded_id' => $release->id,
                'name_slug' => Str::slug($release->artist_name),
                'title_slug' => Str::slug($release->title),
                'filename' => $release->filename,
                'name' => $release->artist_name,
                'title' => $release->title,
            ];
        });
    
        return view('releases.index', compact('releases'));
    }

    this way you would have the communication dynamic and flexible.