How to combine vue-multiselect with vue-treeSelect? - vue.js

I'm working with Vue.js and I need a "smart" search bar.
I've ended up with vue-multiselect, but I need to have nested menus in the dropdown.
Apparently, there's something called treeSelect that can help me with that, but I don't know how to combine them together.
The code is pretty basic.
<template>
<div>
<multiselect v-model="value" :options="options" placeholder="Pick a value" style="width:600px;"></multiselect>
</div>
</template>
<script lang="ts">
import { Multiselect } from 'vue-multiselect'
#Component({
components: {
Multiselect,
},
})
export default class Income extends Vue {
public Multiselect= Multiselect;
data () {
return {
value: '',
options: ['first','second','third','forth','fifth']
}
}
}
I need the "search-select" bar to be able of searching, nesting and showing disabled options.
I would really need some help, and why not, some advice and guidance :)
Thanks

You can refer this.https://codepen.io/anon/pen/pGqXGa
for more information visit - https://vue-treeselect.js.org/#disable-item-selection

The problem is that I'm trying to use treeselect, but I'm also using typescript.
So, after some research, I found that these two are not compatible.
I have to start looking for an alternative for TreeSelect; a dropdown that supports nesting

Related

What's the most idomatic Vue way of handling this higher-order component?

I have a VueJS organization and architecture question. I have a bunch of pages that serve as CRUD pages for individual objects in my system. They share a lot of code . I've abstracted this away in a shared component but I don't love what I did and I'm looking for advice on the idiomatic Vue way to do this.
I'm trying to create a higher order component that accepts two arguments:
An edit component that is the editable view of each object. So you can think of it as a stand in for a text input with a v-model accept that it has tons of different inputs.
A list component which displays a list of all the objects for that type in the system. It controls navigation to edit that object.
Normally this would be simply something where I use slots and invoke this component in the view page for each CRUD object. So basically I'd have something like this:
<!-- this file is src/views/DogsCRUDPage.vue -->
<template>
<general-crud-component
name="dogs"
backendurl="/dogs/"
>
<template slot="list">
<dogs-list-component />
</template>
<template slot="edit">
<dogs-edit-field v-model="... oops .." />
</template>
</general-crud-component>
</template>
<script>
export default {
name: "DogCRUDPage",
components: {
GeneralCrudComponent,
DogListComponent,
DogEditField,
},
}
</script>
This is nice because it matches the general syntax of all of my other VueJS pages and how I pass props and things to shared code. However, the problem is that GeneralCRUDComponent handles all of the mechanisms for checking if an object is edited, and therefor hiding or unhiding the save button, etc. Therefor it has the editable object in its data which will become the v-model for DogsEditField or any other that's passed to it. So it needs to pass this component a prop. So what I've done this:
// This file is src/utils/crud.js
import Vue from "vue"
const crudView = (listComponent, editComponent) => {
return Vue.component('CrudView', {
template: `
<v-row>
<list-component />
<v-form>
<edit-component v-model="obj" />
</v-form>
</v-row>
`,
components: {
ListComponent: listComponent,
EditComponent: editComponent,
},
data() {
return {
obj: {},
}
},
})
}
export default crudView
This file has a ton of shared code not shown that is doing the nuts and bolts of editing, undo, saving, etc.
And then in my src/router/index.js
//import DogCRUDPage from "#/views/libs/DogCRUDPage"
import crudView from "#/utils/crud"
import DogListComponent from "#/components/DogListComponent"
import DogEditField from "#/components/design/DogEditField"
const DogCRUDPage = crudView(DesignBasisList, DesignBasis)
Vue.use(VueRouter);
export default new VueRouter({
routes: [
{
path: "/dog",
name: "dog",
component: DogCRUDPage,
},
})
This is working, but there are issues I don't love about it. For one, I needed to enable runtimecompiler for my project which increases the size of the payload to the browser. I need to import the list and edit components to my router instead of just the page for every single object I have a page for. The syntax for this new shared component is totally different from the template syntax all the other pages use. It puts all of my page creation into the router/index.js file instead of just layed out as files in src/views which I am used to in Vue.
What is the idiomatic way to accomplish this? Am I on the right track here? I'm happy to do this, it's working, if this really is how we do this in Vue. But I would love to explore alternatives if the Vue community does something differently. I guess I'm mostly looking for the idiomatic Vue way to accomplish this. Thanks a bunch.
How about this:
DogsPage.vue
<template>
<CrudView
:editComponent="DogsEdit"
:listComponent="DogsList"
></CrudView>
</template>
<script>
import DogsEdit from '#/components/DogsEdit.vue'
import DogsList from '#/components/DogsList.vue'
import CrudView from '#/components/CrudView.vue'
export default {
components: { CrudView },
data() {
return { DogsEdit, DogsList }
}
}
</script>
CrudView.vue
<template>
<div>
<component :is="listComponent"></component>
<component :is="editComponent" v-model="obj"></component>
</div>
</template>
<script>
export default {
props: {
editComponent: Object,
listComponent: Object
},
data() {
return {
obj: {}
}
}
}
</script>

How to change the language of whole application from the dropdown in Navbar component in vue

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

Unique EventBus for multiple instances of the same Vue application

I have a Vue application (which is basically a video player) that uses EventBus to communicate across components which normally cannot communicate easily. This worked perfectly when I was developing the video player, but now I have bundled it using Rollup, and when I try to put multiple instances of the video player on the same page, any Event one instance sends will also be picked up by all the other instances of the application.
Now in hindsight I understand why people don't seem to like the EventBus, but I can't find a great solution to either improve or replace it. I can't name the EventBus instances dynamically, because then the rest of my application won't be informed about the new name. I can't even use something like a videoId in my EventBus listeners to control the uniqueness, because then I will encounter the same problem if a video is on the same page more than once.
Some posts suggest VueX, but my app doesn't need to be stateful and it doesn't seem like a replacement for Event and listeners (though I could be wrong on that.) It seems like a lot of overhead for more functionality than I need. Again, I could be wrong.
I tried to remove as much irrelevant code as possible, but to give an idea of how I implemented my EventBus:
event-bus.js:
import Vue from 'vue';
const EventBus = new Vue();
export default EventBus;
MediaPlayer.vue:
<template>
<div>
<div>
<div id='media-player'>
<end-screen v-if="videoIsFinished"/>
<tap-video
ref="tapVideoRef"
:source='sourceUrl'
:videoId='videoId'
#videoEndHandler='videoEndHandler'
>
</tap-video>
<div id="control-bar-container">
<transition name="slide-fade">
<div v-show='(showControls || !playing)' >
<media-controls
:playing="playing"
:videoLength="videoLength"
/>
</div>
</transition>
</div>
</div>
</div>
</div>
</template>
<script>
import TapVideo from './TapVideo.vue';
import EventBus from './event-bus';
export default {
data (){
return{
playing: false,
showControls: false,
videoLength: 0,
tapVideoRef: null
}
},
mounted() {
this.tapVideoRef = this.$refs.tapVideoRef;
EventBus.$on('videoLoaded', videoLength => {
this.videoLength = videoLength;
});
EventBus.$on('playStateChange', playing => {
this.onPlayStateChange(playing);
});
},
beforeDestroy() {
EventBus.$off(['playStateChange','closeDrawer']);
},
props: ['sourceUrl', 'platformType', 'videoId'],
}
</script>
In case anyone comes across this problem, I found a solution that works well for me. Thanks to #ChunbinLi for pointing me in the right direction - their solution did work, but passing props everywhere is a bit clunky.
Instead, I used the Provide/Inject pattern supported by Vue: https://v3.vuejs.org/guide/component-provide-inject.html
Some minimal relevant code:
The highest level Grandparent will provide the EventBus,
Grandparent.vue
export default {
provide() {
return {
eventBus: new Vue()
}
}
}
Then any descendant has the ability to Inject that bus,
Parent.vue
export default {
inject: ['eventBus'],
created() {
this.eventBus.$emit('neededEvent')
}
}
Child.vue
export default {
inject: ['eventBus'],
created(){
this.eventBus.$on('neededEvent', ()=>{console.log('Event triggered!')});
}
}
This is still a GLOBAL EventBus, so directionality of events and parental relationship is easy, as long as all components communicating are descendants of the component which "Provided" the bus.

Vue3 Transparent component wrapper not working

I am testing out vue3 and wanted to make a transparent wrapper component (something I will use fairly often) with the new version. With the new syntax it should be as simple v-bind="attrs". This does not seem to be working for me however and there is not much documentation as of yet. Anyone know what the problem could be? See codepen for more details as to how I am implementing it as well.
https://codepen.io/sumcoding/pen/mdJqwgj
I don't know if codepen is a good place to reproduce this since I don't know how to make an import -> import { reactive } from "vue"; so your myValue would be reactive.
Anyway I have got it working using <input v-model="attrs.modelValue">.
Here is codepen
BTW
Since you have registered props, you don't need to return them in a setup method ;-)
The transparent wrapper component pattern for Vue.js 3 final can be implemented like this
<template>
<label>{{ label }}</label>
<input
type="text"
:value="modelValue"
v-bind="$attrs"
#input="$emit('update:modelValue', $event.target.value)"
/>
</template>
<script>
export default {
name: 'InputWrapper',
props: {
modelValue: {
type: String,
default: ''
},
label: {
type: String,
default: ''
}
},
emits: ['update:modelValue']
};
</script>
You can view an example as well as another variation of the pattern for Vue.js 3 here.

Can I debug the export/import of a mixin?

I'm pretty new to vue.js and I'm trying to figure out how to use mixins.
I wondered if it is possible to create components which are bare of markup/template and contain only logic. As far as I understood, this should be possible and these components are called "mixins":
https://blog.bitsrc.io/understanding-mixins-in-vue-js-bdcf9e02a7c1
I'm using router functionality.
I'm now just trying out the vary basics of this concept and created the following:
listData1.vue, where the data for a list is created and then exported:
<script>
export default {
name: "listData1",
data() {
return {
list1: {
listData1A : "listData1A",
listData1A : "listData1B",
listData1A : "listData1C"
}
}
}
}
</script>
then listBuilder.vue, which takes the data and then uses it to create a list of items.
<template>
<div>
<ul>
<li v-for="element in list1" v-text="element"></li>
</ul>
</div>
</template>
<script>
import listData1 from "#/components/complexComponent2/listData1.vue"
export default{
name: 'listBuilder'
}
</script>
And then myComplexView2.vue in my views folder:
<template>
<div>
<h1>Second Awesome List!</h1>
<listBuilder />
</div>
</template>
<script>
import listBuilder from "#/views/myComplexView2.vue"
export default{
name: 'myComplexView2',
components: {
listBuilder
}
}
</script>
Now the result I get is this:
https://imgur.com/hQit785
But it should look like this:
http://localhost:8081/myComplexView
I'm a bit clueless what to do, especially since the vue dev tools in firefox don't show me much: https://imgur.com/RHJNy47.
Am I accessing the imported incorrectly?
Should I store the data differently, with the "data : {}" syntax or should I go for props in the listData component, like this:
props:["listData1"]
And then add the actual data in the component where the list is constructed with v-for? Though this would kind of undermine my goal to accomplish separating the data from the logic injecting it into the markup.
You need to setup listData1 as mixin in listBuilder.
<template>
<div>
<ul>
<li v-for="element in list1" v-text="element"></li>
</ul>
</div>
</template>
<script>
import listData1 from "#/components/complexComponent2/listData1.vue"
export default{
name: 'listBuilder',
mixins: [listData1],
}
</script>
Otherwise the ListBuilder won't have any data.
There's a typo in the mixin data:
listData1A : "listData1A",
listData1A : "listData1B",
listData1A : "listData1C"
Should be:
listData1A : "listData1A",
listData1B : "listData1B",
listData1C : "listData1C"
Apart from this, I don't see anything at syntax level in your code that would prevent mixin and v-for for working.
However, it puzzles me that myComplexView2 is importing myComplexView2.vue as the listBuilder:
import listBuilder from "#/views/myComplexView2.vue"
I don't know if this is an error you made when pasting to SO. Otherwise, the problem is probably here, since you need to import the listBuilder component, not the complex view.