Многоразовый поисковый фильтр Vue JS с сеткой - PullRequest
0 голосов
/ 18 мая 2018

Итак, после 2-3 недель чтения, пробования простых примеров и выполнения пошаговых шагов в VueJs, попытки более сложного варианта использования, и я застрял.

Создан базовый метод поиска для фильтрацииданные по названию и итеративно возвращать результаты в сетке с 3 столбцами.

Теперь мне нужно сделать это что-то повторно используемым.

  1. Имеет несколько разделов (например, section2, section3,...) с соответствующими данными контента (content2, contents3, ...), каждый из которых отвечает на фильтрацию.например, если бы у меня был Раздел 2 с названиями песен вместо Evernote и Pinterest, все равно работал бы так же и выкладывал контент в 3 столбца.
  2. Учитывая, что механизм фильтрации по сути одинаков для всех разделов, желательноиспользовать его как вычисляемое свойство вместо метода (+ увеличение производительности), но не уверен, как я могу идентифицировать контент для каждого раздела (например, content3 для section3) и вернуть отфильтрованное содержимое в сетке из 3 столбцов, не затрагивая другие разделы.

Конечно, «грубая сила» состоит в том, чтобы написать один метод фильтрации для каждого массива контента.В действительности, я просто нуб VueJs, который знает, что (2) возможно, и не может его кодировать.

Заранее благодарен за любые указатели.

HTML

<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

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

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;
}

Fiddle here

Ответы [ 2 ]

0 голосов
/ 30 сентября 2018

Проверьте это!

Очень простая и мощная сетка на основе VUEJS и этот образец в сочетании с ASP.NET MVC

https://github.com/Dariush-Tasdighi/LEARNING_VUE

Learn_1000 Действие и представлениеимеет фильтр!

0 голосов
/ 18 мая 2018

Очень простая реализация этого состояла бы в том, чтобы преобразовать 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...