Очень простая реализация этого состояла бы в том, чтобы преобразовать Vue в компонент и предоставить контент для каждого отображаемого элемента в слот, чтобы родитель мог настроить пользовательский контент.
Вот примеро чем я.
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'
}
]
}
},
})
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;
}
<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>
Исходя из дальнейшего обсуждения в комментариях, вы можете использовать общую строку поиска следующим образом:
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'
}
]
}
},
})
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;
}
<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>