single select vue js multiselect sub groups - javascript

I am using vue js multiselect library for multiselect library. I am using the sub group functionality.
. I want the functionality to allow only one option selection from one group. For example if I select vue js from javascript group, I shouldn't be allowed to select adonis for the given JS fiddle or else if I selelct vue js and then select adonis, then vue js should be deselected. Any help will be much appreciated. Thanks in advance
Example JS fiddle https://jsfiddle.net/bgarrison25/tndsmkq1/4/
new Vue({
components: {
Multiselect: window.VueMultiselect.default
},
data () {
return {
options: [
{
language: 'Javascript',
libs: [
{ name: 'Vue.js', category: 'Front-end' },
{ name: 'Adonis', category: 'Backend' }
]
},
{
language: 'Ruby',
libs: [
{ name: 'Rails', category: 'Backend' },
{ name: 'Sinatra', category: 'Backend' }
]
},
{
language: 'Other',
libs: [
{ name: 'Laravel', category: 'Backend' },
{ name: 'Phoenix', category: 'Backend' }
]
}
],
value: [
{ name: 'Laravel', category: 'Backend' },
{ name: 'Phoenix', category: 'Backend' }
]
}
}
}).$mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link href="https://unpkg.com/vue-multiselect#2.1.4/dist/vue-multiselect.min.css" rel="stylesheet"/>
<script src="https://unpkg.com/vue-multiselect#2.1.4/dist/vue-multiselect.min.js"></script>
<div id="app">
<label class="typo__label">Groups</label>
<multiselect
v-model="value"
:options="options"
:multiple="true"
group-values="libs"
group-label="language"
:group-select="true"
placeholder="Type to search"
track-by="name"
label="name">
<span slot="noResult">Oops! No elements found. Consider changing the search query.</span>
</multiselect>
<pre class="language-json"><code>{{ value }}</code></pre>
</div>

You could try something like this below, it's a bit overly complicated for what it does, but could work well in its own component.
Basically on the input event you can search to see if there are any others in that group and then select accordingly.
new Vue({
components: {
Multiselect: window.VueMultiselect.default
},
methods: {
selectUnique(ev) {
if (!ev || ev.length < this.value.length) {
this.value = ev;
return;
}
let newValue = ev.filter(x => this.value.indexOf(x) === -1)[0];
let group = this.getGroupByLib(newValue);
if (this.value.some(x => this.getGroupByLib(x) === group)) {
this.value = this.value.filter(x => this.getGroupByLib(x) !== group);
this.value.push(newValue);
} else
this.value = ev;
},
getGroupByLib(lib) {
return this.options.filter(x => x.libs.some(y => y.name === lib.name))[0].language;
}
},
data() {
return {
options: [{
language: 'Javascript',
libs: [{
name: 'Vue.js',
category: 'Front-end'
},
{
name: 'Adonis',
category: 'Backend'
}
]
},
{
language: 'Ruby',
libs: [{
name: 'Rails',
category: 'Backend'
},
{
name: 'Sinatra',
category: 'Backend'
}
]
},
{
language: 'Other',
libs: [{
name: 'Laravel',
category: 'Backend'
},
{
name: 'Phoenix',
category: 'Backend'
}
]
}
],
value: []
}
}
}).$mount('#app')
* {
font-family: 'Lato', 'Avenir', sans-serif;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vue-multiselect#2.1.0"></script>
<link rel="stylesheet" href="https://unpkg.com/vue-multiselect#2.1.0/dist/vue-multiselect.min.css">
<div id="app">
<label class="typo__label">Groups</label>
<multiselect :value="value" :options="options" :multiple="true" group-values="libs" group-label="language" placeholder="Type to search" track-by="name" label="name" #input="selectUnique">
<span slot="noResult">Oops! No elements found. Consider changing the search query.</span>
</multiselect>
<pre class="language-json"><code>{{ value }}</code></pre>
</div>

Make these two things into false
:multiple="false" :group-select="false"
Check Here Code

Related

Vue.js update parent data with an input from a child component

I'm using Vue.js 2 and I'm trying to update the description of a file using an input in a child component. I've been reading a few related questions and read some of the official docs along with .sync but I'm struggling to get the result I want since files is a list of objects.
Here's what I've been trying.
Vue.component('myComponent', {
props: ["file"],
data() {
return {
myDescription: '',
}
},
mounted() {
this.myDescription = this.file.description;
},
template: `
<div>
<label>{{ file.name }}</label>
<br>
<input type="text" #input="update" :value="myDescription"></input>
<br><br>
</div>
`,
methods: {
update() {
this.$emit("update-description", this.myDescription, this.file);
},
}
})
var app = new Vue({
el: '#app',
methods: {
updateDescription(description, file) {
console.log(description);
}
},
data: {
files: [{
id: 1,
name: "Hello",
description: "",
},
{
id: 2,
name: "World",
description: "Foo",
},
{
id: 3,
name: "John",
description: "Bar",
}
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div> {{ files }} </div>
<br>
<my-component v-for="file in files" :key="file.id" :file="file" #update-description="updateDescription" />
</div>
You're almost there, you can see in the code you've provided that the child component event is being emitted but the value is empty. The problem is you're not updating myDescription, if you change your :value to v-model then it will update, as v-model uses two way binding.
Also, if you want to update the file description, you can just do:
file.description = description;
Vue.component('myComponent', {
props: ["file"],
data() {
return {
myDescription: '',
}
},
mounted() {
this.myDescription = this.file.description;
},
template: `
<div>
<label>{{ file.name }}</label>
<br>
<input type="text" #input="update" v-model="myDescription"></input>
<br><br>
</div>
`,
methods: {
update() {
this.$emit("update-description", this.myDescription, this.file);
},
}
})
var app = new Vue({
el: '#app',
methods: {
updateDescription(description, file) {
console.log(description);
file.description = description;
}
},
data: {
files: [{
id: 1,
name: "Hello",
description: "",
},
{
id: 2,
name: "World",
description: "Foo",
},
{
id: 3,
name: "John",
description: "Bar",
}
]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div> {{ files }} </div>
<br>
<my-component v-for="file in files" :key="file.id" :file="file" #update-description="updateDescription" />
</div>

TypeError: Cannot read property 'indexOf' of null vuejs on consecutive v-fors

I am having this issue for hours now. I am rendering items on <options> for my <select> element using vuejs' v-for and when it rendered it gave me the type error. I tried changing my :key values but still having the same error and won't render the items that I needed.
Where did I make a mistake?
Code below:
This is my first <select> tag:
<vs-select placeholder="Gender"
v-model="gender"
color="#ffc640"
class="col-span-2 mt-2 z-0"
>
<vs-option v-for="(gender, index) in $store.state.genders"
:key="`${index}_gender`"
:label="gender.label" :value="gender.name">
{{ gender.label }}
</vs-option>
</vs-select>
Here is the next <select> tag:
<vs-select placeholder="Attainment"
v-model="attainment"
color="#ffc640"
class="col-span-2 mt-2 z-0"
>
<vs-option v-for="(attainment, index) in $store.state.attainment"
:key="`${index}_attainment`"
:label="attainment.label" :value="attainment.name">
{{ attainment.label }}
</vs-option>
</vs-select>
Here is the error:
For context, here are the objects
genders: [
{
name: 'male',
label: 'Man',
value: '1-male',
},
{
name: 'female',
label: 'Woman',
value: '2-female',
},
{
name: 'lgbtq',
label: 'Transgender',
value: '3-lgbtq',
},
{
name: 'nottosay',
label: 'Prefer not to say',
value: '4-nottosay',
},
],
attainment: [
{
name: 'high_school',
label: 'High School',
value: 'high_school',
},
{
name: 'senior_high_school',
label: 'Senior High School',
value: 'senior_high_school',
},
{
name: 'associate',
label: 'Associate',
value: 'associate',
},
{
name: 'bachelor',
label: 'Bachelor',
value: 'bachelor',
},
{
name: 'master',
label: 'Master',
value: 'master',
},
{
name: 'doctorate',
label: 'Doctorate',
value: 'doctorate',
},
],
Here are my computed values:
attainment: {
get() {
return this.$store.state.user.attainment;
},
set(value) {
this.$store.commit('setAttainment', value);
},
},
gender: {
get() {
return this.$store.state.user.gender;
},
set(value) {
this.$store.commit('setGender', value);
},
},
Computed are functions that return a value according to the state. You cannot use them in v-model directives (but ': value' is correct). Reading the store state is prohibited also, you should use getters.
Your code can be something like this (remove computeds):
<vs-select placeholder="Gender"
:value="$store.state.user.gender"
#input="$store.commit('setGender', $event)"
color="#ffc640"
class="col-span-2 mt-2 z-0"
>
<vs-option v-for="(gender, index) in $store.state.genders"
:key="`${index}_gender`"
:label="gender.label" :value="gender.name">
{{ gender.label }}
</vs-option>
</vs-select>
Anyway, it is not clean. Revise your store to use getters, listen to #input, and commit data changes.

Vue not Defined

I have been struggling this error for a while now. I've tried multiple ways and none of them work. All I am trying to do is to get this simple component working. Someone help please. :)
<script>
const app = new Vue({
el: "#main",
data: function(){
return {
search: '',
customers: [
{ id: '1', name: 'Something', },
{ id: '2', name: 'Something else', },
{ id: '3', name: 'Something random', },
{ id: '4', name: 'Something crazy', }
]};
},
computed:
{
filteredCustomers:function()
{
var self=this;
return this.customers.filter(function(cust){return cust.name.toLowerCase().indexOf(self.search.toLowerCase())>=0;});
//return this.customers;
}
}
});
</script>
<template>
<div id="main">
Search: <input type="text" v-model="search"/>
<div v-bind:v-for="customer in filteredCustomers">
<span>{{customer.name}}</span>
</div>
</div>
</template>
You should include the Vue library.
One way of doing so is including using <script> as described in Vue's documentation.

Tree-view of Editable Text Areas in Vue.js

I have this fiddle:
https://jsfiddle.net/pnqzspoe/12014/
I want to modify it a bit and want to display each node as a text area containing the corresponding text. Further, I want to give an option to 'reply' to it. This would mean insertion of a new text area into which we can enter text.
Here is the code:
<script type="text/x-template" id="item-template">
<li>
<div
:class="{bold: isFolder}"
#click="toggle"
#dblclick="changeType">
{{ model.name }}
<span v-if="isFolder">[{{ open ? '-' : '+' }}]</span>
</div>
<ul v-show="open" v-if="isFolder">
<item
class="item"
v-for="(model, index) in model.children"
:key="index"
:model="model">
</item>
<li class="add" #click="addChild">+</li>
</ul>
</li>
</script>
<p>(You can double click on an item to turn it into a folder.)</p>
var data = {
name: 'My Tree',
children: [
{ name: 'hello' },
{ name: 'wat' },
{
name: 'child folder',
children: [
{
name: 'child folder',
children: [
{ name: 'hello' },
{ name: 'wat' }
]
},
{ name: 'hello' },
{ name: 'wat' },
{
name: 'child folder',
children: [
{ name: 'hello' },
{ name: 'wat' }
]
}
]
}
]
}
// define the item component
Vue.component('item', {
template: '#item-template',
props: {
model: Object
},
data: function () {
return {
open: false
}
},
computed: {
isFolder: function () {
return this.model.children &&
this.model.children.length
}
},
methods: {
toggle: function () {
if (this.isFolder) {
this.open = !this.open
}
},
changeType: function () {
if (!this.isFolder) {
Vue.set(this.model, 'children', [])
this.addChild()
this.open = true
}
},
addChild: function () {
this.model.children.push({
name: 'new stuff'
})
}
}
})
// boot up the demo
var demo = new Vue({
el: '#demo',
data: {
treeData: data
}
})
What would be the template for this use-case?
If I don't understand your question wrongly...
Replace
{{model.name}}
with
<textarea v-model="model.name"></textarea>
should work?

Vue.js and Twig weird collision

Hello I have this code in my symfony 3 project :
TWIG TEMPLATE:
<div id="fileManagerContainer" class="AppContent">
{% verbatim %}
<!-- item template -->
<script type="text/x-template" id="item-template">
<li>
<div
:class="{bold: isFolder}"
#click="toggle"
#dblclick="changeType">
{{model.name}}
<span v-if="isFolder">{{open ? '-' : '+'}}</span>
</div>
<ul v-show="open" v-if="isFolder">
<item
class="item"
v-for="model in model.children"
:model="model">
</item>
<li class="add" #click="addChild">+</li>
</ul>
</li>
</script>
{% endverbatim %}
<p>(You can double click on an item to turn it into a folder.)</p>
<!-- the demo root element -->
<ul id="demo">
<item
class="item"
:model="treeData">
</item>
</ul>
</div>
VUE FILE :
// demo data
var data = {
name: 'My Tree',
children: [
{ name: 'hello' },
{ name: 'wat' },
{
name: 'child folder',
children: [
{
name: 'child folder',
children: [
{ name: 'hello' },
{ name: 'wat' }
]
},
{ name: 'hello' },
{ name: 'wat' },
{
name: 'child folder',
children: [
{ name: 'hello' },
{ name: 'wat' }
]
}
]
}
]
}
// define the item component
Vue.component('item', {
template: '#item-template',
props: {
model: Object
},
data: function () {
return {
open: false
}
},
computed: {
isFolder: function () {
return this.model.children &&
this.model.children.length
}
},
methods: {
toggle: function () {
if (this.isFolder) {
this.open = !this.open
}
},
changeType: function () {
if (!this.isFolder) {
Vue.set(this.model, 'children', [])
this.addChild()
this.open = true
}
},
addChild: function () {
this.model.children.push({
name: 'new stuff'
})
}
}
})
// boot up the demo
var demo = new Vue({
delimiters: ['{{', '}}'],
el: '#demo',
data: {
treeData: data
}
})
ant it works on jsfiddle, but doesnt do a thing in real project. All scripts are loaded perfectly, Vue.js works but just this piece of code does not. Any ideas ?

Categories