create a category filter with vuejs method - vue.js

I have the following code I am trying to use a function called categorize to filter all of the templates into categories. Each object has a category array. everytime they click on category button a category word is pushed into the selected categories array. if the a template has an category that is in the selected categories array I want to display it. It doesnt work in its current state any ideas?
<div class="col-lg-3 visible-lg-block hidden-md" id="SearchTemplatesLargeContainer">
<form role="form" role="form" onsubmit="return false">
<div class="form-group">
<label for="searchBar">Search</label>
<input v-model="searchQuery" type="text" placeholder="Search Tempates" id="searchBar" autocomplete="off" class="form-control">
</div>
<div class="form-group">
<label>Sort By</label>
<select v-model="sortBy" class="form-control">
<option value="renderTime">Render Time</option>
<option value="az">A-Z</option>
<option value="dateAdded">Newest</option>
</select>
</div>
<div class="form-group">
<h5>Categories</h5>
<div v-for="category in categories" :class="category + 'Button'" v-on:click.prevent="selectCategory(category)" class="btn btn-default categoryButton">
{{category}}
</div>
</div>
</form>
</div>
<!--end lg template menu col-lg-3-->
<!--search templates modal-->
<div class="modal fade" role="dialog" id="searchTemplatesModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">X</button>
<h2 class="modal-title">Search Templates</h2>
<!--end .modal-title-->
</div>
<!--end modal-header-->
<div class="modal-body">
<form role="form">
<div class="form-group">
<label>Search</label>
<input v-model="searchQuery" type="text" placeholder="Search Tempates" autocomplete="off" class="form-control">
</div>
<!--.end searchQuery-->
<div class="form-group">
<label>Sort By</label>
<select v-model="sortBy" class="form-control">
<option value="renderTime">Render Time</option>
<option value="az">A-Z</option>
<option value="dateAdded">Newest</option>
</select>
</div>
<!--end sortBy-->
<div class="form-group">
<h5>Categories</h5>
<div v-for="category in categories" :class="category + 'Button'" v-on:click.prevent="selectCategory(category)" class="btn btn-default categoryButton">
{{category}}
</div>
</div>
<!--end categorys-->
</form>
</div>
<!--end .modal-body-->
<div class="modal-footer">
<button type="button" class="btn btn-primary btn-block" data-dismiss="modal">Search</button>
</div>
<!--end .modal-footer-->
</div>
<!--end .modal-content-->
</div>
<!--end .modal-dialog-->
</div>
<!-- end search templates modal-->
<div class="col-lg-9 col-md-12 col-sm-12 col-xs-12">
<div class="row">
<h1 class="mainHeading">Creative Engine Templates</h1>
</div>
<div class="row">
<div v-cloak v-bind:key="template.itemId + '_' + index" v-for="(template, index) in searchResults" class="col-md-4">
<div class="card">
<video muted :src="'mysource/'+template.itemId+'/'+template.thumbName+'.mp4'" controls preload="none" controlsList="nodownload nofullscreen" :poster="'mysource/'+template.itemId+'/'+template.thumbName+'.jpg'" loop="loop"></video>
<div class="card-body">
<p class="card-title">{{template.itemName}}</p>
<!--end card-title-->
<p v-show="displayCount==0" class="card-text">Please create a display to customize</p>
<!--end user has no display card-text-->
<p v-show="displayCount>0" class="card-text">Custom Text, Custom Colors, Upload Logo</p>
<!--end user has display card text-->
<p class="card-text">{{template.renderTime}} minutes</p>
Customize
<!--logged in and has display button-->
<a href="#" v-show="loggedIn==false" class="btn btn-primary btn-block" disabled>Customize</a>
<!--not logged in button-->
Create A Display
</div>
<!--end card-body-->
</div>
<!--end card-->
</div>
<!-- end col-md-4-->
</div>
<!--end row-->
</div>
<!--end col-lg-9 col-md-12 col-sm-12 col-xs-12-->
</div>
<!--end templates app-->
<script>
var app = new Vue({
el: '#templates',
data: {
loggedIn: false,
displayCount:'36',
searchQuery:'',
templateArray: [{"id":"7","itemId":"17520","itemName":"Arrow Bounce","thumbName":"ARROWBOUNCE","dateAdded":"2016-05-20 16:33:42","renderTime":"30","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/ArrowBounce-Scott\/arrowBounce.aep","stillImageLocation":"2.66","categoryArray":[],"keywordArray":["LensFlare"]},{"id":"11","itemId":"38752","itemName":"Jitter Zoom Flash","thumbName":"JITTERZOOMFLASH","dateAdded":"2016-05-23 13:49:03","renderTime":"45","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/JitterZoomFlash-Scott\/JitterZoomFlash.aep","stillImageLocation":"2.66","categoryArray":["Sports"],"keywordArray":["Sparkles","Snow"]},{"id":"12","itemId":"12737","itemName":"Cloth Drop","thumbName":"CLOTHDROP","dateAdded":"2016-05-23 14:11:42","renderTime":"30","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/ClothDrop-Scott\/cloth drop.aep","stillImageLocation":"2.66","categoryArray":[],"keywordArray":[]},{"id":"15","itemId":"73076","itemName":"Colorful Trans","thumbName":"COLORFULIMAGETRANS","dateAdded":"2016-05-27 10:16:56","renderTime":"30","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/ColorfulImageTrans-Scott\/ColorfulImageTrans.aep","stillImageLocation":"12.90","categoryArray":[],"keywordArray":["Sparkles","Water"]},{"id":"16","itemId":"18488","itemName":"Convex ½ Circle","thumbName":"CONVEXHALFCIRCLE","dateAdded":"2016-05-27 10:38:20","renderTime":"30","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/convexHalfCircle-Scott\/convex half circle.aep","stillImageLocation":"2.66","categoryArray":[],"keywordArray":[]},{"id":"17","itemId":"67039","itemName":"Flag Swap","thumbName":"FLAGBACKSWAP","dateAdded":"2016-06-01 15:34:22","renderTime":"30","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/FlagBackSwap-Scott\/FlagBackSwap.aep","stillImageLocation":"5.83","categoryArray":[],"keywordArray":[]},{"id":"31","itemId":"70006","itemName":"Flag Raise","thumbName":"FLAGRAISE","dateAdded":"2016-06-03 11:13:37","renderTime":"60","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/FlagRaise-Scott\/flag.aep","stillImageLocation":"2.66","categoryArray":[],"keywordArray":[]},{"id":"32","itemId":"58759","itemName":"Logo Dust Poof","thumbName":"LOGODUSTPOOF","dateAdded":"2016-06-03 11:25:34","renderTime":"30","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/LogoDustPoof-Scott\/LogoDustPoof.aep","stillImageLocation":"6.23","categoryArray":[],"keywordArray":[]},{"id":"33","itemId":"58967","itemName":"Flag Wave (Loop)","thumbName":"FLAGWAVE","dateAdded":"2016-06-03 11:35:49","renderTime":"75","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/FlagWave-Scott\/FlagWave.aep","stillImageLocation":"2.66","categoryArray":[],"keywordArray":[]},{"id":"34","itemId":"65288","itemName":"Logo Splash One","thumbName":"LOGOSPLASHONE","dateAdded":"2016-06-03 15:34:19","renderTime":"45","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/LogoSplashOne-Scott\/LogoSplashOne.aep","stillImageLocation":"2.70","categoryArray":[],"keywordArray":[]},{"id":"35","itemId":"91246","itemName":"Metal Sparks","thumbName":"METALSPARKS","dateAdded":"2016-06-06 10:58:29","renderTime":"60","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/MetalSparks-Scott\/MetalSparks.aep","stillImageLocation":"4.63","categoryArray":[],"keywordArray":[]},{"id":"36","itemId":"57489","itemName":"Middle Stripe","thumbName":"MIDDLESTRIPEABA","dateAdded":"2016-06-06 12:25:41","renderTime":"60","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/MiddleStripe-Scott\/middleStripeABA.aep","stillImageLocation":"6.80","categoryArray":[],"keywordArray":[]},{"id":"37","itemId":"38402","itemName":"Water One","thumbName":"WATERONE","dateAdded":"2016-06-07 09:10:32","renderTime":"60","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/waterOne-Scott\/waterOne.aep","stillImageLocation":"8.83","categoryArray":[],"keywordArray":[]},{"id":"39","itemId":"81031","itemName":"Oval Text Flip","thumbName":"OVALTEXTFLIP","dateAdded":"2016-05-07 09:10:32","renderTime":"150","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/OvalTextFlip-Scott\/OvalTextFlip.aep","stillImageLocation":"2.66","categoryArray":[],"keywordArray":[]},{"id":"40","itemId":"55143","itemName":"Close Up Text","thumbName":"CLOSEUPTEXT","dateAdded":"2016-07-05 09:10:32","renderTime":"85","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/CloseUpText-Scott\/CloseUpText\/CloseUpText.aep","stillImageLocation":"9.03","categoryArray":[],"keywordArray":[]},{"id":"41","itemId":"54335","itemName":"Electric Text Spin","thumbName":"ELECTRICTEXTSPIN","dateAdded":"2016-07-13 09:10:32","renderTime":"60","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2016\/ElectricTextSpin-Scott\/ElectricTextSpin\/ElectricTextSpin.aep","stillImageLocation":"1.47","categoryArray":[],"keywordArray":[]},{"id":"42","itemId":"23761","itemName":"Digital Glitch","thumbName":"DIGITALGLITCH","dateAdded":"2016-09-19 09:10:32","renderTime":"60","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2016\/DigitalGlitch-Scott\/DigitalGlitch.aep","stillImageLocation":"3.43","categoryArray":["Retail"],"keywordArray":[]},{"id":"43","itemId":"56465","itemName":"Takeover","thumbName":"TAKEOVER","dateAdded":"2016-09-30 14:10:32","renderTime":"80","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2016\/TakeOver-Scott\/TakeoverProject\/takeoverproject.aep","stillImageLocation":"2.66","categoryArray":[],"keywordArray":[]},{"id":"44","itemId":"17127","itemName":"Fire One","thumbName":"FIREONE","dateAdded":"2016-11-04 14:10:32","renderTime":"25","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2016\/FireOne-Scott\/FireOne.aep","stillImageLocation":"2.66","categoryArray":[],"keywordArray":[]},{"id":"53","itemId":"61617","itemName":"City Spin","thumbName":"CITYSPIN","dateAdded":"2016-11-09 14:17:15","renderTime":"45","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2016\/CitySpin-Scott\/cityspin.aep","stillImageLocation":"8.933","categoryArray":["Church"],"keywordArray":[]},{"id":"56","itemId":"15528","itemName":"Magic Colors","thumbName":"MAGICCOLORS","dateAdded":"2016-11-10 13:10:26","renderTime":"30","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2016\/MagicColors-Scott\/MagicColors.aep","stillImageLocation":"3.966","categoryArray":[],"keywordArray":[]},{"id":"61","itemId":"59239","itemName":"Quick and Simple","thumbName":"QUICKNSIMPLE","dateAdded":"2016-11-14 11:42:09","renderTime":"15","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2016\/QuickNSimple-Scott\/QuickNSimple.aep","stillImageLocation":"2.033","categoryArray":[],"keywordArray":[]},{"id":"62","itemId":"82460","itemName":"Fast Blast","thumbName":"FASTBLAST","dateAdded":"2016-11-22 10:24:48","renderTime":"30","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2016\/FastBlast-Scott\/FastBlast.aep","stillImageLocation":"9.666","categoryArray":[],"keywordArray":[]},{"id":"63","itemId":"83530","itemName":"Tunnel Spin","thumbName":"TUNNELSPIN","dateAdded":"2016-12-02 13:09:06","renderTime":"20","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2016\/tunnelSpin-Scott\/tunnelSpin.aep","stillImageLocation":"2.9","categoryArray":[],"keywordArray":[]},{"id":"64","itemId":"94148","itemName":"Sparkle Splash","thumbName":"SPARKLESPLASH","dateAdded":"2016-12-20 11:23:26","renderTime":"45","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2016\/SparkleSplash-Scott\/SparkleSplash.aep","stillImageLocation":"6.1","categoryArray":[],"keywordArray":[]},{"id":"69","itemId":"98640","itemName":"Gold Bling","thumbName":"GOLDBLING","dateAdded":"2017-01-10 08:16:41","renderTime":"30","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2017\/GoldBling-Joe\/GoldBling.aep","stillImageLocation":"2.66","categoryArray":[],"keywordArray":[]},{"id":"72","itemId":"94169","itemName":"Top Racer","thumbName":"TOPRACER","dateAdded":"2017-02-15 09:46:14","renderTime":"30","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2017\/TopRacer-Scott\/TopRacer.aep","stillImageLocation":"7.833","categoryArray":[],"keywordArray":[]},{"id":"73","itemId":"55871","itemName":"Desert Sand","thumbName":"DESERTSAND","dateAdded":"2017-02-15 14:04:49","renderTime":"45","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2017\/DesertSand-Scott\/DesertSand.aep","stillImageLocation":"10.46","categoryArray":[],"keywordArray":[]},{"id":"76","itemId":"18897","itemName":"Electric Storm","thumbName":"ELECTRICSTORM","dateAdded":"2017-02-23 12:43:08","renderTime":"45","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2017\/ElectricStorm-Scott\/ElectricStorm.aep","stillImageLocation":"4.333","categoryArray":[],"keywordArray":[]},{"id":"78","itemId":"24052","itemName":"Court Smash","thumbName":"COURTSMASH","dateAdded":"2016-06-03 12:03:48","renderTime":"90","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2017\/CourtSmash-Scott\/CourtSmash.aep","stillImageLocation":"5.933","categoryArray":[],"keywordArray":[]},{"id":"81","itemId":"43553","itemName":"Tile Flip","thumbName":"TILEFLIP","dateAdded":"2017-04-25 16:40:41","renderTime":"60","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2017\/TileFlip-Chris\/TileFlip_Final\/TileFlip_Final.aep","stillImageLocation":"5","categoryArray":[],"keywordArray":[]},{"id":"88","itemId":"94677","itemName":"NEON LIGHTS","thumbName":"NEONLIGHTS","dateAdded":"2017-04-28 10:06:23","renderTime":"45","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2017\/NEONLIGHTS-Joe\/NeonLights.aep","stillImageLocation":"2.53","categoryArray":[],"keywordArray":[]},{"id":"89","itemId":"64305","itemName":"Engine (Loop)","thumbName":"ENGINE","dateAdded":"2017-05-15 11:37:07","renderTime":"60","tested":"1","projectPath":"O:\/Projects\/Generics\/CreativeEngine\/2017\/Engine-Scott\/Engine.aep","stillImageLocation":"4.67","categoryArray":[],"keywordArray":[]},{"id":"90","itemId":"11287","itemName":"Energy Core","thumbName":"ENERGYCORE","dateAdded":"2017-05-22 13:08:40","renderTime":"30","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2017\/EnergyCore-Scott\/EnergyCore.aep","stillImageLocation":"6.73","categoryArray":[],"keywordArray":[]},{"id":"91","itemId":"48745","itemName":"Football Helmet","thumbName":"FOOTBALLHELMET","dateAdded":"2017-07-03 16:09:42","renderTime":"120","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2017\/FootballHelmet-Scott\/FootballHelmet.aep","stillImageLocation":"7","categoryArray":[],"keywordArray":[]},{"id":"92","itemId":"85515","itemName":"Light Shine","thumbName":"LIGHTSHINE","dateAdded":"2017-08-18 14:09:50","renderTime":"30","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2017\/LightShine-Scott\/LightShine.aep","stillImageLocation":"2","categoryArray":[],"keywordArray":[]},{"id":"93","itemId":"61876","itemName":"Baseball Dirt","thumbName":"BASEBALLDIRT","dateAdded":"2017-08-31 10:31:22","renderTime":"40","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2017\/BaseballDirt-Scott\/BaseballDirt.aep","stillImageLocation":"7.27","categoryArray":[],"keywordArray":[]},{"id":"94","itemId":"48066","itemName":"Spooky","thumbName":"SPOOKY","dateAdded":"2017-09-01 13:58:36","renderTime":"15","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2017\/Spooky-Jake\/Spooky.aep","stillImageLocation":"2","categoryArray":["Sports"],"keywordArray":[]},{"id":"95","itemId":"33584","itemName":"Get Loud","thumbName":"GETLOUD","dateAdded":"2017-09-07 11:58:02","renderTime":"45","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2017\/GetLoud-Scott\/GetLoud.aep","stillImageLocation":"1.77","categoryArray":[],"keywordArray":[]},{"id":"96","itemId":"21713","itemName":"STAR BURST","thumbName":"STARBURST","dateAdded":"2017-10-19 18:20:29","renderTime":"15","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2017\/StarBurst-Joe\/StarBurst.aep","stillImageLocation":"0","categoryArray":[],"keywordArray":[]},{"id":"97","itemId":"76554","itemName":"Magic Twirl","thumbName":"MAGICFINAL","dateAdded":"2017-10-26 11:19:52","renderTime":"20","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2017\/Magic-Lillie\/Magic.aep","stillImageLocation":"825","categoryArray":[],"keywordArray":[]},{"id":"98","itemId":"64452","itemName":"Sports Car","thumbName":"SPORTSCAR","dateAdded":"2017-10-27 10:26:32","renderTime":"60","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2017\/SportsCar-Scott\/SportsCar.aep","stillImageLocation":"14.77","categoryArray":[],"keywordArray":[]},{"id":"99","itemId":"15074","itemName":"Ice Logo","thumbName":"ICELOGO","dateAdded":"2017-11-01 11:53:48","renderTime":"45","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2017\/IceLogo-Scott\/IceLogo.aep","stillImageLocation":"9.33","categoryArray":[],"keywordArray":["LensFlare"]},{"id":"100","itemId":"95033","itemName":"Hot Air Balloon","thumbName":"BALLOON","dateAdded":"2017-11-02 08:10:22","renderTime":"10","tested":"1","projectPath":"M:\/Projects\/Generics\/CreativeEngine\/2017\/Balloon-Lillie\/Balloon.aep","stillImageLocation":"567","categoryArray":[],"keywordArray":[]},{"id":"243","itemId":"f0adeb21cfbfc7e1894debeef4cc6e22","itemName":"testingCrap","thumbName":"TESTINGCRAP","dateAdded":"2018-10-08 18:06:48","renderTime":"4","tested":"0","projectPath":"M:\/Projects\/Generics\/uploads\/testLocation","stillImageLocation":"0.13","categoryArray":["Sports","Holiday"],"keywordArray":["LensFlare"]}],
filteredTemplateArray: [],
selectedCategories: [],
keywords: ["LensFlare","Snow","Water","Sparkles","Sky","Plants"],
selectedKeywords: [],
sortBy: ''
},
created: function(){
this.filteredTemplateArray=this.templateArray;
},
computed: {
searchResults: function(){
return this.filteredTemplateArray.filter((template)=>{
return template.itemName.toLowerCase().includes(this.searchQuery.toLowerCase());
})
}
},
methods: {
selectCategory: function(category) {
var categoryButton = $('.' + category + 'Button');
categoryButton.toggleClass('categoryButtonSelected');
if(this.selectedCategories.includes(category)){
var array = [];
for(var i = 0; i < this.selectedCategories.length; i++) {
if(this.selectedCategories[i] != category) {
array.push(this.selectedCategories[i]);
}
}
this.selectedCategories = array;
} else {
this.selectedCategories.push(category);
}
this.categorize();
},
categorize: function(){
return this.filteredTemplateArray.filter((template)=>{
return template.categoryArray.inArray(this.selectedCategories);
})
}
}
});
<script>

I am not too sure I understand the question, but.
Try creating a method for checking if a category is in the selectedCategories array
methods: {
isInSelectedCategories(category) {
return this.selectedCategories.includes(category)
}
...
}
In your template, you can do this check like so:
v-if="isInSelectedCategories(template.category)"
<!-- end search templates modal-->
<div class="col-lg-9 col-md-12 col-sm-12 col-xs-12">
<div class="row">
<h1 class="mainHeading">Creative Engine Templates</h1>
</div>
<div class="row">
<div v-cloak v-bind:key="template.itemId + '_' + index" v-for="(template, index) in searchResults" v-if="isInSelectedCategories(template.category)" class="col-md-4">
<div class="card">
<video muted :src="'mysource/'+template.itemId+'/'+template.thumbName+'.mp4'" controls preload="none" controlsList="nodownload nofullscreen" :poster="'mysource/'+template.itemId+'/'+template.thumbName+'.jpg'" loop="loop"></video>
<div class="card-body">
<p class="card-title">{{template.itemName}}</p>
<!--end card-title-->
<p v-show="displayCount==0" class="card-text">Please create a display to customize</p>
<!--end user has no display card-text-->
<p v-show="displayCount>0" class="card-text">Custom Text, Custom Colors, Upload Logo</p>
<!--end user has display card text-->
<p class="card-text">{{template.renderTime}} minutes</p>
Customize
<!--logged in and has display button-->
<a href="#" v-show="loggedIn==false" class="btn btn-primary btn-block" disabled>Customize</a>
<!--not logged in button-->
Create A Display
</div>
<!--end card-body-->
</div>
<!--end card-->
</div>
<!-- end col-md-4-->
</div>

create a watcher that watches selectedCategories and when it changes run a method that filters out the duplicates and sets filtered categories back to the original array when the selectedCategories.length==0. This creates a category filter that counts up. for instance if sports and holidays are checked it will show only all items in sports and all items in holidays which seems to work better than counting down or showing the ones that only exist in both. I use app instead of this as its the variable i called my vue instance and because when you get deep into a function this does not always mean the app.
watch: {
selectedCategories(){
this.categoryFilter();
},
sortBy(){
this.sortTemplatesBy();
}
},
methods: {
sortTemplatesBy: function(){
if(this.sortBy == "az") {
this.filteredTemplateArray.sort(function(a,b) {
if(a.itemName > b.itemName) {
return 1;
}
if(a.itemName < b.itemName) {
return -1;
}
return 0;
});
}
if(this.sortBy == "dateAdded") {
this.filteredTemplateArray.sort(function(a,b) {
if(a.dateAdded < b.dateAdded) {
return 1;
}
if(a.dateAdded > b.dateAdded) {
return -1;
}
return 0;
});
}
if(this.sortBy == "renderTime") {
this.filteredTemplateArray.sort(function(a,b) {
return parseInt(a.renderTime) - parseInt(b.renderTime);
});
}
},
categoryFilter: function(){
if(app.selectedCategories.length!=0){
app.filteredTemplateArray=[];
var templateArray = app.templateArray;
for(i=0; i<app.selectedCategories.length; i++){
var filtered=templateArray.filter(function(template){
var currentCategory=app.selectedCategories[i];
console.log("this is current category"+currentCategory+"end currentCategory");
var returnValue=template.categoryArray.includes(currentCategory);
console.log("this is i"+i+"end i");
console.log("this is return value"+returnValue+"end return value");
return returnValue;
});
console.log(filtered);
app.filteredTemplateArray=app.filteredTemplateArray.concat(filtered);
}
var uniqueArray=[];
var allResultsArray=app.filteredTemplateArray;
for(j=0; j<allResultsArray.length; j++){
if(uniqueArray.indexOf(allResultsArray[j]) == -1){
uniqueArray.push(allResultsArray[j])
}
}
app.filteredTemplateArray=uniqueArray;
} else {app.filteredTemplateArray=app.templateArray;}
this.sortTemplatesBy();
}
}

Related

I want display image in vue3 page before save to database, but my code not working

I want display image before in vue3 page before save to database, but my code not working, i have done some checking but i got no result, any one can help me?
this is my vue3 :
<script setup>
const getPhoto = () => {
let photo = "/img/avatar.png"
if(form.photo){
if (form.photo.indexOf('base64') != -1){
photo = form.photo
}
else{
photo = '/img/upload' + form.photo
}
}
return photo
}
const changePhoto = (e) => {
let file = e.target.files[0];
let reader = new FileReader();
let limit = 1024*1024*2
if (file['size'] > limit){
return false
}
reader.onloadend = (file) => {
form.photo = reader.result
}
reader.readAsDataURL(file)
}
const updateAbout = () => {
console.log('form', form)
}
</script>
this is template for showing image
<template>
<section class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1>Data Pengguna</h1>
</div>
</div>
</div><!-- /.container-fluid -->
<div class="row">
<div class="col-12">
Kembali
<input type="submit" value="Simpan" class="btn btn-success float-right" #click.prevent="updateAbout">
</div>
</div>
</section>
<!-- Main content -->
<section class="content">
<div class="row">
<div class="col-md-6">
<div class="card card-primary">
<div class="card-header">
<h3 class="card-title">Data Lengkap</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse" title="Collapse">
<i class="fas fa-minus"></i>
</button>
</div>
</div>
<div class="card-body">
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
<div class="col-md-6">
<div class="card card-secondary">
<div class="card-header">
<h3 class="card-title">Data Login</h3>
<div class="card-tools">
<button type="button" class="btn btn-tool" data-card-widget="collapse" title="Collapse">
<i class="fas fa-minus"></i>
</button>
</div>
</div>
<div class="card-body">
<div class="form-group">
<div class="avatar_profile">
<img src="getPhoto()" class="" alt="" />
</div>
<input type="file" #change="changePhoto"/>
</div>
</div>
<!-- /.card-body -->
</div>
<!-- /.card -->
</div>
</div>
</section>
</template>
I don't find any error with inspect...
when I open laravel.log, it not show any error
any one can help me? thanks
There's multiple things wrong with this code. First off, the "form" variable is not declared anywhere. This should show up in the console.
Second off, you cannot set a method to the src attribute of an image.
You need a ref variable holding the base64 of your image, like;
import { ref } from "vue";
...
const uploadedImageBase64 = ref();
Then, you need to change the base64 when you upload an image;
const changePhoto = (e) => {
let file = e.target.files[0];
let reader = new FileReader();
let limit = 1024*1024*2
if (file['size'] > limit){
return false
}
reader.onloadend = (file) => {
//Change this line
uploadedImageBase64.value = reader.result
}
reader.readAsDataURL(file)
}
In the template you can then show this data as follows;
<img v-if="uploadedImageBase64" :src="`${uploadedImageBase64}`" />
Don't forget to remove all references to the non-existent "form" variable.
You can see it working here:
Demo

On form button click success, form validations fire again

Hopefully this is a newbie question.
I am using Vuelidate to validate my form and the form validations works fine, when the “send email” button is clicked.However on success, instead of showing me the successful message, the system shows that all the form controls have errors(This is because I have bound the name, email, message controls to their corresponding empty data elements).
What am I missing?
How can I fix this issue?
Contact.vue
<template>
<div>
<section class="slice slice-lg" id="sct_contact_form">
<div class="container">
<div class="mb-5 text-center">
<span class="badge badge-soft-info badge-pill badge-lg">
Contact
</span>
<h3 class=" mt-4">Send us a message</h3>
</div>
<div class="row justify-content-center">
<div class="col-lg-8">
<form>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label class="form-control-label">Name</label>
<input class="form-control" type="text" placeholder="Name" v-model="user.name" id="name" name="name" :class="{ 'is-invalid': submitted && $v.user.name.$error }" >
<div v-if="submitted && !$v.user.name.required" class="invalid-feedback">Name is required</div>
</div>
</div>
</div>
<div class="row align-items-center">
<div class="col-md-12">
<div class="form-group">
<label class="form-control-label">Email</label>
<input class="form-control" type="email" placeholder="email#example.com"
v-model="user.email" id="email" name="email" :class="{ 'is-invalid': submitted && $v.user.email.$error }" >
<div v-if="submitted && $v.user.email.$error" class="invalid-feedback">
<span v-if="!$v.user.email.required">Email is required</span>
<span v-if="!$v.user.email.email">Email is invalid</span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label class="form-control-label">Message</label>
<textarea class="form-control" data-toggle="autosize" placeholder="Tell us a few words ..." rows="3" style="overflow: hidden; overflow-wrap: break-word; resize: none; height: 96.9922px;"
v-model="user.message" id="message" name="message" :class="{ 'is-invalid': submitted && $v.user.message.$error }" ></textarea>
<div v-if="submitted && $v.user.message.$error" class="invalid-feedback">
<span v-if="!$v.user.message.required">Message is required</span>
<span v-if="!$v.user.message.minLength">Message must be at least 6 characters</span>
</div>
</div>
</div>
</div>
<div class="text-center mt-4">
<button type="button" class="btn btn-dark rounded-pill" v-on:click="SendEmail()">Send your message</button>
<span class="d-block mt-4 text-sm">We'll get back to you in 24-48 h.</span>
<div v-if="submitted" class="valid-feedback">
<span v-if="!$v.user.name.$error && !$v.user.email.$error && !$v.user.message.$error">Your email was send successfully. We'll get back to you in 24-48 h.</span>
</div>
</div>
</form>
</div>
</div>
</div>
</section>
</div>
</template>
import { Email } from '../api/email.js';
import { required, email, minLength, sameAs } from "vuelidate/lib/validators";
export default {
name: "Contact",
components:{
},
data() {
return {
user: {
name: "",
email: "",
message: ""
},
submitted: false
};
},
validations: {
user: {
name: { required },
email: { required, email },
message: { required, minLength: minLength(6) }
}
},
methods: {
SendEmail() {
this.submitted = true;
this.$v.$touch();
if (this.$v.$invalid) {
return;
}
var nameWithEmailText="Email message from: "+ this.user.name + "\nEmail message: " + this.user.message;
var subject="Email from contact us page in common membership website";
Meteor.call('email.send', this.user.email, subject, nameWithEmailText);
this.user.name='';
this.user.email='';
this.user.message='';
}
}
}
You should wait for the Meteor.call() to complete, then reset the validation state.
For example
Meteor.call('email.send', this.user.email, subject, nameWithEmailText, (error, result) => {
this.user.name = ''
this.user.email = ''
this.user.message = ''
this.$v.$reset()
})

How do access to vee-validate errors and clear them outside vue instance (component)

I want to clear errors after i close modal or after i send data to database and i do it outside vue instance, i have tryed vm.$validator.errors.clean(), and reset but these wont work for me, any suggestions how do access them outside component?
Vue.component('add-modal', {
template: '#add-modal-template',
mounted() {
$(document).on('hidden.bs.modal','.modal', function (e) {
$(this).remove('bs.modal');
$(this).removeData('bs.modal');
$(".modal-body input").val(" ");
$('.modal-body option:first').prop('selected',true);
console.log($(this).find('.modal-body').html());
var myBackup = $(this).find('.modal-body').html();
$('.modal-body').empty();
$('.modal-body').append(myBackup);
console.log(vm.$validator.Errors);
});
}
}
var vm = new Vue({
el: '#app',
data: {},
created: function(){
//if something is need to be loaded first
}
});
});
<script type="text/x-template" id="add-modal-template">
<!-- Modal -->
<div class="modal fade" id="addModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">Add new deal</h5>
<button type="button" class="close no-glow" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form id="addDealForm" #submit.prevent="" v-on:submit.prevent>
<div class="form-group">
<label>Deal title</label>
<input type="text" name="title" class="form-control" id="Contact Person" v-validate="'required|max:15'" placeholder="Enter deal title" v-model="deal.title">
<span class="error text-danger" v-show="errors.has('title')">{{errors.first('title')}}</span><br>
<div class="modal-footer">
<button type="button" class="btn btn-outline-primary no-glow" data-dismiss="modal">Close</button>
<button class="btn btn-success no-glow">Save</button>
</div>
</form>
</div>
</div>
</div>
</div>
</script>
I provided code example
this in your callback is not Vue instance. You can try below techniques, assign var self=this
Vue.component('add-modal', {
template: '#add-modal-template',
mounted() {
var self = this;
$(document).on('hidden.bs.modal', '.modal', function(e) {
$(this).remove('bs.modal');
$(this).removeData('bs.modal');
$(".modal-body input").val(" ");
$('.modal-body option:first').prop('selected', true);
console.log($(this).find('.modal-body').html());
var myBackup = $(this).find('.modal-body').html();
$('.modal-body').empty();
$('.modal-body').append(myBackup);
console.log(self.errors);
self.errors.clear()
});
}
}

Using vue js with selectpicker

I'm using Vue.js to add multiple rows, each row contain two datetimepicker inputs and a bootstrap-select.
My problem is when I fill the inputs and click to add a new row the previous ones clear out, the reason is each time I add a new row I'm using setTimeout to referesh the selectpicker and the datetimepicker.
So I want to know if there is a way to trigger the last added element them without refreshing the previous ones.
Here is my code :
HTML
<div class="cols" id="app">
<ul style="list-style-type: none;">
<li>
<button style="float: right;margin-right: 20px;margin-top: 5px;" type="button" class="btn btn-success btn-xs" v-on:click="addRow()">
<i class="fa fa-plus"></i> Ajouter
</button>
</li>
<li v-for="(row, id) in rows">
<div class="form-inline cp-out" style="padding-bottom: 9px;border-radius:4px;background-color:white;margin-left:-20px;display:inline-block;width:100%;margin-top:10px;">
<div class="col-md-3">
<label :for="['time_start'+id]" class="control-label">start time</label>
<div class="input-group date form_time" style="width:100%;" data-date="" data-date-format="hh:ii" :data-link-field="['time_start'+id]" data-link-format="hh:ii">
<input v-model="row.startTime" :name="'rows['+id+'][startTime]'" class="form-control" :id="['startTime' + id]" size="16" type="text" value="" >
<span class="input-group-addon"><span class="glyphicon glyphicon-time"></span></span>
</div>
</div>
<div class="col-md-3">
<label :for="['time_end'+id]" class="control-label" style="margin-left:7px;">end time</label>
<div class="input-group date form_time" style="width:100%;" data-date="" data-date-format="hh:ii" :data-link-field="['time_end'+id]" data-link-format="hh:ii">
<input v-model="row.endTime" :name="'rows['+id+'][endTime]'" class="form-control" :id="['endTime'+id]" size="16" type="text" value="" >
<span class="input-group-addon"><span class="glyphicon glyphicon-time"></span></span>
</div>
</div>
<div class="col-md-4">
<div class="form-group select_group" style="margin-left:5px;">
<label :for="['status' + id]" class="control-label">Status</label>
<select data-width="100%" v-model="row.status" :name="'rows['+id+'][status]'" :id="['status' + id]" class="select_picker" data-live-search="true" multiple >
<option value="Status 1">Status 1</option>
<option value="Status 2">Status 2</option>
</select>
</div>
</div>
<div class="col-xs-1">
<button type="button" class="btn btn_delete btn-xs btn-danger" v-on:click="delRow(id)">
<i class="fa fa-remove"></i> delete
</button>
</div>
</div>
</li>
</ul>
</div>
JS
app = new Vue({
el: '#app',
data: {
rows: []
},
methods: {
addRow: function () {
this.rows.push({startTime: '', endTime: '', status: []});
setTimeout(function () {
$('.select_picker').selectpicker('refresh');
$('.form_time').datetimepicker({format: 'LT'});
}.bind(this), 10);
},
delRow: function (id) {
this.rows.splice(id, 1);
}
},created:function() {
this.addRow();
}
});
This is the example : https://jsfiddle.net/rypLz1qg/9/
You really need to write a wrapper component. Vue expects to control the DOM and not to have you making DOM-changing things like the *picker calls at arbitrary times. However, a component gives you the opportunity to tell your component how to interact with the DOM in each of its lifecycle hooks so that its behavior can be consistent. See the JavaScript tab on the wrapper component example page.

Vue.js not updating template when changing child objects

I'm extending laravel spark and wanted to try to validate registration before actually sending it off.
All data is handled nicely and correctly from the server but the errors do not pop-up.
The code being used for validation is as following:
module.exports = {
...
methods: {
...
validate(field, value) {
var formData = {
field: field,
value: value
};
var form = new SparkForm(formData);
Spark.post('/register_validate', form).then(response => {
this.registerForm.addSuccess(field);
}).catch(errors => {
this.registerForm.addError(field, errors[field]);
});
}
}
};
After, i extended SparkForm and added these methods (successes is just a copy of errors, used to display validation success messages):
/**
* SparkForm helper class. Used to set common properties on all forms.
*/
window.SparkForm = function (data) {
...
this.addError = function(field, errors) {
form.successes.remove(field);
form.errors.add(field, errors);
},
this.addSuccess = function(field) {
form.errors.remove(field);
form.successes.add(field, true);
}
};
And finally i added some methods on the SparkFormErrors.
/**
* Spark form error collection class.
*/
window.SparkFormErrors = function () {
...
this.add = function(field, errors) {
this.errors[field] = errors;
};
this.remove = function(field) {
delete this.errors[field];
}
};
In the console no errors are shown and in the network tab i can see the correct messages coming true, also when i add a console log in for example the response callback i can see the actual errors or success messages. But they are not drawn on screen.
For completeness i'll include the important content of the template blade file:
#extends('spark::layouts.app')
#section('content')
<spark-register-stripe inline-template>
<!-- Basic Profile -->
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">
{{ trans('auth.register.title') }}
</div>
<div class="panel-body">
<!-- Generic Error Message -->
<div class="alert alert-danger" v-if="registerForm.errors.has('form')">
#{{ registerForm.errors.get('form') }}
</div>
<!-- Registration Form -->
<form class="form-horizontal" role="form">
<!-- Name -->
<div class="form-group" :class="{'has-error': registerForm.errors.has('name')}">
<label class="col-md-4 control-label">{{ trans('user.name') }}</label>
<div class="col-md-6">
<input type="name" class="form-control" name="name" v-model="registerForm.name" autofocus #blur="validate('name', registerForm.name)">
<span class="help-block" v-show="registerForm.errors.has('name')">
#{{ registerForm.errors.get('name') }}
</span>
</div>
</div>
<!-- E-Mail Address -->
<div class="form-group" :class="{'has-error': registerForm.errors.has('email')}">
<label class="col-md-4 control-label">{{ trans('user.email') }}</label>
<div class="col-md-6">
<input type="email" class="form-control" name="email" v-model="registerForm.email" #blur="validate('email', registerForm.email)">
<span class="help-block" v-show="registerForm.errors.has('email')">
#{{ registerForm.errors.get('email') }}
</span>
</div>
</div>
<!-- Password -->
<div class="form-group" :class="{'has-error': registerForm.errors.has('password')}">
<label class="col-md-4 control-label">{{ trans('user.password') }}</label>
<div class="col-md-6">
<input type="password" class="form-control" name="password" v-model="registerForm.password" #blur="validate('password', registerForm.password)">
<span class="help-block" v-show="registerForm.errors.has('password')">
#{{ registerForm.errors.get('password') }}
</span>
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button class="btn btn-primary" #click.prevent="register" :disabled="registerForm.busy">
<span v-if="registerForm.busy">
<i class="fa fa-btn fa-spinner fa-spin"></i>{{ trans('auth.register.submitting') }}
</span>
<span v-else>
<i class="fa fa-btn fa-check-circle"></i>{{ trans('auth.register.submit') }}
</span>
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</spark-register-stripe>
#endsection
Any bright mind seeing what i'm missing/forgetting?