Prop mutating warning in VUE - laravel

I got an vue-warning (which results to as an error on my end coz my code is not working) that says:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "editmode"
With it, tried the suggestion here but can't make it work. Below is my work:
props:{
editmode:{
type: Boolean,
default: false,
}
},
methods:{
toggleM(){
var editmode = this.editmode;
editmode = !editmode;
this.editmode = editmode;
if(editmode == false){
//dothis
}else{
//dothat
}
},
}
TEMPLATE
<template>
<div class="ui-table-container-body">
<div class="ui-table" v-if="Boolean(items.length) || Boolean(Object.keys(items).length)" v-cloak>
<ui-table-body ref="body" v-model="items"
:editmode="editmode"
>
</ui-table-body>
</div>
</div>
</template>
The line this.editmode = editmode; is the one pointed in my console, is there any way I can surpass this?

You must use a data variable as a gateway to your prop.
In your component, the code code should look like this:
props:{
editmode:{
type: Boolean,
default: false,
}
},
data: {
dataEditMode = false
},
watch: {
'editmode': {
handler: 'onEditmodeChanged',
immediate: true,
},
'dataEditMode': {
handler: 'onDataEditModeChanged'
}
},
methods:{
toggleM(){
var editmode = this.dataEditMode;
editmode = !editmode;
this.dataEditMode = editmode;
if(editmode == false){
//dothis
}else{
//dothat
}
},
onEditmodeChanged (newVal) {
this.dataEditMode = newVal
},
onDataEditModeChanged (newVal) {
this.$emit('editmodeChanged', newVal)
}
}
and the the inclusion of this component in your parent-component should look like this:
<my-component-name :editmode="editmode" #editmodeChanged="(e) => { editmode = e }"></my-component-name>

You shouldn't mutate props from the component itself. See the One Way Data Flow section of the guide. You can use a prop as the initial value, and then keep a value in the data section and mutate that:
props: {
editmode: {
type: Boolean,
default: false,
}
},
data () {
return {
emode: this.editmode,
}
},
methods: {
toggleM () {
let editmode = this.emode;
editmode = !editmode;
this.emode = editmode;
if (editmode == false) {
// dothis
} else {
// dothat
}
},
}
Demo
Vue.component('editbox', {
template: '<div>' +
'<button #click="toggleM">{{ btext }}</button>' +
'<input v-if="emode" />' +
'</div>',
props: ['editmode'],
data () {
return {
emode: this.editmode,
}
},
computed: {
btext () {
return this.emode ? "Text" : "Edit";
}
},
methods:{
toggleM() {
this.emode = !this.emode;
},
}
})
var app = new Vue({
el: '#app',
data: {
mode: true,
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<editbox :editmode="mode" />
</div>

I would send back an event to the parent so it could modify its value:
For example (not tested):
Child Component
props:{
editmode:{
type: Boolean,
default: false,
}
},
methods:{
toggleM(){
var editmode = !this.editmode;
this.$emit('changeEditMode', editmode);
if (editmode == false){
//dothis
} else {
//dothat
}
},
}
Parent
<child-component #changeEditMode="editModeChanged" :editmode="editmode"></child-component>
...
methods:{
editModeChanged(value){
this.editmode = value
},
}

Related

Created() is not executing after delete. Inertia + Vuejs

I'm new with Vuejs and Inertia. I've created a simple dashboard using both and Laravel.
Everything works well except after delete an element, created() is not executing. Surely I'm making a mistake, but I don't found it.
Example:
Initial page:
If I press 'editar', I can edit the value.
I edit first value, 17 to 18.
And this works well. But if I want to delete the first element. The table is updated, because it depends from a prop (elements), but the elements below depend with created().
After removing:
Index.vue
<div class="mb-6 bg-white rounded-md shadow" v-if="elementsFiltrats[0].elements.length != 0">
<h2 class="pressupost_gastos_h2">General</h2>
<ul class="pressupost_gastos_ul">
<li v-for="e in elementsFiltrats[0].elements" :key="e.id" class="hover:bg-gray-100 focus-within:bg-gray-100 pressupost_gastos_li">
{{e.nom}} - {{e.valor.toFixed(2)}}€
</li>
</ul>
<h3 class="pressupost_gastos_h3">Total: {{elementsFiltrats[0].valor_total.toFixed(2)}}€ (+10%) = {{(elementsFiltrats[0].valor_total*1.10).toFixed(2)}}€</h3>
</div>
export default {
metaInfo: { title: 'Presupuestos' },
components: {
Icon,
Pagination,
SearchFilter,
},
layout: Layout,
props: {
filters: Object,
exercici: Object,
elements: Array,
comunitat: Object,
elementsagrupats: Array
},
data() {
return {
form: {
search: this.filters.search
},
elementsFiltrats: []
}
},
created(){
this.splitByType()
},
watch: {
form: {
deep: true,
handler: throttle(function() {
this.$inertia.get(this.route('pressuposts', this.exercici.id), pickBy(this.form), { preserveState: true })
}, 150),
},
'$route' : 'splitByType'
},
methods: {
reset() {
this.form = mapValues(this.form, () => null)
},
destroy(idExercici, idPressupost) {
if (confirm("Seguro que quieres eliminar el elemento del presupuesto?")) {
this.$inertia.delete(this.route('pressuposts.destroy', [idExercici, idPressupost]))
}
},
edit(idExercici, idPressupost) {
this.$inertia.get(this.route('pressuposts.edit', [idExercici, idPressupost]))
},
splitByType(){
console.log("hola")
// 0 general
// 1 escales
// 2 garatge
// valor_total
var elem = [
{
'elements' : [],
'valor_total': 0
},
{
'elements' : [],
'valor_total': 0
},
{
'elements' : [],
'valor_total': 0
}
];
this.elementsagrupats.forEach(element => {
var pos = 0;
if(element.casella === 'general'){
pos = 0;
}else if(element.casella === 'escales'){
pos = 1;
}else{
pos = 2;
}
elem[pos].elements.push({
'nom' : element['nom'],
'valor' : element['valor_total']
});
elem[pos].valor_total += element.valor_total;
});
this.elementsFiltrats = elem;
}
},
}
Controller:
Update method has:
Update code...
return Redirect::route('pressuposts', $exercici->id)->with('success', 'Element actualitzat.');
Destroy method has:
Delete code...
return Redirect::route('pressuposts', $exercici->id)->with('success', 'Element eliminat.');
Any help is appreciated.
Thanks.

Laravel vue js Opening video in modal onClick not working

So what i'm trying to do is pretty simple but for some reason is not working please let me show you.
This is my navigation bar written in vue js and here i'm holding almost all my vue components.
Navigation.vue
<template>
<single-video :user_id="user_id" :video="this.video" :videoUser="this.videoUser"
:videoOptions="videoOptions"></single-video>
</template>
<script>
export default {
name: "Navigation",
data: function () {
return {
user_id: user_id,
// isLogin: isLogin,
video: {},
videoUser: {},
videoOptions: {
autoplay: true,
controls: true,
sources: [
{
src: "",
type: "application/x-mpegURL"
}
]
}
}
},
methods: {
openVideoModal(video){
this.videoOptions.sources[0].src = 'storage/videos/' + video.id + '/' + video.id + '.m3u8';
axios.get('video/' + video.channel_id).then(response => {
this.videoUser = response.data;
}).catch(error => {
if (error.response.status == 422) {
this.errors = error.response.data.errors;
}
console.log('Error');
});
this.video = video;
$('#singleVideo').modal('show');
},
},
}
</script>
In one of my child component i have this method which calling openVideoModal() function in navigation. With the video id. So far it's working.
<a #click="$parent.openVideoModal(video)">
This one is my single video component which display the video itself.And is in modal ("singleVideo" modal)
single-video.vue
<video v-if="this.video"
ref="videoPlayer"
id="my-video"
class="video-js vjs-default-skin vjs-big-play-centered"
controls
preload="auto"
width="749"
height="421"
:poster="this.video.thumbnail"
data-setup="{}">
</video>
<script>
import videojs from 'video.js';
export default {
props: ['user_id', 'video', 'videoUser', 'videoOptions'],
data: function () {
return {
errors: {},
singleVideo: {},
player: null
}
},
mounted() {
if(this.videoOptions.sources[0].src) {
this.player = videojs(this.$refs.videoPlayer, this.videoOptions, function onPlayerReady() {
})
}
},
methods: {
beforeDestroy() {
if (this.player) {
this.player.dispose()
}
}
}
</script>
Please don't delete my question it is very important to me to solve this. I'm going nuts over it. :(

How to pass data from a child component to the parent component using Laravel and Vue

I am using Vue.js 2 and Laravel 7.
I want to pass some errors from the child component to the parent controller. But for some reasons emit did not work.
This is the method of the child component AllocateTask:
methods: {
assignTask: function() {
this.form.task_id = this.user.tasks;
this.form.user_id = this.dev.id;
alert(this.form.task_id);
alert(this.form.user_id);
axios.post('/ticketsapp/public/api/store_task_user', this.form)
.then((response) => {
console.log(response.data);
alert('ok');
this.errors = response.data;
alert(Object.keys(this.errors).length);
if (Object.keys(this.errors).length === 0) {
alert('viva');
} else {
alert('noviva');
this.$emit('failure');
this.$emit('pass-errors', this.errors);
}
})
.catch(error => {
alert('no ok');
console.log(error);
});
}
}
This is the parent component TheReporterTickets:
<template>
<div>
<allocate-task :dev="dev" :tasks="tasks" #pass-errors="onPassErrors" #failure="displayErrors=true" #success="displaySuccess=true"></allocate-task>
<hr>
<validated-errors :errorsForm="errorsTicket" v-if="displayErrors===true"></validated-errors>
</div> </template>
<script>
import AllocateTask from "./AllocateTask.vue"
import ValidatedErrors from "./ValidatedErrors.vue"
export default {
components: {
'allocate-task': AllocateTask,
'validated-errors': ValidatedErrors
},
props: {
dev: {
type: Array,
required: true,
default: () => [],
},
tasks: {
type: Array,
required: true,
default: () => [],
}
},
mounted() {
console.log('Component mounted.');
},
data: function() {
return {
displayErrors: false,
errorsTicket: []
}
},
methods: {
onPassErrors(value) {
alert('error');
console.log('errors passed');
const values = Object.values(value);
this.errorsTicket = values;
console.log(this.errorsTicket);
}
}
} </script>
As you can imagine, I am unable to call the method onPassErrors located in the parent component. I visualize correctly the alert in the else statement of the child component, so I suppose that I am unable to pass the data from the child to the parent component.
Can help?

Property not defined error But it is defined Vue js Laravel

I have defined the function in my vue js file but it is giving me error for nameWithLang() function Please have a look
My Form
<multiselect v-model="selected" track-by="id" label="name" :options="options" :loading="isLoading" :internal-search="false" #search-change="getData" :multiple="true" :close-on-select="false" :hide-selected="true":internal-search="false" name="books[]" :show-labels="false" :custom-label="nameWithLang"></multiselect>
My vue js file
import AppForm from '../app-components/Form/AppForm';
Vue.component('coupon-form', {
mixins: [AppForm],
data: function() {
return {
form: {
name: '' ,
description: '' ,
valid_from: '' ,
valid_till: '' ,
discount: '' ,
enabled: false,
books: [],
},
isLoading: false,
options: [],
selected: [],
}
},
methods: {
nameWithLang({ name, sku }) {
return `${name} — ${sku}`
},
getData(query){
this.isLoading = true;
axios.post('/admin/books/find/'+query)
.then((response) => {
this.options = response.data;
this.isLoading = false;
})
.catch((error) => {
this.isLoading = false;
});
},
},
watch: {
selected (newValues) {
this.form.books = newValues.map(obj => obj.id)
}
}
});
Other properties and functions are working nameWithLang is not working
It gives me error this
Property or method "nameWithLang" is not defined on the instance but referenced during render.
why not you just return the value into a variable in data, and set the function into mounted/watch instead using the function to get the value.
just for refer, you can make the script like this :
import AppForm from '../app-components/Form/AppForm';
Vue.component('coupon-form', {
mixins: [AppForm],
data: function() {
return {
form: {
name: '' ,
description: '' ,
valid_from: '' ,
valid_till: '' ,
discount: '' ,
enabled: false,
books: [],
},
isLoading: false,
options: [],
selected: [],
newName: '',
}
},
methods: {
nameWithLang({ name, sku }) {
this.newName = `${name} — ${sku}`;
},
getData(query){
this.isLoading = true;
axios.post('/admin/books/find/'+query)
.then((response) => {
this.options = response.data;
this.isLoading = false;
})
.catch((error) => {
this.isLoading = false;
});
},
},
watch: {
selected (newValues) {
this.form.books = newValues.map(obj => obj.id)
this.nameWithLang();
}
}
});
then you can make the template like this:
<multiselect v-model="selected" track-by="id" label="name" :options="options" :loading="isLoading" :internal-search="false" #search-change="getData" :multiple="true" :close-on-select="false" :hide-selected="true":internal-search="false" name="books[]" :show-labels="false" :custom-label="newName"></multiselect>
this is just another way you can make it and the way i'm understand what actually you want to do. you want to pass the name with lang value into the :custom-label right? so why not just defined one more variable and add the value into the variable. so you just need to pass the value instead the function. in v-bind it's more appropiate to pass a property instead of a method

I want to show the value of label inside the pie graph. (vue-chartjs / pieceLabel)

I am a student studying vue.
I used Vue-chartjs to draw a graph, and I'd like to display the value on a pie graph.
But I don't know what to do.
Please help me...
the current situation (image) : enter image description here
My wish (image) : enter image description here
Vue.component('pie-chart', {
extends : VueChartJs.Pie,
props: ['data', 'options'],
mounted(){
this.renderPieChart();
},
computed: {
attendanceData : function(){
return this.data
}
},
methods : {
renderPieChart : function(){
this.renderChart(
{
labels: ['a','b','c','d'],
datasets: [{
backgroundColor: ['#10a236', '#f9cd41', '#fe7272', '#5c7add'],
data: [10,20,30,40]
}]
},
{
responsive: true,
maintainAspectRatio: false,
pieceLabel: {
render: 'value',
precision: 1,
}
}
)
}
},
watch : {
attendanceData : function(){
this.$data._chart.destroy();
this.renderPieChart();
}
}
});
As The dicusstion on tooltip of chart.js at Stackoverflow, uses plugin is one solution.
then as Vue chart.js guide said,
in mounted(), uses this.addPlugin to add your plugin like below demo:
Vue.config.productionTip = false
//below plugin is copied from https://stackoverflow.com/a/37989832/5665870
let pluginConfig = {
id: 'my-plugin',
beforeRender: function (chart) {
if (chart.config.options.showAllTooltips) {
// create an array of tooltips
// we can't use the chart tooltip because there is only one tooltip per chart
chart.pluginTooltips = [];
chart.config.data.datasets.forEach(function (dataset, i) {
chart.getDatasetMeta(i).data.forEach(function (sector, j) {
chart.pluginTooltips.push(new Chart.Tooltip({
_chart: chart.chart,
_chartInstance: chart,
_data: chart.data,
_options: chart.options.tooltips,
_active: [sector]
}, chart));
});
});
// turn off normal tooltips
chart.options.tooltips.enabled = false;
}
},
afterDraw: function (chart, easing) {
if (chart.config.options.showAllTooltips) {
// we don't want the permanent tooltips to animate, so don't do anything till the animation runs atleast once
if (!chart.allTooltipsOnce) {
if (easing !== 1)
return;
chart.allTooltipsOnce = true;
}
// turn on tooltips
chart.options.tooltips.enabled = true;
Chart.helpers.each(chart.pluginTooltips, function (tooltip) {
tooltip.initialize();
tooltip.update();
// we don't actually need this since we are not animating tooltips
tooltip.pivot();
tooltip.transition(easing).draw();
});
chart.options.tooltips.enabled = false;
}
}
}
Vue.component('pie-chart', {
extends : VueChartJs.Pie,
props: ['data', 'options'],
mounted(){
this.addPlugin(pluginConfig);
this.renderPieChart();
},
computed: {
attendanceData : function(){
return this.data
}
},
methods : {
renderPieChart : function(){
this.renderChart(
{
labels: ['a','b','c','d'],
datasets: [{
backgroundColor: ['#10a236', '#f9cd41', '#fe7272', '#5c7add'],
data: [10,20,30,40]
}]
},
{
responsive: true,
maintainAspectRatio: false,
pieceLabel: {
render: 'value',
precision: 1
},
showAllTooltips: true
}
)
}
},
watch : {
attendanceData : function(){
//this.$data._chart.destroy();
//this.renderPieChart();
}
}
})
var vm = new Vue({
el: '#app',
data: {
message: 'Hello World'
}
})
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.min.js"></script>
<script src="https://unpkg.com/vue-chartjs/dist/vue-chartjs.min.js"></script>
<div id="app">
<pie-chart></pie-chart>
</div>

Resources