I am using this example for Vue Multiselect "^2.0.0-beta.14" in Laravel 5.3. https://github.com/monterail/vue-multiselect/tree/2.0#install--basic-usage
The plugin renders correctly but I cannot get the selection via v-model. I am expecting #{{ selected }} to update with the current selection.
app.js
Vue.component('dropdown', require('./components/Multiselect.vue'));
VUE JS
<template>
<div>
<multiselect
v-model="value"
:options="options">
</multiselect>
</div>
</template>
<script>
import Multiselect from 'vue-multiselect'
export default {
components: { Multiselect },
data () {
return {
value: null,
options: ['list', 'of', 'options']
}
}
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
HTML
<div id="app">
<h3>Dropdown</h3>
<div>
<label class="typo__label">Single select</label>
<dropdown></dropdown>
<pre class="language-json"><code>#{{ value }}</code></pre>
</div>
</div>
NB
The official example uses selected instead of value but this does not work either. According to the docs selection is replaced by value as of V2.
If you are using TypeScript Interfaces with Vue.js 2.0, avoid using a optional properties to store the value from child components. i.e. if your property is
value:? IMyCustomInterface instead use value: MyCustomObject|null and set the object to null in the constructor.
If the property is optional, it will compile fine, but child components won't update it properly.
The reason value is not showing up in root is because the data is isolated to the dropdown component. To get your data from a component to show up in the Root you need to use props.
See this question for a detailed explanation
How to get data from a component in VueJS
Related
I created a language selection dropdown in my Navbar component. So here is my navbar component:
<div>
<h6>{{ translate("welcomeMsg")}} </h6>
<select name="lang" v-model="lang">
<option value="en">English</option>
<option value="de">Deutsch</option>
</select>
</div>
<script>
export default {
mixins: [en, de],
data() {
return {
lang: "en",
};
},
methods: {
translate(prop) {
return this[this.lang][prop];
}
}
}
</script>
So the parent of this component is an Index.vue which is main component in my application.
<div id="app">
<Topnav/>
<Navbar/>
<router-view></router-view>
<Footer/>
</div>
Currently, I am able to change the language in my Navbar component. So according to the selected value in the dropdown in Navbar component, welcomeMsg is changing. What I am trying to do is I want to put this pieve of code to TopBar "{{ translate("welcomeMsg")}} ", and according to the value of the dropdown in Navbar component, I want to change this value.
Can you help me with this or can you give me an idea how to do it?
If I understand you correctly, you want to use translate method inside Topnav component.
This method is however defined in Navbar, so it's not accessible in Topnav.
To use it elsewhere you could create a mixin with this method to import it to any component. I don't recommend this solution though as mixins are making the code messy.
Another solution is to create a component with translate method defined inside. Let this component do just that: translate a message passed by prop and render it inside some div:
<div>
{{ translatedMessage }}
</div>
<script>
mixins: [en, de],
props: {
message: {
type: String,
default: ''
},
language: {
type: String,
default: 'en'
}
},
computed: {
translatedMessage() {
return this[this.language][this.message];
}
}
</script>
You can reuse this component anywhere in the application. You would still need to pass a language prop somehow, possibly the solution would be to use vuex store to do this, since language is probably global for entire application.
For easier and more robust solutions I would use vue-i18n, which #Abregre has already suggested in his comment: https://stackoverflow.com/a/70694821/9463070
If you want a quick solution for a full-scale application and you don't have a problem with dependencies, you could try to use vue-i18n.
It's a plugin for vue that does exactly this for multi-locale websites/apps in Vue.
https://www.npmjs.com/package/vue-i18n
EDIT
Then in order to use it globally in your app, you should use vuex.
Keep the language selection state there and then wherever you want to use it, you make a computed function with the state.language getter.
The translate function should be a global registered filter
https://v2.vuejs.org/v2/guide/filters.html
I have layout default.vue:
<template>
<div>
<Header></Header>
<div class="body-wrapper">
<Nuxt :numberThird="numberThird" />
</div>
<Footer></Footer>
</div>
</template>
<script>
export default {
data() {
return {
numberThird: 3
};
},
};
</script>
Here I am trying to pass prop numberThird.
I want to be able to change this value in the future through components that are deeply nested.
But there is a problem: my pages don't accept this prop (numberThird), they treat it as $parent.$attr.
The question is: can I somehow change this value through deeply nested child components?
As stated here: https://stackoverflow.com/a/67817642/8816585
Props and listeners are not the most friendly on either <nuxt> nor <nuxt-link>.
Kinda confirmed here by Alexander too: https://github.com/nuxt/nuxt.js/issues/8669#issuecomment-764006062
You better off using Vuex in this case, especially if you are aiming to something deep. Props drilling is not recommended most of the time.
Please see this minium example
1
This minimum template won't throw any error since the parent is not rendering anything.
<template>
<div v-if="false">
{{ i.dont.have.the.value }}
</div>
</template>
2
Now if I create a Modal wrapper with default slot, and still using v-if
Modal.vue
<template>
<div v-if="show">
<slot />
</div>
</template>
<script>
export default {
props: {
show: Boolean,
},
};
</script>
However, this will throw Error, Why?
App.vue
<template>
<Modal :show="false">
<div>{{ i.dont.have.the.value }}</div>
</Modal>
</template>
<script>
import Modal from "#/components/Modal.vue";
export default {
components: {
Modal,
},
};
</script>
[Vue warn]: Error in render: "TypeError: Cannot read property 'dont' of undefined"
Why is this happening?
How can I create a Modal with guarantee children won't be excuted if not show?
I can't directly use v-if in the Modal component because I need a transition.
You made a mistake about the slot in App.vue because VueJS will try to resolve your i.dont.have.the.value variable based on parent scope (your slot is not conditioned)
Remember this rule from official documentation
Everything in the parent template is compiled in parent scope; everything in the child template is compiled in the child scope.
https://v2.vuejs.org/v2/guide/components-slots.html#Compilation-Scope
Just define the variable in the right scope and you will be fine (if you define it in child component and want to expose it to parent, use a scoped slot then).
NOTE: I have made a video version of this quesiton, which you can view here:
https://www.loom.com/share/6a23d0791c2444068e964b388ed5497e
The VueJS documentation dicusses how to use v-model with components: https://v2.vuejs.org/v2/guide/components.html#Passing-Data-to-Child-Components-with-Props
If I create a component exactly has written in the documentation, it works just fine. Here is the code:
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
But now let me try and change the name of the prop in that component -- for example, to bar:
Vue.component('custom-input', {
props: ['bar'],
template: `
<input
v-bind:value="bar"
v-on:input="$emit('input', $event.target.value)"
>
`
})
Now it only half-works. That is to say, let's say I am binding custom-input to a data property called message, like this: <custom-input v-model="message"></custom-input>. If I dynamically update the value of message, then the value of custom-input will update -- but only once. If I update the value again it will not update the value of custom-input.
However, if I change the prop name back to value (instead of bar), then the value of custom-input will update each and every time I udpate the value of message.
[again, here is the video description of what I am talking about: https://www.loom.com/share/6a23d0791c2444068e964b388ed5497e]
Why is that? Why should it matter what name I give the prop? Does it have to be named value because I am binding to the value attribute? If so, why?
In short, what is going on here?
Thanks.
Yes, the attribute must be named value because you are using v-model to bind. v-model is an abbrevistion of binding the attribute value and listening to the event input. It's a special use case for binding a value two ways.
So, this is exactly the same:
<custom-input v-model="user" />
And:
<custom-input :value="user" #input="user = $event" />
If you prefer the full attribute notation:
<custom-input v-bind:value="user" v-on:input="user = $event" />
With the variable $event, you can, directly in the template, access the emitted value. You can also write a function name without brackets into the template to pass the emitted value as first parameter (e.g. #input="setUser", then declare a method setUser(user)).
props is used to get data down from a parent component to a child component
$emit is used to send back data from the child to the parent
v-model is used for 2 way data binding
always make the data reactive with a return data(){return{...}}
props example:
Vue.component('product',{
template:`
<div>
<item :items="items"/>
</div>
`,
data() {
return{
items:'hi'
}
}
})
Vue.component('item',{
template:`
<div>
{{items}}
</div>
`,
props:{
items:{
type:String,
required:false
}
}
})
var app= new Vue({
el:"#app"
})
//in html file
<div id="app">
<product/>
</div>
I have a vue-multiselect component in a form, and the console reports that my options are undefined, even though I can see they are not. My options are fetched from a back end and put in the store well before this component is created.
The console error is
Invalid prop: type check failed for prop "options". Expected Array, got Undefined
Here is my component
<template>
<form action="#" #submit.prevent>
<section>
<div class="container">
<h2 class="subtitle">Details</h2>
<b-field label="Role" horizontal>
<multiselect
:options="roleOptions"
track-by="id"
label="title"
:multiple="true"
:close-on-select="false"
:clear-on-select="false"
></multiselect>
</b-field>
</div>
</section>
</form>
</template>
<script>
import Multiselect from "vue-multiselect";
import { mapState, mapActions } from "vuex";
export default {
name: "ProcessDetailsComponent",
components: {
multiselect: Multiselect
},
computed: {
roleOptions() {
return this.$store.state.processes.formData.process_roles;
}
},
};
</script>
<style scoped>
</style>
In the developer tools Vue inspector, I can see that the options look correct (to me). I've tried passing them in as props, computed values, mapped state - same problem every time.
If I swap the options out for a static array, defined in the data() function, it works ok. Can anyone confirm that I am implementing this correctly?
:options="roleOptions" expects an array. Make sure the vue variable is an array and not an object or undefined etc as mentioned above.
data(){
return{
roleOptions:[],
}
}