显示使用 AlpineJS 过滤的列表中的项目数

Display number of items in list filtered with AlpineJS

提问人:DamsFX 提问时间:9/29/2023 更新时间:9/29/2023 访问量:81

问:

我有一个可以应用于图像列表的过滤器系统。 我使用 AlpineJS 只保留过滤器处于活动状态时要显示的项目。

一切正常,但我在显示与活动过滤器对应的项目数量时遇到问题。

例如,我希望能够显示 X(过滤图像)/总数。

我尝试在 Alpine 完成修改 DOM 后使用该方法进行计算,但没有成功。$nextTick()

https://codepen.io/damsfx/pen/WNLyaWM

JavaScript的 列表 滤波 alpine.js

评论


答:

1赞 Calvin Furano 9/29/2023 #1

您可以在您所在的州添加其他属性。这可以是“getter”属性。每次访问它时,它都会计算结果的数量。numResults

          get numResults () {
            return this.images.filter(i => this.is_selected(i.tags)).length
          },

document.addEventListener('alpine:init', () => {
      Alpine.data('filterList', () => ({

          // Availables filters
          filters: [
            { id: 'burger', label: 'Burgers' },
            { id: 'pizza', label: 'Pizzas' },
            { id: 'drink', label: 'Drinks' },
          ],
        
          // Images set
          images: [
            { src: 'https://images.unsplash.com/photo-1571091718767-18b5b1457add?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=300&h=300&q=80', tags: ['burger'] },
            { src: 'https://images.unsplash.com/photo-1544145945-f90425340c7e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8OHx8ZHJpbmt8ZW58MHx8MHx8fDA%3D&auto=format&fit=crop&w=300&h=300&q=60', tags: ['drink'] },
            { src: 'https://images.unsplash.com/photo-1565364909484-74ef98a238bf?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTB8fHBpenphJTIwd2luZXxlbnwwfHwwfHx8MA%3D%3D&auto=format&fit=crop&w=300&h=300&q=60', tags: ['pizza', 'drink'] },
            { src: 'https://images.unsplash.com/photo-1457460866886-40ef8d4b42a0?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8NDl8fGJ1cmdlcnN8ZW58MHx8MHx8fDA%3D&auto=format&fit=crop&w=300&h=300&q=60', tags: ['burger', 'drink'] },
            { src: 'https://images.unsplash.com/photo-1551024709-8f23befc6f87?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTJ8fGRyaW5rfGVufDB8fDB8fHww&auto=format&fit=crop&w=300&h=300&q=60', tags: ['drink'] },
            { src: 'https://images.unsplash.com/photo-1644447381354-662bfd7c78f2?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTR8fGJ1cmdlcnMlMjBkcmlua3xlbnwwfHwwfHx8MA%3D%3D&auto=format&fit=crop&w=300&h=300&q=60', tags: ['burger', 'drink'] },
            { src: 'https://images.unsplash.com/photo-1544029048-b78834e2c277?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MzR8fHBpenphJTIwZHJpbmt8ZW58MHx8MHx8fDA%3D&auto=format&fit=crop&w=300&h=300&q=60', tags: ['pizza', 'drink'] },
            { src: 'https://images.unsplash.com/photo-1556994302-558991b74265?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTh8fGRyaW5rfGVufDB8fDB8fHww&auto=format&fit=crop&w=300&h=300&q=60', tags: ['drink']},
            { src: 'https://images.unsplash.com/photo-1513104890138-7c749659a591?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mnx8cGl6emF8ZW58MHx8MHx8fDA%3D&auto=format&fit=crop&w=300&h=300&q=60', tags: ['pizza'] },
            { src: 'https://images.unsplash.com/photo-1572802419224-296b0aeee0d9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTV8fGJ1cmdlcnN8ZW58MHx8MHx8fDA%3D&auto=format&fit=crop&w=300&h=300&q=60', tags: ['burger'] },
            { src: 'https://images.unsplash.com/photo-1560526860-1f0e56046c85?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MjJ8fGRyaW5rfGVufDB8fDB8fHww&auto=format&fit=crop&w=300&h=300&q=60', tags: ['drink'] },
            { src: 'https://images.unsplash.com/photo-1584365685547-9a5fb6f3a70c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8NjB8fHBpenphfGVufDB8fDB8fHwy&auto=format&fit=crop&w=300&h=300&q=60', tags: ['pizza'] }
          ],

          // active tags filters
          tags: [],
        
          get numResults () {
            return this.images.filter(i => this.is_selected(i.tags)).length
          },

          init() {},

          // add/remove tag to filters
          toggle(tag) {
              if (this.tags.includes(tag)) {
                  this.tags = this.tags.filter(x => x != tag)
              }
              else {
                  this.tags.push(tag)
              }
          },

          // check if tag is in active tag filters
          is_active(tag) {
              return this.tags.includes(tag)
          },

          // check if image's tags are in active tag filters
          is_selected(t) {
              var flag = false;
              if(this.tags.length == 0) {
                  return true;
              }
              for(i=0; i<t.length; i++) {
                  if(this.tags.includes(t[i])) {
                      flag = true;
                  }
              }
              return flag;
          },
        
      }))
  });
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
<section class="max-w-7xl p-6">
  <h1 class="text-3xl md:text-6xl tracking-tight leading-none font-extrabold text-cyan-500 mb-4">Filter item list with AlpineJS</h1>
  <div x-data="filterList">
    
    <div class="py-6">
      <div class="flex gap-4 my-2">
        <span>Click to filter list on : </span>
        <template x-for="filter in filters" :key="filter.id">
          <button class="px-1 py-0 bg-stone-200 rounded"
                  @click="toggle(filter.id)" 
                  :class="{'bg-cyan-500 text-white':is_active(filter.id)}" 
                  x-text="filter.label"></button>
        </template>
        <div>
          Results: <span x-text="numResults"></span> of <span x-text="images.length"></span>
        </div>
      </div>
      <p class="text-xs">only images with the selected tag(s) will be displayed - hover images to see their associated tags</p>
    </div>

    <div class="grid grid-cols-4 gap-4">
      <template x-for="(image, index) in images" :key="index">
        <div x-show="is_selected(image.tags);" class="group relative aspect-1" x-transition:enter="transition ease-out duration-500" x-transition:enter-start="opacity-0 scale-10" x-transition:enter-end="opacity-100 scale-100" x-transition:leave="transition ease-in duration-500" x-transition:leave-start="opacity-100 scale-100" x-transition:leave-end="opacity-0 scale-10">
          <img :src="image.src" class="object-cover" />  
          <div x-text="image.tags.join(', ')" class="absolute top-[75%] left-0 right-0 bg-black/75 text-white px-1 translate-y-8 opacity-0 transition-all group-hover:translate-y-0 group-hover:opacity-100"></div>
        </div>      
      </template>
    </div>
  
  </div>
</section>