Laravel 5.7, Update <options> in a select drop-down with Vue - laravel

I'm writing my first Laravel Vue component and I have this error in console.
[Vue warn]: Computed property "options" was assigned to but it
has no setter.
The code is about two select dropdowns, one with continents and one with countries. I want that when I update the continent the countries get updated.
I just miss to update the options of the second select with the updated values that I have in this.countries.
After the continent change the this.countries variable get updated but the values of the options of the country_id select are not changing.
I've tried adding computed but I get this error.
What am I doing wrong?
<template>
<div>
<div class="form-group continent_id">
<select name="continent_id" v-model="continent_selected" id="continent_id" class="selectpicker" data-live-search="false" title="Pick a continent" v-on:change="getAllCountries(continents)">
<option v-if="continents.length>0" v-for="continent in continents" v-bind:value="continent.id">
{{ continent.name }}
</option>
</select>
</div>
<div class="form-group country_id">
<select name="country_id" v-model="country_selected" id="country_id" class="selectpicker" data-live-search="true" title="Pick a country">
<option v-for="(country, index) in countries" v-bind:value="country.id" >
{{ country.name }}
</option>
</select>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.');
this.loadData();
},
data() {
return {
continents: [],
countries: [],
continent_selected: '',
country_selected: '',
}
},
computed: {
get: function () {
return this.countries;
},
set: function (newValue) {
this.countries = newValue;
}
},
methods: {
loadData: function() {
axios.get('/api/continents')
.then((response) => {
// handle success
this.continents = response.data.data;
this.getAllCountries(this.continents);
})
.catch((error) => {
// handle error
console.log(error);
})
.then(() => {
// always executed
});
},
getAllCountries: function(continents) {
console.log(this.continent_selected);
console.log(continents);
var j = 0;
this.countries = [];
for (var i = 0, len = continents.length; i < len; i++) {
if (!this.continent_selected){
for (var key in continents[i].active_countries) {
this.countries[j] = {id: continents[i].active_countries[key], name: key};
j++;
}
}
else{
console.log("continent selected: "+ this.continent_selected);
for (var key in continents[i].active_countries) {
if (continents[i].id == this.continent_selected){
this.countries[j] = {id: continents[i].active_countries[key], name: key};
j++;
}
}
}
}
}
},
}
</script>

I have solved my issue that was mainly related to the computed set method of the variable optionCountries.
The dropdown was not updating because it use Bootsrap Select, so in order to show the new options it needs to be refreshed.
I've also figure out that I need to add a timeout to the refresh request.
This is the final code.
<template>
<div>
<div class="form-group continent_id">
<select name="continent_id" v-model="continent_selected" id="continent_id" class="selectpicker" data-live-search="false" title="Pick a continent" v-on:change="getAllCountries(continents)">
<option v-if="continents.length>0" v-for="continent in continents" v-bind:value="continent.id">
{{ continent.name }}
</option>
</select>
</div>
<div class="form-group country_id">
<select name="country_id" v-model="country_selected" id="country_id" class="selectpicker" data-live-search="true" title="Pick a country">
<option v-for="(country, index) in optionCountries" v-bind:value="country.id" >
{{ country.name }}
</option>
</select>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.');
this.loadData();
},
data() {
return {
continents: [],
countries: [],
continent_selected: '',
country_selected: '',
}
},
computed: {
optionCountries:{
get: function () {
return this.countries;
},
set: function (newValue) {
this.countries = newValue;
setTimeout(() => {
jQuery('.selectpicker').selectpicker('refresh');
}, 500);
}
}
},
methods: {
loadData: function() {
axios.get('/api/continents')
.then((response) => {
// handle success
this.continents = response.data.data;
this.getAllCountries(this.continents);
})
.catch((error) => {
// handle error
console.log(error);
})
.then(() => {
// always executed
});
},
getAllCountries: function(continents) {
var j = 0;
this.countries = [];
for (var i = 0, len = continents.length; i < len; i++) {
if (!this.continent_selected){
for (var key in continents[i].active_countries) {
this.countries[j] = {id: continents[i].active_countries[key], name: key};
j++;
}
}
else{
for (var key in continents[i].active_countries) {
if (continents[i].id == this.continent_selected){
this.countries[j] = {id: continents[i].active_countries[key], name: key};
j++;
}
}
this.optionCountries = this.countries;
}
}
}
},
}
</script>

In this line you are setting the value of options attribute:
this.options = this.options;
, but, this property is computed:
computed: {
options: function(event) {
return this.countries
}
},
In this case you can:
create a computed setter: https://v2.vuejs.org/v2/guide/computed.html#Computed-Setter
computed: {
options: {
get: function () {
return this.countries;
},
set: function (newValue) {
this.countries = newValue;
}
}
},
Set the value directly:
this.countries = this.options;

Related

How to disable select option in Vuejs

Good day! I am new in Vuejs. Please help me.
I want to disable the select option after I select and set the select to default value.
for example
<select name="recipient" #change="changeFunc($event)">
<option>--Select--</option>
<option v-for="recipient in recipients" :key="recipient.id" :value="recipient.id" :disabled="recipient.disabled">{{ recipient.name }}</option>
</select>
export default {
data: function() {
return {
recipients: [
{'id':3, 'name':'aaa'},
{'id':5, 'name':'bbb'},
{'id':8, 'name':'ccc'}
],
}
},
method: {
changeFunc(e) {
//TODO disable the select value and set to default value
}
}
}
Please help how to do it. Thank you.
Try the following code
<select name="recipient" v-model="selected" #change="changeFunc($event)">
<option>--Select--</option>
<option v-for="recipient in recipients" :key="recipient.id" :value="recipient.id" :disabled="recipient.disabled">{{ recipient.name }}</option>
</select>
export default {
data: function() {
return {
selected: {},
recipients: [
{'id':3, 'name':'aaa', 'disabled': false},
{'id':5, 'name':'bbb', 'disabled': false},
{'id':8, 'name':'ccc', 'disabled': false}
],
}
},
method: {
changeFunc(e) {
const index = this.recepients.findIndex((el) =>{
if(el.id == this.selected){
return true;
}
});
if(index){
this.recepeints[index].disabled = true;
this.selected = "";
}
}
}
}
The changeFunc() find index of selected value and set disabled to true and reset selected value
You can achieve this with a simple approach by iterating the input array on option change.
this.recipients.forEach(obj => {
obj['disabled'] = (obj.id === this.recipient)
});
Live Demo :
new Vue({
el: '#app',
data: {
recipient: '',
recipients: [
{'id':3, 'name':'aaa'},
{'id':5, 'name':'bbb'},
{'id':8, 'name':'ccc'}
]
},
methods: {
changeFunc() {
this.recipients.forEach(obj => {
obj['disabled'] = (obj.id === this.recipient)
});
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<select v-model="recipient" #change="changeFunc">
<option value="">--Select--</option>
<option v-for="recipient in recipients" :key="recipient.id" :value="recipient.id" :disabled="recipient.disabled">{{ recipient.name }}</option>
</select>
</div>

How to check if a laravel validator response is an error or not with Vue.js

I am using Laravel 7 and Vue.js 2.
I make a delete call with axios and if everything is correct I receive as a response the new data in the related tables to update a select and if there is an error I receive the errors of the Laravel validator.
The problem is that I have to understand with javascript if the response is an error or not... but I don't know how to do that.
This is my Vue component:
<template>
<form method="DELETE" #submit.prevent="removeTask">
<div class="form-group">
<title-form v-model="titleForm" :titleMessage="titleForm"></title-form>
</div>
<hr>
<div class="form-group">
<label for="tasks">Tasks:</label>
<select required v-model="user.tasks" class="form-control" id="tasks" #mouseover="displayResults(false, false)">
<option v-for="task in tasks_user" :value="task.id" :key="task.id">
{{ task.task_name }} - {{ task.task_description }}
</option>
</select>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
<hr>
<div class="form-group">
<validated-errors :errorsForm="errors" v-if="displayErrors===true"></validated-errors>
<!--<success-alert :success_message="successMessage" v-if="displaySuccess===true"></success-alert>-->
</div>
</form>
</template>
<script>
import ValidatedErrors from "./ValidatedErrors.vue"
import SuccessAlert from "./SuccessAlert.vue"
import TitleForm from "./TitleForm.vue"
export default {
components: {
'validated-errors': ValidatedErrors,
'success-alert': SuccessAlert,
'title-form': TitleForm
},
mounted() {
console.log('Component mounted.');
},
props: {
tasks_user: {
type: Array,
required: true,
default: () => [],
}
},
computed: {
titleForm: function () {
return "Remove a task from " + this.tasks_user[0].user_name;
}
},
data: function() {
return {
user: {
tasks: ""
},
errors: {},
displayErrors: false,
displaySuccess: false,
successMessage: "The task has been removed."
}
},
methods: {
removeTask: function() {
alert(this.user.tasks);
//axios.delete('/ticketsapp/public/api/remove_task_user?id=' + this.user.tasks)
axios.delete('/ticketsapp/public/api/remove_task_user?id=' + 101)
.then((response) => {
console.log(response.data);
if(typeof response.data[0].task_id !== "undefined") {
alert("There are no errors.");
} else {
alert("There are errors.");
}
if (typeof response.data[0].task_id === "undefined") { //problem
alert('noviva');
console.log(response.data);
this.errors = response.data;
this.displayErrors = true;
} else {
alert('viva');
this.tasks_user = response.data;
this.errors = {};
}
})
.catch(error => {
alert(noooooo);
console.log(error);
});
},
displayResults(successShow, errorShow) {
this.displayErrors = errorShow;
this.displaySuccess = successShow;
}
},
}
</script>
This is my method in the controller:
public function remove(Request $request) {
$validator = Validator::make($request->all(), [
'id' => 'required|exists:task_user,id'
]);
if ($validator->fails()) {
return response($validator->errors()); //problem
}
$task_user_id = $request->id;
$user_id = TaskUser::where('id', $task_user_id)->pluck('user_id')->first();
TaskUser::find($task_user_id)->delete();
$tasks_user = TaskUser::with(['user', 'task'])->get();
$tasks_user = TaskUser::where('user_id', $user_id)->get();
$tasks_user = TaskUserResource::collection($tasks_user);
return json_encode($tasks_user);
}
To distinguish the type of return I created this condition: if (typeof response.data[0].task_id === "undefined") but when that condition is true everything falls down and I receive the following error in the console:
Uncaught (in promise) ReferenceError: noooooo is not defined
So how can I do to distinguish the type of return of the API call?

How to get option key from a select using Vue.js with Laravel

I am using Vue.js 2 with Laravel 7.
I must do an insert to the db through a form submit but I am unable to get the option key of a select.
This is the select:
<div class="form-group">
<label for="room">Room:</label>
<select v-model="room" class="form-control" id="room">
<option v-for="room in rooms" :key="room.id">
{{ room.name }}
</option>
</select>
</div>
This is the script:
export default {
props: ['hours'],
mounted() {
console.log('Component mounted.');
this.loadUsers();
this.loadRooms();
},
data: function() {
return {
users: [],
rooms: [],
room: ''
}
},
methods: {
onCreate: function (args) {
let value = this.$refs.textareaObj.value;
alert(value);
},
loadUsers: function() {
axios.get('api/users')
.then((response) => {
this.users = response.data.data;
})
.catch(function(error) {
alert('noviva');
console.log(error);
});
},
loadRooms: function() {
axios.get('api/rooms')
.then((response) => {
this.rooms = response.data.data;
})
.catch(function(error) {
alert('noviva');
console.log(error);
});
},
insertMeeting: function() {
alert(this.room);
}
}
}
In the insert, I need to get the id of the room but I don't know how to do that. The function insertMeeting should alert that id. By the way, in that alert appears only the option value (room.name) but I am interested in the option key (room.id).
Can help?
Bind the option's value to its room's id, which will be stored in the room property bound to its parent, select, via v-modal:
<select v-model="room" class="form-control" id="room">
<option v-for="room in rooms" :value="room.id" :key="room.id">
{{ room.name }}
</option>
</select>
Demo here

Vue onclick sort child component in parent component

I'm using Vue cli and I have an array of users that I display as a profile card in template that I would like to sort by name.
I'm using Vuex to store data.
In the child component I have in my template:
<div class="outside" v-for="(item, index) in arrays" :key="item.id" v-bind:id="item.name">
<div class="stay"> <p class="n"> {{ item.name }}</p> </div>
and I'm getting the Vuex data:
computed: {
arrays () {
return this.$store.state.arrays
},
}
and my array contains:
arrays: [
{ name: 'Peter'},
{ name: 'Beth'},
{ name: 'Daniel'},
]
I used a function in the child component:
computed:{
sortedArray: function() {
function compare(a, b) {
if (a.name < b.name)
return -1;
if (a.name > b.name)
return 1;
return 0;
}
return this.arrays.sort(compare);
}
},
and changed the template to:
<div class="outside" v-for="(item, index) in sortedArrays" :key="item.id" v-bind:id="item.name">
But I'd like a way to do this onclick (in the parent), instead of always displaying it in order.
In my parent component I have:
<div class="begin">
<div class="b">Sort By:
<select name="sort" class="sort" >
<option value="name" #click="sortArrays()">Name</option>
</select>
</div>
<childComponent></childComponent>
</div>
I would like a sortArrays function I can use in the parent to display the user profile card in alphabetical order when I click on sort by name.
Thanks so much!
You can send the array from parent to child in props
Using Props
Parent
.html
<div class="begin">
<div class="b">Sort By:
<select name="sort" class="sort" >
<option value="name" #click="sortArray()">Name</option>
</select>
</div>
<childComponent :arrays=arrays></childComponent>
</div>
.Vue
new Vue({
data() {
return {
arrays: this.$store.state.arrays
}
},
methods: {
sortArray: function() {
this.array.sort((a, b) => return a.name < b.name ? -1 : 1)
}
}
})
Child
.Vue
new Vue({
props: ['arrays']
})
Using Sort flag
Child component will accept a sort prop from parent. When Sort is clicked on parent it will be true and sent to child. On Prop Change, the child will sort and display the sorted array.
Child
.Vue
new Vue({
props: ['isSorted],
watch: {
isSorted: function(oldval, newVal){
if(newVal) {
// Do the sorting
}
}
}
})
Parent
.Vue
new Vue({ data(){ return { isSorted: false }}, methods: { sortArrays: { this.isSorted = true; } }})
.html
<child-component :isSorted=isSorted></child-component>
Update
The change event can be binded to select.
<select name="sort" class="sort" #change="sortArray" v-model="filter">
<option value="name">Name</option>
</select>
In Vue
props: { filter: ''},
methods: { sortArray: function() { if(this.filter === 'name') // Do the sorting }}
Add a showSorted in your data with a bool value:
data(){
return {
showSorted: false
}
}
then edit your computed to use showSorted:
computed:{
sortedArray: function() {
function compare(a, b) {
if (a.name < b.name)
return -1;
if (a.name > b.name)
return 1;
return 0;
}
if(this.showSorted){
return this.arrays.sort(compare);
}
return this.arrays;
}
},
and then onClick you just have to set this.showSorted to true;
Move the array, sortedArray computer properties to the parent component
And pass the array you want to be displayed to the child component as prop
handle all the logic in the parent component, here is a code snippet below that shows the basic operation. Hope you can make changes as you need.
var ChildComponent = {
template: "<div>{{ sortedArray }}</div>",
props: {
sortedArray: {
type: Array,
default: () => [],
}
}
}
var app = new Vue({
el: '#app',
components: { ChildComponent },
data: {
sortBy: "",
array: [
{ name: 'Peter', age: 24},
{ name: 'Beth', age: 15},
{ name: 'Daniel', age: 30},
]
},
computed: {
displayArray() {
// declare the compare functions
// for name
function cmpName(a, b) {
if (a.name < b.name)
return -1;
if (a.name > b.name)
return 1;
return 0;
}
// for age
function cmpAge(a, b) {
if (a.age < b.age)
return -1;
if (a.age > b.age)
return 1;
return 0;
}
if (this.sortBy === "name") return this.array.sort(cmpName);
else if (this.sortBy === "age") return this.array.sort(cmpAge);
else return this.array;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<select v-model="sortBy">
<option value="">Please select a value</option>
<option value="name">name</option>
<option value="age">age</option>
</select>
<child-component :sorted-array="displayArray" />
</div>

TypeError: _this2.categoryOptions.find is not a function

I'm trying to create a select option that will show the category when it's been saved. The problem I'm having is that I'm getting this error in my console
[Vue warn]: Error in render: "TypeError: _this2.categoryOptions.find is not a function"
Here is my code
<template>
<div>
<select class="form-control" v-model="addCategory" name="category">
<option v-for="category in categoryOptions" :value="category.id">{{ category.name }}</option>
</select>
</div>
</template>
<script>
export default {
props: ['product', 'categories'],
data() {
return {
addCategory: null,
categoryOptions: []
}
},
mounted() {
axios.get('/admin/products/'+this.product.id+'/category').then((response) => {
this.categoryOptions = response.data;
});
},
computed: {
categoryOptions(){
let options = [];
options.push({id:0, text: "Please select one"});
let filteredCategory = this.categories.filter(category => {
return this.categoryOptions.find(selected => categoryOptions.category_id === category.id) == null;
});
filteredCategory.forEach(sc => {
options.push({id: sc.id, text: sc.name});
});
return options;
}
},
}
</script>
Replace:
let filteredCategory = this.categories.filter(category => {
return this.categoryOptions.find(selected => categoryOptions.category_id === category.id) == null;
});
per
let filteredCategory = this.categories.filter(category => {
return this.categoryOptions.find(selected => selected.category_id === category.id) == null;
});
note that you just forgot to replace categoryOptions with selected. But to ensure that the component is loaded, I advise you to make the props categories required, and ensure that it is persisted for the component before rendering.
<script>
export default {
props: {
'product',
'categories': {
type: [Array, Object],
required: true,
},
},
...
}
</script>
Another tip if you use the chrome browser, is to use a very cool extension which is Vue.js devtools to follow the status of your application.

Resources