Vuejs change prop value in component after select option component - javascript

I have 2 components, one creates an Editor using vue2-ace-editor, the other component is a select Option component. I Need to Change the theme of the Editor if I select another value in my select Option, so far my Editor component Looks like this:
<template>
<editor v-model="content" #init="editorInit" lang="javascript" theme="monokai"></editor> <!-- the default theme is monokai currently -->
</template>
<script>
export default {
props: [],
data () {
return {
language: 'javascript',
content: 'test',
}
},
components: {
editor: require('vue2-ace-editor'),
},
methods: {
editorInit () {
require('brace/ext/language_tools');
require('brace/mode/html');
require('brace/mode/javascript');
require('brace/mode/less');
require('brace/theme/clouds_midnight');
require('brace/theme/chrome');
require('brace/theme/ambiance');
}
},
}
</script>
And my select Option Looks like this:
<template>
<div>
<v-select
:items="themes"
v-model="themeSelection"
label="Select"
single-line
></v-select>
</div>
</template>
<script>
export default {
data () {
return {
themeSelection: null,
themes: [
{ text: 'ambiance' },
{ text: 'chrome' },
{ text: 'clouds_midnight' },
]
}
}
}
</script>
So basically if I select a different Option, I Need to Change the theme of the Editor, how do I manage this?

Related

Apply changes in nested class during disabled state in Vue 3

I have implemented radio buttons using Oruga Library in Vue 3, I want to check for the disabled state of the radio button and apply class accordingly.
The code I am using is given below.
<template>
<o-radio v-bind="$props" v-model="model">
<slot />
</o-radio>
</template>
<script lang="ts">
import { defineComponent } from "#vue/runtime-core";
import Radio from '../mixins/radio-mixins';
export default defineComponent({
name: "BaseRadio",
computed: {
model: {
get() {
return this.$props.nativeValue;
},
set(value: any) {
this.$emit("input", value);
},
},
},
emits: ["input"],
props: {
...Radio.props,
},
});
</script>
<style>
.b-radio.radio .check:before {
background : #A2A9AD;
}
.b-radio.radio .check:checked {
border-color: #A2A9AD;
}
</style>
I want to apply the two changes present in style tags during radio is disabled. How can i achieve that ?.
I want to check if the radio button is disabled and apply the styles when it is disabled.
Use the class prop .o-radio--disabled :
.b-radio.radio .o-radio--disabled {
...
}

Mutate props in Vue.js 3 - or how do I reflect a prop's changes to it's parents

I am migrating things from Vue.js 2 to Vue.js 3. During my migration, I just mentioned that eslint does warn me, because I am mutating props.
This is an example of an element that causes this:
<template>
<ToggleField
v-model="item.checked"
:name="`item.${name}.checked`"/>
</template>
<script>
import ToggleField from "./ToggleField";
export default {
name: 'TestField',
components: {ToggleField},
props: {
name: {
type: String,
required: true,
},
item: {
type: Object,
},
},
}
</script>
This element is deeply nested and every parent element passes the :item-attribute to the next "level" until it's finally displayed and changeable due v-model.
This is an example:
Parent view
<template>
<CustomForm name="test" :item="item" />
<!-- Reflect changes on item here -->
{{ item }}
</template>
<script>
import CustomForm from "./CustomForm";
export default {
components: {
CustomForm
},
data: () => ({
item:
{name: 'Foo', 'checked': false},
}),
}
</script>
CustomForm
<template>
<!-- Do other fancy stuff here -->
<TestField :name="name" :item="item"/>
</template>
<script>
import TestField from "./TestField";
export default {
name: 'CustomForm',
components: {TestField},
props: {
name: {
type: String,
required: true,
},
item: {
type: Object,
},
},
}
</script>
TestField
<template>
<ToggleField
v-model="item.checked"
:name="`item.${name}.checked`"/>
</template>
<script>
import ToggleField from "./ToggleField";
export default {
name: 'TestField',
components: {ToggleField},
props: {
name: {
type: String,
required: true,
},
item: {
type: Object,
},
},
}
</script>
So my question is: How can I update the item and reflect the changes to it's parent (and it's parent, and it's parent again, if necessary) without running into the prop-mutation?

VueJS select component in single file

I'm despaired. I absolutely have no clue anymore how to outsource html "select"-tag to a single Vue component file. I tried so much different ways but unfortunately I've never got it to work yet.
User component:
<custom-select :id="'continent'" :selected="user.continent" :options="continents" #change.native="changedContinent" class="some css classes" />
export default {
props: ['user'],
components: {
CustomSelect,
},
data() {
return {
continents: [
{ value: 'africa', text: 'Africa' },
{ value: 'america-north', text: 'America (North)' },
{ value: 'america-south', text: 'America (South)' },
{ value: 'asia', text: 'Asia' },
{ value: 'australia', text: 'Australia' },
{ value: 'europe', text: 'Europe' }
]
};
},
methods: {
updateUser() {
axios.post('/updateUser', this.user).then(() => {});
},
changedContinent() {
// Do something with the new selected continent like changing background relative to the selected continent
}
}
};
CustomSelect:
<template>
<select v-model="selected" class="some css classes">
<option v-for="option in options" :key="option.value" :value="option.value">{{ option.text }}</option>
</select>
</template>
<script>
export default { props: ['id', 'selected', 'label', 'options'] };
</script>
Whenever I change the selected value it should update user.continent value but instead, it throws the following error:
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:
"selected"
But I do not know how to handle this.
Could anyone help me please?Thank you in advance!
As the error message says, 'to avoid mutating a prop directly, use a data or computed property ...'
I modified your code to create the components in my test environment (Vue 2 using Vue CLI), and bound the CustomSelect to a new data property 'selectedContinent' initialized from the 'selected' prop. I am also updating the parent component (User component in your case) via a custom event. Here are my components. See the Vue Custom Events documentation
My CustomSelect.vue component:
<template>
<div class="custom-select">
<select v-model="selectedContinent" class="" v-on:change="changeContinent">
<option v-for="option in options" :key="option.value" :value="option.value">{{ option.text }}</option>
</select>
</div>
</template>
<script>
export default {
props: ['id', 'selected', 'label', 'options'],
data() {
return {
selectedContinent: this.selected
}
},
methods: {
changeContinent() {
//console.log('changeContinent: ' + this.selectedContinent);
this.$emit('change-continent-event', this.selectedContinent);
}
}
}
</script>
My Parent.vue component (your User):
<template>
<div class="parent">
<custom-select :id="'continent'" :selected="user.continent" :options="continents"
v-on:change-continent-event="changedContinent" />
</div>
</template>
<script>
import CustomSelect from './CustomSelect.vue'
export default {
props: ['user'],
components: {
CustomSelect,
},
data() {
return {
continents: [
{ value: 'africa', text: 'Africa' },
{ value: 'america-north', text: 'America (North)' },
{ value: 'america-south', text: 'America (South)' },
{ value: 'asia', text: 'Asia' },
{ value: 'australia', text: 'Australia' },
{ value: 'europe', text: 'Europe' }
]
};
},
methods: {
updateUser() {
//axios.post('/updateUser', this.user).then(() => { });
},
changedContinent(newContinent) {
// Do something with the new selected continent like changing background relative to the selected continent
console.log('changedContinent: ' + newContinent);
}
}
}
</script>

How to call api when change value in vue multiselect

How to call api when there is a change value event in vue multiselect?
i want change value in multiselect , call api and show in console. The following is my code.
<template>
<div>
<multiselect v-model="value" placeholder="Select Your Currency" label="title" track-by="title" :options="options" :option-height="10" :show-labels="false">
<template slot="singleLabel" slot-scope="props"><img class="option__image" :src="props.option.img" alt="No Man’s Sky"><span class="option__desc"><span class="option__title">{{ props.option.title }}</span></span></template>
<template slot="option" slot-scope="props"><img class="option__image" :src="props.option.img" alt="No Man’s Sky">
<div class="option__desc"><span class="option__title" :id="props.option.id">{{ props.option.title }}</span><span class="option__small">{{ props.option.desc }}</span></div>
</template>
</multiselect>
</div>
</template>
<script>
import Vue from 'vue';
import Multiselect from 'vue-multiselect'
Vue.component('multiselect', Multiselect);
export default {
props: ['options'],
components: {
Multiselect
},
data () {
return {
value: { title: 'Bitcoin', img: 'https://coinnik.com/uploads/crypto-
logos/06fa2ab3d5a0f1d60e7d236aeccd6c6a.png' },
}
},
methods: {
}
}
</script>
app.js
methods: {
stepChanged(step) {
this.currentstep = step;
},
apiCalc(){
let self = this;
axios.get('/ajax-calculation?includes=direction,direction.receiveCurrency&send_currency=2').then((response) => {
self.calcApi = response.data;
console.log('api:',self.calcApi.currency_id);
// self.calcApi.currency_id;
})
}
},
components:{
'multiselect': Multiselect
},
created() {
this.apiCalc();
},
});
html
<div class="col-md-12 mb-2">
<multiselect :options="[ { title: 'Tether', img: 'https://coinnik.com/uploads/crypto-logos/006fe133d48ea7cd45cf8ccb8cb7ec42.png' },
{ title: 'value', img: 'https://coinnik.com/uploads/crypto-logos/006fe133d48ea7cd45cf8ccb8cb7ec42.png' }]">
</multiselect>
</div>
How to define a method that I use when changing item then call api and show inside the console?
You can use the deep watch on the value variable.
watch: {
'value': {
handler: function (after, before) {
this.apiCalc();
},
deep: true
},
},
Ok, so first, you can use the #input event to recognize when something has been picked, like so:
<multiselect v-model="value" :options="options" #input="doApi" multiple></multiselect>
Then in doApi, you can check the current value.
doApi() {
console.log(this.value);
}
The value of this.value will be an array of what you have picked. You can pass these to your API. Full codepen here: https://codepen.io/cfjedimaster/pen/gOOjjrb?editors=1111
Have you tried to use the #select event? Vue Multiselect's documentation includes several events you can try, check it out here: https://vue-multiselect.js.org/#sub-events
So try
<multiselect v-model="value" :options="options" #select="actionToExecute" multiple></multiselect>
And then define that in your methods. I'm currently using vue multiselect too so let me know if that works for you.

Selected values in a v-for loop of components

I implemented a component that have a select element inside and it is like following more or less:
<!-- child component -->
<template>
<b-form-select
v-model="selectedProject"
:options="projects"
#change="changedValue">
<template v-slot:first>
<option :value="null" disabled>-- Please select a project --</option>
</template>
</b-form-select>
</template>
<script>
export default {
name: 'AllocationItem',
props: {
projects: {
type: Array,
default: () => [{ value: Number}, { text: String}]
}
},
data() {
return {
selectedProject: null,
}
},
methods: {
changedValue(value) {
this.selectedProject = value;
}
}
}
</script>
I use this component in a parent componenent where it can be possible add other AllocationItem clicking on a button.
To do that i have used an array where i push a new item every time that there is a click on add button (i don't know if it's the right way...)
Follow parent component code:
<!-- parent component -->
<template>
<b-button class="btnAction" #click="addItem()">Add</b-button>
<b-button class="btnAction" #click="sendAllocation()">Send</b-button>
<b-row v-for="allocation in resourceItem.allocations" v-bind:key="allocation.id">
<allocation-item v-bind:projects="projects"></allocation-item>
</b-row>
</template>
<script>
export default {
name: 'Management',
components: {
AllocationItem
},
data() {
return {
allocations: []
}
},
methods: {
addItem() {
this.allocations.push(AllocationItem);
},
sendAllocation() {
this.allocations.forEach((allocation) => {
// I WOULD LIKE TO HAVE ALL SELECTED VALUE HERE!!!
});
},
},
created() {
const dataProjects = this.getProjectsData();
dataProjects.then(response => {
this.projects = response.map((item) => {
return {value: item.id, text: item.name}
})
});
}
</script>
In my application i have another button, send button, that should be read values selected in all child component (allocation-item).
How can i do to have an array with that selected values?
Thank you in advance to all
This depends on the relationship from your 'Send Button'-component with your parent component.
You can either:
Use the $emit() method to propagate data up the component tree to the shared parent component. Then prop it down to the 'Send Button'-component.
Have a single source of truth by using Vuex. This is a store that keeps all your data in a centralised object.
Perhaps you can provide us with more information on the project structure?
First of all, ask yourself if this component is being used anywhere else but here. If you only use it once, build it into the parent component and your problems are solved.
Otherwise I'd go with #laurensvm's approach of using emit or Vuex.
After some researches on google i have founded a solution. I don't know if it the right way but it works fine and it seems clean.
My solution consists to use emit on child component and v-model on parent like show following example.
<!-- child component -->
<template>
<b-form-select
v-model="selectedProject"
:options="projects"
#change="changedValue">
<template v-slot:first>
<option :value="null" disabled>-- Please select a project --
</option>
</template>
</b-form-select>
</template>
<script>
export default {
name: 'AllocationItem',
props: {
projects: {
type: Array,
default: () => [{ value: Number}, { text: String}]
},
value: {
type: Number
}
},
data() {
return {
selectedProject: this.value
}
},
methods: {
changedValue(value) {
this.$emit('input', value);
}
}
}
</script>
And on parent using an array variable on v-model.
Something like this:
<!-- parent component -->
<template>
<b-container>
<b-row>
<b-button class="btnAction" variant="success" #click="addItem(index)">Add</b-button>
<b-button class="btnAction" #click="sendAllocation(index)">Send</b-button>
</b-row>
<b-row v-for="(allocation, index) in resourceItem.allocations" v-bind:key="allocation.id">
<allocation-item v-bind:projects="projects" v-model="allocationItemSelected[index]"></allocation-item>
</b-row>
</b-container>
</template>
See a runnable example clicking on codesandbox link below:
https://codesandbox.io/s/vue-template-4f9xj?autoresize=1&expanddevtools=1&fontsize=14&hidenavigation=1&moduleview=1

Categories