Vue.js select2 Wrapper Component change event - javascript

I'm using jquery select2 Wrapper Component. This is my code,
<select2 :options="brands_options" #change="onChange" v-model="brand">
<option disabled value="0">Select one</option>
</select2>
<select2 :options="models_options" v-model="model">
<option disabled value="0">Select one</option>
</select2>
<script type="text/x-template" id="select2-template">
<select>
<slot></slot>
</select>
</script>
<script type="text/javascript">
Vue.component('select2', {
props: ['options', 'value'],
template: '#select2-template',
mounted: function () {
var vm = this
$(this.$el)
// init select2
.select2({data: this.options})
.val(this.value)
.trigger('change')
// emit event on change.
.on('change', function () {
vm.$emit('input', this.value);
});
},
watch: {
value: function (value) {
// update value
$(this.$el)
.val(value)
.trigger('change')
},
options: function (options) {
// update options
$(this.$el).empty().select2({data: options});
}
},
destroyed: function () {
$(this.$el).off().select2('destroy');
}
});
var vm = new Vue({
el: '#el',
delimiters: ["[[", "]]"],
data: {
brand: 0,
model: 0,
brands_options: [],
models_options: [],
cross_border: true,
},
created: function () {
this.loadBrands();
},
methods: {
loadBrands: function () {
var vm = this;
axios.get('http://localhost:81/tenly/public/get_brands')
.then(function (response) {
vm.brands_options = response.data;
{# alert(JSON.stringify(vm.brands_options[0].id));#}
axios.get('http://localhost:81/tenly/public/get_brand_model/' + vm.brands_options[0].id)
.then(function (response) {
vm.models_options = response.data;
{# alert(JSON.stringify(vm.models_options));#}
})
.catch(function (error) {
vm.models_options = 'An error occured' + error;
});
})
.catch(function (error) {
vm.brands_options = 'An error occured' + error;
});
},
onChange(event) {
alert(event.target.value);
}
}
});
</script>
Here I'm trying to alert value from onchange event. think #change="onChange" I added is wrong but not sure how to add correctly. It would be great if someone can help achieve it.

Since you're emitting an event from child component select2 via this statement :
vm.$emit('input', this.value);
so you should do something like :
<select2 :options="brands_options" #input="onChange" v-model="brand">
<option disabled value="0">Select one</option>
</select2>
and add onChange to the methods of the Vue instance as follows :
methods: {
onChange(value){
//do whatever you want with value
},
loadBrands: function () {
....

Related

Functionality like $(document).ready( with Vue.js

I wrote a small code with Laravel, Vue and JQuery, which works fine. Now I want to remove JQuery and run all with Vue and Axios.
Here's my template:
<ul id="product_list" class="vue-list-wrapper list-wrapper" data-rest="{{ route('rest_get_products', ["id"=>$product_type_id]) }}" data-pagination="0">
<li v-for="item in items">
<div class="item-name item-section">#{{ item.name }}</div>
...bla bla...
</li>
</ul>
Following code actually works and I can render what I get from AJAX. I know how to apply Axios, no problem.
The point I'm confused about: How can I ensure $(document).ready( functionality with Vue?
(function(){
"use strict";
function init_vue_list(){
var vue_list_handler = new Vue({
el: '.vue-list-wrapper',
data: {
items: []
},
mounted: function (event) {
var self = this;
var ajax_url = this.$el.getAttribute('data-rest');
$.ajax({ // No problem to convert this to Axios.
url: ajax_url,
method: 'GET',
success: function (data) {
self.items = data;
},
error: function (error) {
console.log(error);
}
});
},
methods:{
open_production:function(event){
}
}
});
}
$(document).ready( // I'm confused how I can replace this with Vue.
function(){
if($('.vue-list-wrapper').length > 0) {
init_vue_list();
}
}
);
})(document, $);
The recommended way by vue to do this is using mounted().
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
}
Check: https://v2.vuejs.org/v2/api/#mounted
Similar approach, but without JQuery and just using Javascript:
mounted() {
document.addEventListener('DOMContentLoaded', function () {
// INSERT CODE HERE
});
}
You can do this inside mounted, assuming that you are using JQuery:
mounted() {
$(function() {
// Your code goes here...
})
}

Vue wrapper component not working properly with axios

Hi I'm trying to change my vue wrapper component dropdown with axios. This is my code.
<html>
<head>
<title>title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</style>
</head>
<body>
<div id="el"></div>
<script type="text/x-template" id="demo-template">
<div>
<p>Selected: {{ input.selected }}</p>
<select2 :options="options" v-model="input.selected">
<option disabled value="0">Select one</option>
</select2>
</div>
</script>
<script type="text/x-template" id="select2-template">
<select>
<slot></slot>
</select>
</script>
<script src="http://themestarz.net/html/craigs/assets/js/jquery-3.3.1.min.js"></script>
<script src="https://unpkg.com/vue#2.5.17/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
Vue.component('select2', {
props: ['options', 'value'],
template: '#select2-template',
mounted: function () {
var vm = this;
$(this.$el)
// init select2
.select2({data: this.options})
.val(this.value)
.trigger('change')
// emit event on change.
.on('change', function () {
vm.$emit('input', this.value)
})
},
watch: {
value: function (value) {
// update value
$(this.$el)
.val(value)
.trigger('change')
},
options: function (options) {
// update options
$(this.$el).empty().select2({data: options})
}
},
destroyed: function () {
$(this.$el).off().select2('destroy')
}
});
var vm = new Vue({
el: '#el',
template: '#demo-template',
data: {
input: {
selected: "all"
},
options: []
},
created: function () {
this.mymethod();
},
methods: {
mymethod: function () {
var vm = this;
axios.get('https://api.coindesk.com/v1/bpi/currentprice.json')
.then(function (response) {
vm.options = [
{id: 'all', text: 'All'},
{id: 1, text: 'Hello'},
{id: 2, text: 'World'},
{id: 3, text: 'Bye'}
];
vm.input.selected = 2;
})
.catch(function (error) {
console.log(error);
});
}
}
});
</script>
</body>
</html>
The problem I have is when I try to add selected item it's not working inside axios. And it's working properly outside axios.
vm.input.selected = 2;
I got selected the all initially as the image shows. Think ajax call does not matter so I reduced the code complexity a bit. Thanks.
You need to recreate the input object inside the axios:
vm.input = { selected: 2 };
axios.get('https://api.coindesk.com/v1/bpi/currentprice.json')
.then(function (response) {
vm.options = [
{id: 'all', text: 'All'},
{id: 1, text: 'Hello'},
{id: 2, text: 'World'},
{id: 3, text: 'Bye'}
];
// recreate the 'input' object for reactivity
vm.input = { selected: 2 };
})
.catch(function (error) {
console.log(error);
});
You've encountered change detection caveat.
Use Vue.set(vm.input, 'selected', value) (or vm.$set) to update properties of your objects.
seems like I had issues with with wrapper component. After I changed the order of options and value in the component's watch this was fixed. I'm adding this in case anyone in the future faced the same issue.
watch: {
options: function(options) {
// update options
$(this.$el).empty().select2({
data: options
})
},
value: function(value) {
// update value
$(this.$el)
.val(value)
.trigger('change')
}
},

Vue.js: Passing anonymous functions in props object

I'm trying to extend the select2 example to be more practically useful. So far I've added multiselect functionality, and trying to allow custom select2 configuration.
https://jsfiddle.net/5ytm3LL6/
It appears that function properties of props objects are being stripped when handed to component.
What is a solution for a parent to give widget component configuration with js functions?
I'm somewhat confused, because the embedded version actually shows params being transferred properly in the built-in console output, while jsfiddle browser console output does not. However, both versions do not pass the functions to select2 widget.
Vue.component('select2', {
props: ['options', 'value', 'params'],
template: '<select><slot></slot></select>',
mounted: function () {
var vm = this, params = $.extend({}, this.params || {});
console.log(this.params, params);
params.data = this.options;
$(this.$el).val(this.value).select2(params).on('change', function () {
vm.$emit('input', $(vm.$el).val());
});
},
watch: {
value: function (value) {
var $el = $(this.$el);
if (!_.isEqual($el.val(), value)) {
$el.select2('val', value);
}
},
options: function (options) {
$(this.$el).select2({ data: options });
}
},
destroyed: function () { $(this.$el).off().select2('destroy'); }
});
new Vue({
el:'#app',
data: function () {
return {
value: 'a',
options: [{id:'a', label:'A'}, {id:'b', label:'B'}],
params: {
test: 'TEST',
formatSelection: function (item) {return item.label;},
formatResult: function (item) {return item.label;}
}
};
}
});
<link rel="stylesheet" type="text/css" href="https://unpkg.com/select2/dist/css/select2.css"></style>
<script src="https://unpkg.com/lodash"></script>
<script src="https://unpkg.com/jquery"></script>
<script src="https://unpkg.com/select2"></script>
<script src="https://unpkg.com/vue"></script>
<div id="app">
<select2 v-model="value" :options="options" :params="params"></select2>
</div>
I see js functions you have passed being printed in console log. I have updated your fiddle and put following logs in select2 compoent:
console.log(params.formatResult);
console.log(params.formatSelection);
Which prints following:
function (item) {return item.label;}
function (item) {return item.label;}

Vuejs directive binding a model

I'm using a Vuejs 1.0 directive to turn a select field (single and multiple) into a Select2 jQuery plugin field.
Vue.directive('select2', {
twoWay: true,
priority: 1000,
params: ['max-items'],
bind: function () {
var self = this;
console.log(self.params);
$(this.el)
.select2({
maximumSelectionLength: self.params.maxItems,
theme: 'bootstrap',
closeOnSelect: true
})
.on('change', function () {
var i, len, option, ref, values;
if (self.el.hasAttribute('multiple')) {
values = [];
ref = self.el.selectedOptions;
for (i = 0, len = ref.length; i < len; i++) {
option = ref[i];
values.push(option.value);
}
return self.set(values);
} else {
return self.set(self.el.value);
}
})
},
update: function (value, oldValue) {
$(this.el).val(value).trigger('change')
},
unbind: function () {
$(this.el).off().select2('destroy')
}
});
This all works fine. I'm also trying to bind a model to the value of the field but can't seem to get it to bind properly.
<select class="form-control" name="genre" v-model="upload.genre" v-select2="">
<option value="50">Abstract</option>
<option value="159">Acapella</option>
<option value="80">Acid</option>
...
</select>
The upload.genre property does not update automatically.
v-model is actually syntactic sugar on passing a prop and on change event setting the value, so following:
<input v-model="something">
is equivalent of
<input v-bind:value="something" v-on:input="something = $event.target.value">
You have to also make similar changes, You can see this type code in the select2 example provided by vue team.
.on('change', function () {
vm.$emit('input', this.value)
})
With Vue 1.0
As you are using vue 1.0, there is a two-way options for directives which helps to write data back to the Vue instance, you need to pass in twoWay: true. This option allows the use of this.set(value) inside the directive:
Vue.directive('select2', {
twoWay: true,
bind: function () {
this.handler = function () {
// set data back to the vm.
// If the directive is bound as v-select2="upload.genre",
// this will attempt to set `vm.upload.genre` with the
// given value.
this.set(this.el.value)
}.bind(this)
this.el.addEventListener('input', this.handler)
},
unbind: function () {
this.el.removeEventListener('input', this.handler)
}
})
and in HTML:
<select class="form-control" name="genre" v-select2="upload.genre">

Vue.js with laravel 5.3

I'm using Laravel 5.3 with Vue.js(very new to this).
Here's my current code
app.js
var vm = new Vue({
el: '#app',
data: {
messages: []
},
ready: function(){
this.getMessages();
},
methods: {
getMessages: function(){
this.$http.get('api/messages').then((response) => {
this.$set('messages', data);
}, (response) => {
});
}
}
});
api.php route is very simple
Route::get('/messages', function() {
return Message::latest()->get();
});
Note: here when i try access the route directly as localhost:8000/api/messages i get the array with the full data
On my view i have
<div class="content" id="app">
<tr v-for="message in messages">
<td> #{{ message}} </td>
</tr>
</div>
I have included online libraries for all jquery, vue.js, and vue.resource.
When i use vue.js debugger it shows that it returns messages[] but it's empty.
I have followed a lot of examples but couldn't get it to work.
Any help is greatly appreciated
if you are using vue.js 2.0 , ready is deprecated now, you may use mounted instead
mounted: function () {
this.$nextTick(function () {
this.getMessages();
})
}
Vue.js Docs
Since you are using the arrow syntax, then I switched to full ES2015 Code
getMessages() {
this.$http.get('api/messages')
.then( result => {
this.messages = result.json()
})
}
Try this:
var vm = new Vue({
el: '#app',
data: {
messages: []
},
ready: function(){
this.getMessages();
},
methods: {
getMessages: function(){
let ctrl = this;
this.$http.get('api/messages').then((response) => {
this.messages = response.data;
});
}
}
});

Categories