How to use query parameter in Vue search box? - vue.js

I have a page with a search box on it using Vue. What I want to do is this: when a user comes from another page with a parameter in the URL (e.g., myurl.com/?windows), I capture the parameter and populate the search field to run the search on that string when the page loads. If there's no parameter, nothing happens.
I'm capturing the string from the URL with JavaScript, but don't see how to get it in the input to run the search.... I created a method but don't see how to apply it.
<div id="app">
<input type="text" v-model="search" placeholder="Search Articles" />
<div v-for="article in filteredArticles" v-bind:key="article.id" class="container-fluid to-edges section1">
<div class="row">
<div class="col-md-4 col-sm-12 section0">
<div class="section0">
<a v-bind:href="article.url" v-bind:title="toUppercase(article.title)">
<img class="resp-img expand section0"
v-bind:src="article.src"
v-bind:alt="article.alt"/>
</a>
</div>
<div>
<h3 class="title-sec">{{ article.title }}</h3>
<p>{{ article.description }}</p>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
var pgURL = window.location.href;
var newURL = pgURL.split("?")[1];
console.log(newURL);
</script>
// Filters
Vue.filter('to-uppercase', function(value){
return value.toUpperCase();
});
new Vue({
el: "#app",
data: {
articles: [
{ id: 1, title: 'Trend Alert: Black Windows', category: 'Windows', description: 'Timeless, elegant, and universally flattering, black is an excellent color to add to any wardrobe – or any window. Get in the black with this chic design trend.', src: 'http://i1.adis.ws/i/stock/Trending_Polaroid_Black_Windows_2018_1?$trending-mobile$', url: '/{StorefrontContextRoot}/s/trending/trend-alert-black-windows', alt: 'Pantone Colors image' },
{ id: 2, title: 'Benefits of a Pass-Through Window', category: 'Windows', description: 'Whether you’re adding a pass-through window in order to enjoy an al fresco aperitif or for easier access to appetizers in the kitchen, we’re big fans of bringing the outdoors in.', src: 'http://i1.adis.ws/i/stock/polaroid_benefitsofapassthroughwindow655x536?$trending-mobile$', url: '/{StorefrontContextRoot}/s/trending/kitchen-pass-through-bar-window', alt: 'Benefits of a Pass-Through Window image' }, etc....
],
search: ''
},
methods: {
toUppercase: function(title){
return title.toUpperCase();
},
urlSearch: function(newURL) {
if (newURL) {
return this.search = newURL;
}
}
},
computed: {
filteredArticles: function() {
// returning updated array based on search term
return this.articles.filter((article) => {
return article.category.match(new RegExp(this.search, "i"));
});
}
}
})

You can call the urlSearch method during the mounted hook:
mounted() {
this.urlSearch(newURL)
},
methods: {
urlSearch(url) {
return this.search = url
}
},

Related

How to reuse vue method for different form inputs

I am making a gaming character creator menu, which has a lot of form inputs that essentialy work the same way, incrementing an attribute up or down by one on button click.
I am fairly new to using Vue and would like to be able to pass the character attribute eg. 'torso' aswell from the form element to my increase() and decrease() methods without to repeat code. Currently I can change by hardcoding in the this.form.heads++ or this.form.heads-- but would like to do something like this.form.val++ with val being which ever form element was clicked.
index.html
<form>
<div class="attr_block">
<div class="attr_row">
<div class="attr-col">
<label class="attr_label">Head Type</label>
</div>
<div class="input-col center">
<div class="left_arrow" #click="decrease()"></div>
<label class="attr_label">Type {{form.heads}}</label>
<div class="right_arrow" #click="increase()"></div>
</div>
</div>
<div class="attr_row">
<div class="attr-col">
<label class="attr_label">Torso Type</label>
</div>
<div class="input-col center">
<div class="left_arrow" #click="decrease()"></div>
<label class="attr_label">Type {{form.torso}}</label>
<div class="right_arrow" #click="increase()"></div>
</div>
</div>
</div>
</form>
app.js (cut down a little to focus on the issue)
const APP = new Vue({
el: '#app',
data: {
step:1,
display: false,
form: {
albedo:'mp_head_mr1_sc08_c0_000_ab',
heads:1,
torse:1,
},
},
methods: {
OpenUI() {
this.display = true
},
CloseUI() {
this.display = false
$.post('https://parks_creator/CloseUI')
},
decrease(){
this.form.heads--;
$.post('https://parks_creator/inputchange', JSON.stringify({
data: this.form
}))
},
increase(){
this.form.heads++;
$.post('https://parks_creator/inputchange', JSON.stringify({
data: this.form
}))
},
prev() {
this.step--;
},
next() {
this.step++;
},
change(){
$.post('https://parks_creator/inputchange', JSON.stringify({
data: this.form
}))
},
},
computed: {},
watch: {
},
})```
You can pass a character attribute to your methods as a parameter.
script:
methods: {
decrease(attribute){
this.form[attribute]--
},
increase(attribute){
this.form[attribute]++
}
}
template
<div #click="increase('heads')" />
<div #click="decrease('heads')" />
<div #click="increase('torso')" />
<div #click="decrease('torso')" />

How to toggle individual row at a time in vue for rows generated from an array? [duplicate]

I work with single file components and have a list in one of them. This list should work like a accordion, but as far as I can find in the Vuejs docs, it's not that easy to make each item open separately very easily. The data (questions and answers) is retrieved from an ajax call. I use jQuery for that, but would like to know how I can make the accordion work Vuejs-style. Any help would be appreciated!
Here's the code:
export default {
name: 'faq-component',
props: ['faqid', 'faqserviceurl', 'ctx'],
data: function () {
return {
showFaq: "",
totalFaqs: this.data,
isOpen: true
}
},
watch: {
'showFaq': function(val, faqid, faqserviceurl) {
var self = this;
$.ajax ({
url: this.faqserviceurl,
type: 'GET',
data: {id: this.faqid, q: val, scope:1},
success: function (data) {
self.totalFaqs = data;
},
error: function () {
$("#answer").html('Sorry');
}
});
}
},
methods: {
'toggle': function() {
this.isOpen = !this.isOpen
}
}
}
<template>
<div class="card faq-block">
<div class="card-block">
<form>
<div class="form-group">
<input class="form-control" type="text" placeholder="Your question" id="faq" v-model="showFaq">
</div>
</form>
<div id="answer"></div>
<ul class="faq">
<li v-for="faq in totalFaqs">
<p class="question" v-html="faq.vraag" v-bind:class={open:isOpen} #click="isOpen = !isOpen"></p>
<p class="answer" v-html="faq.antwoord"></p>
</li>
</ul>
</div>
</div>
</template>
Add an isOpen property to each object in totalFaqs and use that instead of your single isOpen property in data.
<p class="question" v-html="faq.vraag" v-bind:class={open: faq.isOpen} #click="faq.isOpen = !faq.isOpen"></p>
If you can't change the model from the server side, then add it client side.
success: function (data) {
data.forEach(d => self.$set(d, 'isOpen', false))
self.totalFaqs = data
}

Nested Components and Proper Wrapping Techniques in VueJS

I'm trying to put Bootstrap Select2 in a Vue wrapper. Somehow, my code works. But It doesn't seem proper to me.
I found a reference in here https://v2.vuejs.org/v2/examples/select2.html
In the VueJS website example, the el is empty. I didn't stick to this concept where in the new Vue part, they included a template because my el have sidebars and other stuffs also. What I did was I added a
<admin-access></admin-access>
to a section in the HTML.
I think I made my component nested which I'm skeptical if it is proper in VueJS.
Is there a better way to code this?
Templates
<template id="select2-template">
<select>
<slot></slot>
</select>
</template>
<template id="admin-access">
<div>
<transition name="access" enter-active-class="animated slideInRight" leave-active-class="animated slideOutRight" appear>
<div class="box box-solid box-primary" v-if="admin" key="create">
<div class="box-header with-border">
<i class="fa fa-text-width"></i>
<h3 class="box-title">Create Admin</h3>
</div>
<div class="box-body">
<form action="" class="search-form">
<div class="form-group">
<label for="search_user_admin">Search User</label>
<select2 name="search_user_admin" id="search-user-admin" class="form-control select2" :options="options" v-model="selected">
<option disabled value="0">Select one</option>
</select2>
</div>
</form>
</div>
</div>
</transition>
</div>
</template>
Script
Vue.component('admin-access', {
template: '#admin-access',
props: ['options', 'value'],
created: function() {
$('#search-user-admin').select2({
placeholder: "Select User",
allowClear: true
});
},
data: function() {
return {
admin : true,
selected : false
}
},
});
Vue.component('select2', {
template: '#select2-template',
data: function() {
return {
selected: 0,
options: [
{ id: 1, text: 'Hello' },
{ id: 2, text: 'Darkness' },
{ id: 3, text: 'My' },
{ id: 4, text: 'Old' },
{ id: 5, text: 'Friend' }
]
}
},
mounted: function() {
var vm = this;
$(this.$el).select2({
data: this.options,
placeholder: "Select an option",
})
.val(this.value)
.trigger('change')
.on('change', function () {
vm.$emit('input', this.value);
});
},
watch: {
value: function (value) {
$(this.$el).val(value).trigger('change');
},
options: function (options) {
$(this.$el).select2({ data: options });
}
},
destroyed: function () {
$(this.$el).off().select2('destroy');
}
});
var admin = new Vue({
el: '#app'
});
There's nothing wrong with nesting components in the first place. But you have to keep in mind that nesting them, or just in general, just wrapping generic html components like a select into a Vue Component is very costly when it comes to rendering, especially when you are nesting them. Typically when all your component does is just wrapping a simple HTMl element with some styles, you should probably just use generic HTML with classes here, for performance sake.
Also, I'd try to get rid of all the jQuery code inside of Vue Components. Vue offers all the functionality that jQuery has and it'll just increase loading times further.

Vuejs open/toggle single item

I work with single file components and have a list in one of them. This list should work like a accordion, but as far as I can find in the Vuejs docs, it's not that easy to make each item open separately very easily. The data (questions and answers) is retrieved from an ajax call. I use jQuery for that, but would like to know how I can make the accordion work Vuejs-style. Any help would be appreciated!
Here's the code:
export default {
name: 'faq-component',
props: ['faqid', 'faqserviceurl', 'ctx'],
data: function () {
return {
showFaq: "",
totalFaqs: this.data,
isOpen: true
}
},
watch: {
'showFaq': function(val, faqid, faqserviceurl) {
var self = this;
$.ajax ({
url: this.faqserviceurl,
type: 'GET',
data: {id: this.faqid, q: val, scope:1},
success: function (data) {
self.totalFaqs = data;
},
error: function () {
$("#answer").html('Sorry');
}
});
}
},
methods: {
'toggle': function() {
this.isOpen = !this.isOpen
}
}
}
<template>
<div class="card faq-block">
<div class="card-block">
<form>
<div class="form-group">
<input class="form-control" type="text" placeholder="Your question" id="faq" v-model="showFaq">
</div>
</form>
<div id="answer"></div>
<ul class="faq">
<li v-for="faq in totalFaqs">
<p class="question" v-html="faq.vraag" v-bind:class={open:isOpen} #click="isOpen = !isOpen"></p>
<p class="answer" v-html="faq.antwoord"></p>
</li>
</ul>
</div>
</div>
</template>
Add an isOpen property to each object in totalFaqs and use that instead of your single isOpen property in data.
<p class="question" v-html="faq.vraag" v-bind:class={open: faq.isOpen} #click="faq.isOpen = !faq.isOpen"></p>
If you can't change the model from the server side, then add it client side.
success: function (data) {
data.forEach(d => self.$set(d, 'isOpen', false))
self.totalFaqs = data
}

why doesn't my interpolated html display properly in vue.js?

The data in my app.js file is not being interpolated and displayed on the screen. I tried the older v-repeat. Could it be some missing items in the installation? Any help would be appreciated.
Here is my html
<!-- show the events -->
<div class="col-sm-6">
<div class="list-group">
<a href="#" class="list-group-item" v-for="event in events">
<!-- <h1>{{text}}</h1> -->
<h4 class="list-group-item-heading">
<i class="glyphicon glyphicon-bullhorn"></i>
{{ event.name }}
</h4>
<h5>
<i class="glyphicon glyphicon-calendar" v-if="event.date"></i>
{{ event.date }}
</h5>
<p class="list-group-item-text" v-if="event.description">{{ event.description }}</p>
<button class="btn btn-xs btn-danger" v-on="click: deleteEvent($index)">Delete</button>
</a>
</div>
</div>
And here is my js file
new Vue({
data: {
text: 'hello world',
event: { name: '', description: '', date: '' },
events: []
},
// Anything within the ready function will run when the application loads
ready: function() {
// When the application loads, we want to call the method that initializes
// some data
this.fetchEvents();
},
// Methods we want to use in our application are registered here
methods: {
// We dedicate a method to retrieving and setting some data
fetchEvents: function() {
var events = [
{
id: 1,
name: 'TIFF',
description: 'Toronto International Film Festival',
date: '2015-09-10'
},
{
id: 2,
name: 'The Martian Premiere',
description: 'The Martian comes to theatres.',
date: '2015-10-02'
},
{
id: 3,
name: 'SXSW',
description: 'Music, film and interactive festival in Austin, TX.',
date: '2016-03-11'
}
];
// $set is a convenience method provided by Vue that is similar to pushing
// data onto an array
this.$set('events', events);
},
// Adds an event to the existing events array
addEvent: function() {
if(this.event.name) {
this.events.push(this.event);
this.event = { name: '', description: '', date: '' };
}
}
}
});
Thanks for any help.
So, there are a few issues here. I've cleaned them up for you to look over.
Added the el: "#app" property to tell Vue where to mount your Vue.
Changed your ready to mounted (you could also use created).
Changed this.$set('events',events) to this.events = events.
Added the unreferenced deleteEvent method.
Fixed the click handler syntax for v-on:click.
So the code ends up looking like this.
new Vue({
el: "#app",
data: {
text: 'hello world',
event: { name: '', description: '', date: '' },
events: []
},
mounted: function() {
this.fetchEvents();
},
// Methods we want to use in our application are registered here
methods: {
// We dedicate a method to retrieving and setting some data
fetchEvents: function() {
var events = [...];
this.events = events;
},
// Adds an event to the existing events array
addEvent: function() {
if(this.event.name) {
this.events.push(this.event);
this.event = { name: '', description: '', date: '' };
}
},
deleteEvent(){
alert('delete this one')
}
}
});
Template
<div id="app">
<!-- show the events -->
<div class="col-sm-6">
<div class="list-group">
<a href="#" class="list-group-item" v-for="event in events">
<!-- <h1>{{text}}</h1> -->
<h4 class="list-group-item-heading">
<i class="glyphicon glyphicon-bullhorn"></i>
{{ event.name }}
</h4>
<h5>
<i class="glyphicon glyphicon-calendar" v-if="event.date"></i>
{{ event.date }}
</h5>
<p class="list-group-item-text" v-if="event.description">{{ event.description }}</p>
<button class="btn btn-xs btn-danger" v-on:click=" deleteEvent(event)">Delete</button>
</a>
</div>
</div>
</div>
Working example.