Multiselect vuejs and laravel form - laravel

I'm trying to use multiselect into my laravel project.
I'm able to select several option, in my vue pluggin, they are present, but when i submit my form, the value is null. There is something that i miss, but i don't know what ...
Here some of my code :
My Component :
<template>
<div>
<label class="typo__label">Chosir les pays à mettre en avant </label>
<multiselect
v-model="defaultCountry"
:options="options"
placeholder="Pays prioritaires"
:multiple="true"
:close-on-select="false"
:clear-on-select="false"
:searchable="true"
name="defaultCountry"
label="name"
track-by="name"
>
</multiselect>
</div>
</template>
<script>
import Multiselect from "vue-multiselect"
export default {
components: {
Multiselect,
},
name: "country-selector",
props: {
countriesList:
{
type: Object,
default: null,
},
},
data() {
return {
options: [],
defaultCountry: [],
}
},
methods: {
fillOptionsWithCountries() {
this.options = Object.entries(this.countriesList).map(
([code, name]) => ({code, name})
)
},
emitCountries(){
}
},
mounted() {
this.fillOptionsWithCountries();
}
}
</script>
<style scoped>
</style>
-My blade part :
<div class="form-group">
<dtc-country-selector
:countries-list="{{ json_encode(__('country')) }}"
>
</dtc-country-selector>
</div>
I tried to add "value" and v-model, but nothing change. I tried to use input hidden with v-model on my defaultCountries, but obviously my v-model isn't defined into blade.

Related

Vuejs Sibling Component data passing with method

I have a Vuejs Laravel page with Parent and 2 child
In my Parent template I have
<template>
<div>
<h4>DATA ANALYSIS</h4>
<dataAnalysisGraph />
<br>
<hr>
<dataAnalysisTable />
</div>
and in my dataAnalysisGraph child component i have a method that send request everytime the dropdown changes.
//dataAnalysisGraph
<template>
<div>
<div>
<select class="form-control select" name="" #change="GenerateChart" v-model='chartType' >
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
</div>
<br><br><br>
<highcharts class="hc" :options="chartOptions" ref="chart"></highcharts>
</div>
</template>
<script>
export default {
data() {
return {
data: [],
}
},
methods: {
GenerateChart(){
this.axios
.get('api/'+this.chartType)
.then(response => {
this.data = response.data;
});
},
},
created() {
this.GenerateChart();
},
};
</script>
In my dataAnalysisTable child component. I want to get the this.data from dataAnalysisGraph component and pass it to the dataAnalysisTable and updates it every time the dropdown form dataAnalysisGraph component changes.
this is my dataAnalysisTable component currently
<template>
<div>
{{data}}
</div>
</template>
<script>
export default {
data() {
return {
data: [],
};
},
methods: {
},
created() {
},
};
</script>
You can emit an event inside dataAnalysisGraph returning this.data to the parent and connect this value using v-model to the dataAnalysisTable component. You can read more in the vuejs guide specifically in the "Usage with v-model" section.

Get all values of custom input inside v-for vuejs

I created a vuejs custom input that I wanted to use to dynamically display inputs by using props within the custom input. I haven't shown them here because it would be too long.
By clicking on the submit button, which is also part of the custom input, I wanna be able to get the values of each input, but for some reason, I have only been able to get the value of the last input.
What am I doing wrong?
Custom input:
<template>
<div class="form-input">
<label :label="label" :for="name" v-if="label && type !='submit' ">{{label}} <span v-if="required">*</span></label>
<a v-if="multiple" href="#" class="btn">Upload</a>
<input v-model="inputVal" :multiple="multiple" v-if="type != 'textarea' && type != 'submit'" class="form-control" :required="required" :class="classes" :type="type" :name="name" :placeholder="placeHolder">
<textarea v-model="inputVal" :multiple="multiple" v-else-if="type != 'submit'" class="form-control" :required="required" :class="classes" :type="type" :name="name" :placeholder="placeHolder"></textarea>
<button :multiple="multiple" :name="name" v-else type="submit">{{label}}</button>
</div>
</template>
<script>
export default {
name: "Input",
data () {
return {
inputVal: null
}
},
watch: {
inputVal: {
handler: function(newValue, oldValue) {
this.$emit('input', newValue);
},
deep: true,
}
}
}
</script>
Form where custom input is used:
<template>
<div class="form container">
<form v-on:submit.prevent="sendMail" method="post" class="d-flex row shadow bg-dark border-right border-dark">
<h3 class="col-12">Contact me</h3>
<Input v-model="formInput" v-for="input in inputs" v-bind:key="input.name" :label="input.label" :multiple="input.multiple" :type="input.type" :name="input.name" :class="input.classes" :required="input.required"></Input>
</form>
</div>
</template>
<script>
import Input from "../components/Input";
export default {
name: "Contact",
components: {Input},
data() {
return {
formInput: null,
}
},
methods: {
sendMail () {
console.log(this.formInput);
}
}
}
</script>
The issue I see in your code is, you are using only one variable "formInput" ( in case of Contact component ) and "inputVal" ( in case of Input component ) but you have number of input fields from where you need data right.
The simplest way to deal with these kind of cases is to create a datastructure and loop through that.
For eg.
// Contact component ( i am making it simple to make you understand the scenario )
<template>
<div class="form container">
<form v-on:submit.prevent="sendMail" method="post" class="d-flex row shadow bg-dark border-right border-dark">
<h3 class="col-12">Contact me</h3>
<!-- we are looping through our data structure and binding each inputVal to this input -->
<input v-for="(input, i) in formInputs" :key="i" v-model="input.inputVal">
</form>
</div>
</template>
<script>
import Input from "../components/Input";
export default {
name: "Contact",
components: {Input},
data() {
return {
formInputs: [
{inputVal: ''},
{inputVal: ''},
{inputVal: ''},
],
}
},
methods: {
sendMail () {
// You can extract the data from formInputs as per your need
}
}
}
</script>

Search functionality with rest api prevent DDOSing the server

The Problem
I have a search component and component which implements the search component. When I type something in the search bar after 1/2 second of not typing (debounce) the server should be hit and the results should be returned.
The solution i am trying to implement comes from this post on Stackoverflow
The code
This leads me to the following code.
I have search.vue
<template>
<label for="search">
<input
id="search"
class="w-full py-2 px-1 border-gray-900 border"
type="text"
name=":searchTitle"
v-model="searchFilter"
:placeholder="searchPlaceholder"
autocomplete="off"
v-on:keydown="filteredDataset"
/>
</label>
</template>
<script>
import {debounce} from 'lodash';
export default {
props: {
searchPlaceholder: {
type: String,
required: false,
default: ''
},
searchName: {
type: String,
required: false,
default: 'search'
}
},
data() {
return {
searchFilter: '',
}
},
methods: {
filteredDataset() {
console.log('event fired');
this.$emit('searchValue', this.searchFilter);
}
},
}
</script>
And product.vue
<template>
<div>
<div class="my-4">
<search
search-placeholder=""
search-name=""
v-on:searchValue="filterValue = $event"
v-model="productsFiltered"
>
</search>
<div class="flex w-full py-1 border px-2 my-2" v-for="product in productsFiltered"> (...)
</div>
</div>
</div>
</div>
</template>
<script>
import {debounce} from 'lodash';
export default {
data() {
return {
products: [],
filterValue: '',
filteredProducts: ''
}
},
computed: {
productsFiltered: {
get(){
console.log('getter called');
return this.filteredProducts;
},
set: _.debounce(function(){
console.log('setter called');
if (this.filterValue.length < 1) {
this.filteredProducts = [];
}
axios.get(`${apiUrl}search/` + this.filterValue)
.then(response => {
this.products = response.data.products;
const filtered = [];
const regOption = new RegExp(this.filterValue, 'ig');
for (const product of this.products) {
if (this.filterValue.length < 1 || product.productname.match(regOption)) {
filtered.push(product);
}
}
this.filteredProducts = filtered;
});
}, 500)
}
},
}
</script>
The result
The result is that the setter in the computed property in product.vue does not get called and no data is fetched from the server. Any ideas on how to solve this?
Your first code block imports debounce but does not use it. It also declares a prop, searchName, that isn't used. These aren't central issues, but clutter makes it harder to figure out what's going on.
Your second code block uses v-model but does not follow the required conventions for getting v-model to work with components:
the component must take a prop named value
the component must emit input events to signal changes to value
You have the component emit searchValue events, and handle them with a v-on that sets a data item. You seem to expect the v-model to call the setter, but as I noted, you haven't hooked it up to do so.
From what's here, you don't even really need to store the input value. You just want to emit it when it changes. Here's a demo:
const searchComponent = {
template: '#search-template',
props: {
searchPlaceholder: {
type: String,
required: false,
default: ''
}
},
methods: {
filteredDataset(searchFilter) {
console.log('event fired');
this.$emit('input', searchFilter);
}
}
};
new Vue({
el: '#app',
data() {
return {
products: [],
filterValue: '',
filteredProducts: ''
}
},
components: {
searchComponent
},
computed: {
productsFiltered: {
get() {
console.log('getter called');
return this.filteredProducts;
},
set: _.debounce(function() {
console.log('setter called');
if (this.filterValue.length < 1) {
this.filteredProducts = [];
}
setTimeout(() => {
console.log("This is the axios call");
this.filteredProducts = ['one','two','three'];
}, 200);
}, 500)
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
<template id="search-template">
<label for="search">
<input
id="search"
class="w-full py-2 px-1 border-gray-900 border"
type="text"
name=":searchTitle"
:placeholder="searchPlaceholder"
autocomplete="off"
#input="filteredDataset"
/>
</label>
</template>
<div id="app">
<div class="my-4">
<search-component search-placeholder="enter something" v-model="productsFiltered">
</search-component>
<div class="flex w-full py-1 border px-2 my-2" v-for="product in productsFiltered"> (...)
</div>
</div>
</div>

Parsing dynamically loaded directives in Vue

I have a Vue component that makes a post request, and then outputs the returned html.
Sometimes, the html that is returned by the post contains Vue directives.
Is there a way to have Vue parse the returned html before it is output?
(In the longer term, I will rewrite this as a pure Vue solution, with the post request returning data rather than html. I'm after a short term solution if its possible).
EDIT:
Here's my stab based on thanksd's suggestion but I'm not sure how to bind the new Vue instance to an html element.
<template>
<div>
<input type="text" class="form-control" v-model="value" #change="getResults" ></input>
<div>
<template v-bind="results"></template>
</div>
</div>
</template>
<script>
import{eventHub} from '../utils/event.js'
export default {
data : function(){
return {
value : '',
results : {}
}
},
methods:{
getResults(){
if(this.value.length < 3){return;}
this.$http.post('/ajax/search',{search:this.value}).then((response)=>{
this.results = Vue({template:response.body});
});
},
},
}
After the post request returns you could create a new Vue instance, passing the html as the template and binding it to an element in your current Vue instance's template:
<template>
<div>
<input type="text" class="form-control" v-model="value" #change="getResults" ></input>
<div>
<div id="results"></div>
</div>
</div>
</template>
<script>
export default {
data() {
return { value: '' }
},
methods: {
getResults() {
if (this.value.length < 3) {
return;
}
this.$http.post('/ajax/search', { search: this.value }).then((response) => {
new Vue({ el: '#results', template: response.body });
});
}
}
}
</script>
Or as #Bert pointed out, you could add a <component> tag to your template and pass its definition via the is prop:
<template>
<div>
<input type="text" class="form-control" v-model="value" #change="getResults" ></input>
<component :is="results"/>
</div>
</template>
<script>
export default {
data() {
return {
value: '',
results: null
}
},
methods: {
getResults() {
if (this.value.length < 3) {
return;
}
this.$http.post('/ajax/search', { search: this.value }).then((response) => {
this.results = { template: response.body };
});
}
}
}
</script>

Vue Multiselect not displaying

I am trying to use Vue Multiselect V2 in my Laravel 5.3 project. I am using this example, http://monterail.github.io/vue-multiselect/#sub-single-select
I have the following setup, in my app.js file:
Vue.component('multiselect', require('./components/Multiselect.vue'));
var vm = new Vue({
el: '#app'
});
In the Multiselect.vue file
<script>
import Multiselect from 'vue-multiselect'
export default {
components: {
Multiselect
},
data () {
return {
value: '',
options: ['Select option', 'options', 'selected', 'mulitple', 'label', 'searchable', 'clearOnSelect', 'hideSelected', 'maxHeight', 'allowEmpty', 'showLabels', 'onChange', 'touched']
}
}
}
</script>
And I am calling it in the blade as below:
<div id="app">
<label class="typo__label">Single select</label>
<multiselect v-model="value" :options="options" :searchable="false" :close-on-select="false" :show-labels="false" placeholder="Pick a value"></multiselect>
<pre class="language-json"><code>#{{ value }}</code></pre>
</div>
This is how it displays in the DOM
<div id="app">
<label class="typo__label">Single select</label>
<!---->
<pre class="language-json"><code></code></pre>
</div>
Currently the dropdown does not display, and I don't see any errors in the console. I would have expected to add a template in somewhere but I couldn't find any mention of that in the Vue Multiselect docs.
For anyone having these issues, do not follow the examples on the official documentation. They do not work, rather use this from their Github page. https://github.com/monterail/vue-multiselect/tree/2.0#install--basic-usage
Basic example
<template>
<div>
<multiselect
v-model="selected"
:options="options">
</multiselect>
</div>
</template>
<script>
import Multiselect from 'vue-multiselect'
export default {
components: { Multiselect },
data () {
return {
selected: null,
options: ['list', 'of', 'options']
}
}
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
For updating the array from vue multiselect use #select and #remove events
Example: <multiselect #select="selectionChange" #remove="removeElement"> </multiselect>
Into methods add the next functions
methods: {
removeElement() {
this.$forceUpdate();
},
selectionChange() {
this.$forceUpdate();
},
}
this.$forceUpdate(); will update the state.

Resources