Skip to content

Instantly share code, notes, and snippets.

@chris-schmitz
Created January 24, 2017 04:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chris-schmitz/935a6fc9811c6508893fbf1b329f7638 to your computer and use it in GitHub Desktop.
Save chris-schmitz/935a6fc9811c6508893fbf1b329f7638 to your computer and use it in GitHub Desktop.
search bar/query builder
<!--
Note: This gist is from code I mocked up in the jsbin: http://jsbin.com/moqido/16/edit?js,output
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<style id="jsbin-css">
.search-bar{
background-color: lightgray;
}
.search-bar > ul{
width: 100%;
display: flex;
justify-content: center;
margin:0;
}
.search-bar > ul > li{
display: inline-block;
margin: 5px;
}
.search-bar > ul > li > button{
background-color: cyan;
outline: none;
border: 0;
padding:20px;
width: 150px;
font-size:20px;
}
.search-drawer{
background-color: gray;
padding: 0;
display: flex;
flex-direction: column;
}
.search-drawer > div {
display: flex;
text-align: center;
justify-content: space-around;
flex: 1;
}
.search-drawer > div > ul {
list-style: none;
}
.search-drawer > div > ul > li > button{
background-color: white;
border: none;
outline: none;
margin: 5px;
padding: 5px;
width: 100px;
}
.search-drawer > div > ul > li > button.selected{
background-color: cyan;
}
.search-drawer > button{
background-color: cyan;
border: 0;
padding:10px;
width: 100px;
font-size:15px;
outline: none;
}
</style>
</head>
<body>
<div id="search-bar-wrapper">
<search-bar :search-options="searchOptionKeys"></search-bar>
<search-drawer v-if="showDrawer"></search-drawer>
</div>
<p>
Content not bound to viewmodel
</p>
<script type="text/x-template" id="search-bar">
<div class="search-bar">
Sort by:
<ul>
<li v-for="option in searchOptions">
<button v-text="option" @click="showDrawer"></button>
</li>
</ul>
</div>
</script>
<script type="text/x-template" id="search-drawer">
<div class="search-drawer">
<div>
<ul v-for="(options, index) in state.searchOptions">
<li>{{index}}</li>
<li v-for="option in options">
<button @click="toggleInQuery(index, option)" :class="{'selected':option.includeInQuery}">
{{ option.label }}
</button>
</li>
</ul>
</div>
<label>
Show QueryString as it's being built:
<input type="checkbox" v-model="showQueryStringInProgress">
</label>
<p v-if="showQueryStringInProgress">
{{ queryString }}
</p>
<button @click="buildAndClose">Apply</button>
</div>
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>
<script id="jsbin-javascript">
Vue.component('search-bar',{
template: '#search-bar',
props: ['searchOptions'],
data: function(){
return {
state: state
}
},
methods:{
showDrawer: function(){
this.state.showDrawer = true
}
}
})
Vue.component('search-drawer',{
template: '#search-drawer',
data: function(){
return {
state: state,
showQueryStringInProgress: false,
}
},
computed:{
queryString: function(){
return Object.keys(state.searchOptions)
.map(function(key){
return {key: key, details: state.searchOptions[key]}
})
.map(function(optionDetails){
// woof this one is ugly. I'm sure there's a way of flattening
// this double map while accomplishing the same goal, but I'm
// _hella_ tired so I'm not even gonna worry about it now!!!!:Oomg!
return optionDetails.details.map(function(details){
details.key = optionDetails.key
return details
})
})
.reduce(function(carry, next){ return carry.concat(next)},[]) //flatten
.filter(function(optionData){
return optionData.includeInQuery === true
})
.map(function(optionDetails){
return optionDetails.key + "=" +optionDetails.queryString
})
.reduce(function(carry, next){
return carry + next + "&"
},'')
}
},
methods:{
buildAndClose: function(){
var queryString = "?" + this.queryString.substr(0, this.queryString.length - 1) // the last character is always going to be a & because of the last reduce in the queryString chain
alert("this is the query string we're gonna use (also printed in console.log): " + queryString)
console.log(queryString)
this.state.showDrawer = false
},
toggleInQuery: function(optionType, option){
// this is a _cheap shit_ way of handling the toggle. Refactor
// the shared state to be a store and extract this to a store method
// so that your component doesn't have to know this level of detailed
// knowledge about our shared state. And do that _in the morning_ ;)
if(optionType === 'date'){
if(option.queryString === 'newest-to-oldest'){
this.state.searchOptions.date[1].includeInQuery = false
} else {
this.state.searchOptions.date[0].includeInQuery = false
}
}
option.includeInQuery = !option.includeInQuery
}
}
})
var state = {
showDrawer: true, // default false
searchOptions:{
category:[
{queryString: "what-is-modern-ag", label: "What is Modern Ag?", includeInQuery: false},
{queryString: "using-less-water", label:"Using Less Water", includeInQuery: false},
{queryString: "using-less-soil", label:"Using Less Soil", includeInQuery: false},
{queryString: "using-less-energy", label:"Using Less Energy", includeInQuery: false},
{queryString: "biodiversity", label:"Biodiversity", includeInQuery: false},
{queryString: "technology-spotlight", label:"Technology Spotlight", includeInQuery: false}
],
type:[
{queryString: "articles", label:"Articles", includeInQuery: false},
{queryString: "questions", label:"Questions", includeInQuery: false}
],
date: [
{ queryString: "newest-to-oldest", label:"Newest to Oldest", includeInQuery: false},
{ queryString: "oldest-to-newest", label:"Oldest to Newest", includeInQuery: false}
]
}
}
new Vue({
el: '#search-bar-wrapper',
data: state,
computed:{
searchOptionKeys: function(){
return Object
.keys(this.searchOptions)
// can add maps to title case it or jsut use uppercase key names
}
}
})
</script>
<script id="jsbin-source-css" type="text/css">.search-bar{
background-color: lightgray;
}
.search-bar > ul{
width: 100%;
display: flex;
justify-content: center;
margin:0;
}
.search-bar > ul > li{
display: inline-block;
margin: 5px;
}
.search-bar > ul > li > button{
background-color: cyan;
outline: none;
border: 0;
padding:20px;
width: 150px;
font-size:20px;
}
.search-drawer{
background-color: gray;
padding: 0;
display: flex;
flex-direction: column;
}
.search-drawer > div {
display: flex;
text-align: center;
justify-content: space-around;
flex: 1;
}
.search-drawer > div > ul {
list-style: none;
}
.search-drawer > div > ul > li > button{
background-color: white;
border: none;
outline: none;
margin: 5px;
padding: 5px;
width: 100px;
}
.search-drawer > div > ul > li > button.selected{
background-color: cyan;
}
.search-drawer > button{
background-color: cyan;
border: 0;
padding:10px;
width: 100px;
font-size:15px;
outline: none;
}</script>
<script id="jsbin-source-javascript" type="text/javascript">Vue.component('search-bar',{
template: '#search-bar',
props: ['searchOptions'],
data: function(){
return {
state: state
}
},
methods:{
showDrawer: function(){
this.state.showDrawer = true
}
}
})
Vue.component('search-drawer',{
template: '#search-drawer',
data: function(){
return {
state: state,
showQueryStringInProgress: false,
}
},
computed:{
queryString: function(){
return Object.keys(state.searchOptions)
.map(function(key){
return {key: key, details: state.searchOptions[key]}
})
.map(function(optionDetails){
// woof this one is ugly. I'm sure there's a way of flattening
// this double map while accomplishing the same goal, but I'm
// _hella_ tired so I'm not even gonna worry about it now!!!!:Oomg!
return optionDetails.details.map(function(details){
details.key = optionDetails.key
return details
})
})
.reduce(function(carry, next){ return carry.concat(next)},[]) //flatten
.filter(function(optionData){
return optionData.includeInQuery === true
})
.map(function(optionDetails){
return optionDetails.key + "=" +optionDetails.queryString
})
.reduce(function(carry, next){
return carry + next + "&"
},'')
}
},
methods:{
buildAndClose: function(){
var queryString = "?" + this.queryString.substr(0, this.queryString.length - 1) // the last character is always going to be a & because of the last reduce in the queryString chain
alert("this is the query string we're gonna use (also printed in console.log): " + queryString)
console.log(queryString)
this.state.showDrawer = false
},
toggleInQuery: function(optionType, option){
// this is a _cheap shit_ way of handling the toggle. Refactor
// the shared state to be a store and extract this to a store method
// so that your component doesn't have to know this level of detailed
// knowledge about our shared state. And do that _in the morning_ ;)
if(optionType === 'date'){
if(option.queryString === 'newest-to-oldest'){
this.state.searchOptions.date[1].includeInQuery = false
} else {
this.state.searchOptions.date[0].includeInQuery = false
}
}
option.includeInQuery = !option.includeInQuery
}
}
})
var state = {
showDrawer: true, // default false
searchOptions:{
category:[
{queryString: "what-is-modern-ag", label: "What is Modern Ag?", includeInQuery: false},
{queryString: "using-less-water", label:"Using Less Water", includeInQuery: false},
{queryString: "using-less-soil", label:"Using Less Soil", includeInQuery: false},
{queryString: "using-less-energy", label:"Using Less Energy", includeInQuery: false},
{queryString: "biodiversity", label:"Biodiversity", includeInQuery: false},
{queryString: "technology-spotlight", label:"Technology Spotlight", includeInQuery: false}
],
type:[
{queryString: "articles", label:"Articles", includeInQuery: false},
{queryString: "questions", label:"Questions", includeInQuery: false}
],
date: [
{ queryString: "newest-to-oldest", label:"Newest to Oldest", includeInQuery: false},
{ queryString: "oldest-to-newest", label:"Oldest to Newest", includeInQuery: false}
]
}
}
new Vue({
el: '#search-bar-wrapper',
data: state,
computed:{
searchOptionKeys: function(){
return Object
.keys(this.searchOptions)
// can add maps to title case it or jsut use uppercase key names
}
}
})</script></body>
</html>
.search-bar{
background-color: lightgray;
}
.search-bar > ul{
width: 100%;
display: flex;
justify-content: center;
margin:0;
}
.search-bar > ul > li{
display: inline-block;
margin: 5px;
}
.search-bar > ul > li > button{
background-color: cyan;
outline: none;
border: 0;
padding:20px;
width: 150px;
font-size:20px;
}
.search-drawer{
background-color: gray;
padding: 0;
display: flex;
flex-direction: column;
}
.search-drawer > div {
display: flex;
text-align: center;
justify-content: space-around;
flex: 1;
}
.search-drawer > div > ul {
list-style: none;
}
.search-drawer > div > ul > li > button{
background-color: white;
border: none;
outline: none;
margin: 5px;
padding: 5px;
width: 100px;
}
.search-drawer > div > ul > li > button.selected{
background-color: cyan;
}
.search-drawer > button{
background-color: cyan;
border: 0;
padding:10px;
width: 100px;
font-size:15px;
outline: none;
}
Vue.component('search-bar',{
template: '#search-bar',
props: ['searchOptions'],
data: function(){
return {
state: state
}
},
methods:{
showDrawer: function(){
this.state.showDrawer = true
}
}
})
Vue.component('search-drawer',{
template: '#search-drawer',
data: function(){
return {
state: state,
showQueryStringInProgress: false,
}
},
computed:{
queryString: function(){
return Object.keys(state.searchOptions)
.map(function(key){
return {key: key, details: state.searchOptions[key]}
})
.map(function(optionDetails){
// woof this one is ugly. I'm sure there's a way of flattening
// this double map while accomplishing the same goal, but I'm
// _hella_ tired so I'm not even gonna worry about it now!!!!:Oomg!
return optionDetails.details.map(function(details){
details.key = optionDetails.key
return details
})
})
.reduce(function(carry, next){ return carry.concat(next)},[]) //flatten
.filter(function(optionData){
return optionData.includeInQuery === true
})
.map(function(optionDetails){
return optionDetails.key + "=" +optionDetails.queryString
})
.reduce(function(carry, next){
return carry + next + "&"
},'')
}
},
methods:{
buildAndClose: function(){
var queryString = "?" + this.queryString.substr(0, this.queryString.length - 1) // the last character is always going to be a & because of the last reduce in the queryString chain
alert("this is the query string we're gonna use (also printed in console.log): " + queryString)
console.log(queryString)
this.state.showDrawer = false
},
toggleInQuery: function(optionType, option){
// this is a _cheap shit_ way of handling the toggle. Refactor
// the shared state to be a store and extract this to a store method
// so that your component doesn't have to know this level of detailed
// knowledge about our shared state. And do that _in the morning_ ;)
if(optionType === 'date'){
if(option.queryString === 'newest-to-oldest'){
this.state.searchOptions.date[1].includeInQuery = false
} else {
this.state.searchOptions.date[0].includeInQuery = false
}
}
option.includeInQuery = !option.includeInQuery
}
}
})
var state = {
showDrawer: true, // default false
searchOptions:{
category:[
{queryString: "what-is-modern-ag", label: "What is Modern Ag?", includeInQuery: false},
{queryString: "using-less-water", label:"Using Less Water", includeInQuery: false},
{queryString: "using-less-soil", label:"Using Less Soil", includeInQuery: false},
{queryString: "using-less-energy", label:"Using Less Energy", includeInQuery: false},
{queryString: "biodiversity", label:"Biodiversity", includeInQuery: false},
{queryString: "technology-spotlight", label:"Technology Spotlight", includeInQuery: false}
],
type:[
{queryString: "articles", label:"Articles", includeInQuery: false},
{queryString: "questions", label:"Questions", includeInQuery: false}
],
date: [
{ queryString: "newest-to-oldest", label:"Newest to Oldest", includeInQuery: false},
{ queryString: "oldest-to-newest", label:"Oldest to Newest", includeInQuery: false}
]
}
}
new Vue({
el: '#search-bar-wrapper',
data: state,
computed:{
searchOptionKeys: function(){
return Object
.keys(this.searchOptions)
// can add maps to title case it or jsut use uppercase key names
}
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment