首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >具有网格布局的可重用Vue JS搜索过滤器

具有网格布局的可重用Vue JS搜索过滤器
EN

Stack Overflow用户
提问于 2018-05-18 18:30:34
回答 2查看 2K关注 0票数 2

因此,经过2-3周的阅读,尝试简单的例子,并在VueJ中迈出了初步的一步,尝试了一个更复杂的用例,我被困住了。

创建了一个基本的搜索方法,通过标题过滤数据,并在3列网格布局中迭代返回结果。

现在我需要让这个东西重复使用。

  1. 有多个部分(如section2、section3、.)有相应的内容数据(contents2,contents3,.)每个响应过滤。例如,如果我有第2节的歌曲标题,而不是Evernote和Pinterest,仍然会以同样的方式工作,并在3列中列出内容。
  2. 考虑到过滤机制对每个部分本质上是相同的,希望将其用作计算属性而不是方法(+性能增益),但不确定如何识别每个部分的内容(例如,content3表示section3),并在不影响其他部分的情况下返回3列网格中的过滤内容。

当然,“蛮力”的方法是为每个内容数组编写一种过滤方法。然而,在现实中,我只是一个知道(2)是可能的,不能编码的VueJs noob。

提前谢谢你的指点。

代码语言:javascript
复制
<div id="app">
  <h2>Search</h2>
  <div><input type="text" v-model="searchString" placeholder="Search" /></div>

  <section id="section1">
    <div class="row" v-for="i in rowCount" :key="i.id">
      <div v-for="content in filtered1(i)" :key="content.id" class="one-third">
        <img :src="content.image" class="center-block" />
        <h3>{{content.title}}</h3>
        <p class="m-t-content tablet">{{content.description}}</p>
        <p class="m-t-content mobile-center tablet"><a :href="content.url">Read more</a></p>
      </div>
    </div>
  </section>

  <section id="section2">
  </section>  
</div>

JS

代码语言:javascript
复制
new Vue({
  el: "#app",
  data () {
    return {
      searchString: '',
      itemsPerRow: 3,
      contents1: [
        {
          'title': 'Android',
          'url': '/',
          'description': 'Lorem ipsum dolor sit amet.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/android-icon.png'
        },
        {
          'title': 'Pinterest',
          'url': '/',
          'description': 'Consectetur adipiscing elit.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/pinterest-icon.png'
        },
        {
          'title': 'Behance',
          'url': '/',
          'description': 'Pellentesque pulvinar nisi.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/behance-icon.png'
        },
        {
          'title': 'Evernote',
          'url': '/',
          'description': 'Id tincidunt orci elementum.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/evernote-icon.png'
        },
        {
          'title': 'Github',
          'url': '/',
          'description': 'Lorem ipsum dolor sit amet.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/github-icon.png'
        }
      ]
    }
  },
  computed: {
    rowCount: function () {
      return Math.ceil(this.contents1.length / this.itemsPerRow)
    }
  },
  methods: {
    filtered1: function (index) {
      var contentsArray = this.contents1
      var searchString = this.searchString

      if (!searchString) {
        return contentsArray.slice((index - 1) * this.itemsPerRow, index * this.itemsPerRow)
      }

      searchString = searchString.trim().toLowerCase()

      contentsArray = contentsArray.filter(function (item) {
        if (item.title.toLowerCase().indexOf(searchString) !== -1) {
          return item
        }
      })

      return contentsArray.slice((index - 1) * this.itemsPerRow, index * this.itemsPerRow)
    }
  }
})

CSS

代码语言:javascript
复制
body {
  background: #fff;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

li {
  margin: 8px 0;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

h3 {
  font-weight: bold;
  margin-bottom: 5px;
}

del {
  color: rgba(0, 0, 0, 0.3);
}

.one-third {
  box-sizing: border-box;
  float: left;
  margin: 0 1%;
  width: 31.33%;  
}

.row {
  margin: 10px 0;
}

.row:after {
  content: "";
  display: table;
  clear: both;
}

小提琴在这里

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-05-18 19:03:13

这方面的一个非常简单的实现是将Vue转换为组件,并将每个呈现的元素的内容公开到一个插槽中,以便父元素可以设置自定义内容。

这里是我的意思的一个例子。

代码语言:javascript
复制
console.clear()

Vue.component("filtered", {
	template: `
  <div>
    <h2>Search</h2>
    <div><input type="text" v-model="searchString" placeholder="Search" /></div>

    <section>
    <div class="row" v-for="i in rowCount" :key="i">
      <div v-for="content in filtered.slice(itemsPerRow * (i - 1), itemsPerRow * i)" :key="content[keyColumn]" class="one-third">
				<slot :content="content" />
      </div>
    </div>
    </section>  
  </div>
	`,
  props: ["contents", "itemsPerRow", "filterColumns", "keyColumn"],
  data(){
  	return {
    	searchString: ''
    }
  },
  computed: {
    rowCount: function () {
      return Math.ceil(this.filtered.length / this.itemsPerRow)
    },
    filtered(){
      let results = this.contents
      if (!this.searchString)
        return results
        
      let searchString = this.searchString.toLowerCase()
        
      return results.filter(item => {
        for (let column of this.filterColumns)
          if (item[column].toLowerCase().includes(searchString))
            return true
        
        return false
      })
      
    }
  },
})

new Vue({
  el: "#app",
  data () {
    return {
      records:[
        {
          name: "Dark Side of the Moon",
          artist: "Pink Floyd"
        },
        {
          name: "Wish You Were Here",
          artist: "Pink Floyd",
        },
        {
          name: "The Joshua Tree",
          artist: "U2"
        }
      ],
      contents1: [
        {
          'title': 'Android',
          'url': '/',
          'description': 'Lorem ipsum dolor sit amet.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/android-icon.png'
        },
        {
          'title': 'Pinterest',
          'url': '/',
          'description': 'Consectetur adipiscing elit.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/pinterest-icon.png'
        },
        {
          'title': 'Behance',
          'url': '/',
          'description': 'Pellentesque pulvinar nisi.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/behance-icon.png'
        },
        {
          'title': 'Evernote',
          'url': '/',
          'description': 'Id tincidunt orci elementum.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/evernote-icon.png'
        },
        {
          'title': 'Github',
          'url': '/',
          'description': 'Lorem ipsum dolor sit amet.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/github-icon.png'
        }
      ]
    }
  },
})
代码语言:javascript
复制
body {
  background: #fff;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

li {
  margin: 8px 0;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

h3 {
  font-weight: bold;
  margin-bottom: 5px;
}

del {
  color: rgba(0, 0, 0, 0.3);
}

.one-third {
  box-sizing: border-box;
  float: left;
  margin: 0 1%;
  width: 31.33%;  
}

.row {
  margin: 10px 0;
}

.row:after {
  content: "";
  display: table;
  clear: both;
}
代码语言:javascript
复制
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
  <filtered :contents="records" :items-per-row="2" :filter-columns="['name']" :key-column="'name'">
    <template slot-scope="{content}">
      <h4>{{content.name}}</h4>
    </template>
  </filtered>
  <filtered :contents="contents1" :items-per-row="3" :filter-columns="['title', 'description']" :key-column="'title'">
    <template slot-scope="{content}">
      <img :src="content.image" class="center-block" />
        <h3>{{content.title}}</h3>
        <p class="m-t-content tablet">{{content.description}}</p>
        <p class="m-t-content mobile-center tablet"><a :href="content.url">Read more</a></p>
    </template>
  </filtered>
</div>

根据注释中的进一步讨论,可以使用常见的搜索字符串,如下所示:

代码语言:javascript
复制
console.clear()

Vue.component("filtered", {
  props: ["contents","itemsPerRow", "filterColumns", "keyColumn", "searchString"],
	template: `
    <section>
    <div class="row" v-for="i in rowCount" :key="i">
      <div v-for="content in filtered.slice(itemsPerRow * (i - 1), itemsPerRow * i)" :key="content[keyColumn]" class="one-third">
				<slot :content="content" />
      </div>
    </div>
    </section>  
	`,
  computed: {
    rowCount: function () {
      return Math.ceil(this.filtered.length / this.itemsPerRow)
    },
    filtered(){
      let results = this.contents
      if (!this.searchString)
        return results
        
      let searchString = this.searchString.toLowerCase()
        
      return results.filter(item => {
        for (let column of this.filterColumns)
          if (item[column].toLowerCase().includes(searchString))
            return true
        
        return false
      })
      
    }
  },
})

new Vue({
  el: "#app",
  data () {
    return {
      searchString: '',
      records:[
        {
          name: "Dark Side of the Moon",
          artist: "Pink Floyd"
        },
        {
          name: "Wish You Were Here",
          artist: "Pink Floyd",
        },
        {
          name: "The Joshua Tree",
          artist: "U2"
        }
      ],
      contents1: [
        {
          'title': 'Android',
          'url': '/',
          'description': 'Lorem ipsum dolor sit amet.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/android-icon.png'
        },
        {
          'title': 'Pinterest',
          'url': '/',
          'description': 'Consectetur adipiscing elit.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/pinterest-icon.png'
        },
        {
          'title': 'Behance',
          'url': '/',
          'description': 'Pellentesque pulvinar nisi.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/behance-icon.png'
        },
        {
          'title': 'Evernote',
          'url': '/',
          'description': 'Id tincidunt orci elementum.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/evernote-icon.png'
        },
        {
          'title': 'Github',
          'url': '/',
          'description': 'Lorem ipsum dolor sit amet.',
          'image': 'http://icons.iconarchive.com/icons/danleech/simple/64/github-icon.png'
        }
      ]
    }
  },
})
代码语言:javascript
复制
body {
  background: #fff;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

li {
  margin: 8px 0;
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

h3 {
  font-weight: bold;
  margin-bottom: 5px;
}

del {
  color: rgba(0, 0, 0, 0.3);
}

.one-third {
  box-sizing: border-box;
  float: left;
  margin: 0 1%;
  width: 31.33%;  
}

.row {
  margin: 10px 0;
}

.row:after {
  content: "";
  display: table;
  clear: both;
}
代码语言:javascript
复制
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
    <h2>Search</h2>
    <div><input type="text" v-model="searchString" placeholder="Search" /></div>

  <filtered :contents="records" 
            :items-per-row="2" 
            :filter-columns="['name']" 
            :key-column="'name'"
            :search-string="searchString">
    <template slot-scope="{content}">
      <h4>{{content.name}}</h4>
    </template>
  </filtered>
  
  <filtered :contents="contents1" 
            :items-per-row="3" 
            :filter-columns="['title', 'description']" 
            :key-column="'title'"
            :search-string="searchString">
    <template slot-scope="{content}">
      <img :src="content.image" class="center-block" />
      <h3>{{content.title}}</h3>
      <p class="m-t-content tablet">{{content.description}}</p>
      <p class="m-t-content mobile-center tablet"><a :href="content.url">Read more</a></p>
    </template>
  </filtered>
</div>

票数 2
EN

Stack Overflow用户

发布于 2018-09-30 17:16:38

看看这个!

一个非常简单、功能强大的基于VUEJS的网格,这个例子结合了ASP.NET MVC。

VUE

Learn_1000动作和视图有过滤器!

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50417310

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档