Vue draggable works for whole group but not for each item - vue.js

The code below allows dragging the groups in toListFetchRouteA1:
<draggable id="first" data-source="juju" :list="toListFetchRouteA1" class="list-group" draggable=".item" group="a">
<div class="list-group-item item" v-for="teamfetch in toListFetchRouteA1" :key="teamfetch.id">
<div v-for="personame in teamfetch.Team_player_sessions" :key="personame.id">
{{personame.Player.Person.first_name}}
</div>
</div>
</draggable>
I am trying to allow dragging each person name rather than by group, so I changed :list="toListFetchRouteA1" into :list="teamfetch.Team_player_sessions" (shown below), but it throws an error: Cannot read property 'Team_player_sessions' of undefined".
<draggable id="first" data-source="juju" :list="teamfetch.Team_player_sessions" class="list-group" draggable=".item" group="a">
<div v-for="teamfetch in toListFetchRouteA1" :key="teamfetch.id">
<div class="list-group-item item" v-for="personame in teamfetch.Team_player_sessions" :key="personame.id">
{{personame.Player.Person.first_name}}
</div>
</div>
</draggable>
It does not drag as well. Is there a way to make div v-for="personame in teamfetch.Team_player_sessions draggable for personname?

The error occurs because teamfetch is out of scope. It was declared on the inner v-for but used in the outer element.
You could make the person names draggable by wrapping them in a second draggable (assuming you want the groups themselves and the names within the groups to be draggable):
<draggable :list="toListFetchRouteA1" class="list-group" draggable=".item" group="a">
<div class="list-group-item item" v-for="teamfetch in toListFetchRouteA1" :key="teamfetch.id">
<!-- 2nd draggable for persons -->
<draggable :list="teamfetch.Team_player_sessions" draggable=".person">
<div v-for="personame in teamfetch.Team_player_sessions" :key="personame.id" class="person">
{{personame.Player.Person.first_name}}
</div>
</draggable>
</div>
</draggable>
new Vue({
el: '#app',
data: () => ({
toListFetchRouteA1: [
{
id: 1,
Team_player_sessions: [
{
id: 100,
Player: {
Person: {
first_name: 'john'
}
}
}
]
},
{
id: 2,
Team_player_sessions: [
{
id: 200,
Player: {
Person: {
first_name: 'adam'
}
}
},
{
id: 201,
Player: {
Person: {
first_name: 'allen'
}
}
}
]
},
{
id: 3,
Team_player_sessions: [
{
id: 300,
Player: {
Person: {
first_name: 'dave'
}
}
},
{
id: 301,
Player: {
Person: {
first_name: 'dylan'
}
}
}
]
},
]
}),
})
.item {
background: #eee;
margin: 2px;
padding: 10px;
}
.person {
background: #ccc;
margin: 2px;
}
<script src="https://unpkg.com/vue#2.6.11"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/sortablejs#1.8.4/Sortable.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/2.20.0/vuedraggable.umd.min.js"></script>
<div id="app">
<draggable :list="toListFetchRouteA1" class="list-group" draggable=".item" group="a">
<div class="list-group-item item" v-for="teamfetch in toListFetchRouteA1" :key="teamfetch.id">
<draggable :list="teamfetch.Team_player_sessions" draggable=".person">
<div v-for="personame in teamfetch.Team_player_sessions" :key="personame.id" class="person">
{{personame.Player.Person.first_name}}
</div>
</draggable>
</div>
</draggable>
<pre>{{toListFetchRouteA1}}</pre>
</div>

Related

nuxtjs add and remove class on click on elements

I am new in vue and nuxt and here is my code I need to update
<template>
<div class="dashContent">
<div class="dashContent_item dashContent_item--active">
<p class="dashContent_text">123</p>
</div>
<div class="dashContent_item">
<p class="dashContent_text">456</p>
</div>
<div class="dashContent_item">
<p class="dashContent_text">789</p>
</div>
</div>
</template>
<style lang="scss">
.dashContent {
&_item {
display: flex;
align-items: center;
}
&_text {
color: #8e8f93;
font-size: 14px;
}
}
.dashContent_item--active {
.dashContent_text{
color:#fff;
font-size: 14px;
}
}
</style>
I tried something like this:
<div #click="onClick">
methods: {
onClick () {
document.body.classList.toggle('dashContent_item--active');
},
},
but it changed all elements and I need style change only on element I clicked and remove when click on another
also this code add active class to body not to element I clicked
This is how to get a togglable list of fruits, with a specific class tied to each one of them.
<template>
<section>
<div v-for="(fruit, index) in fruits" :key="fruit.id" #click="toggleEat(index)">
<span :class="{ 'was-eaten': fruit.eaten }">{{ fruit.name }}</span>
</div>
</section>
</template>
<script>
export default {
name: 'ToggleFruits',
data() {
return {
fruits: [
{ id: 1, name: 'banana', eaten: false },
{ id: 2, name: 'apple', eaten: true },
{ id: 3, name: 'watermelon', eaten: false },
],
}
},
methods: {
toggleEat(clickedFruitIndex) {
this.fruits = this.fruits.map((fruit) => ({
...fruit,
eaten: false,
}))
return this.$set(this.fruits, clickedFruitIndex, {
...this.fruits[clickedFruitIndex],
eaten: true,
})
},
},
}
</script>
<style scoped>
.was-eaten {
color: hsl(24, 81.7%, 49.2%);
}
</style>
In Vue2, we need to use this.$set otherwise, the changed element in a specific position of the array will not be detected. More info available in the official documentation.

V-model same value in loop

I have created loops for some v-select. When I enter v-model it makes my v-select the same value. How i can fix this?
<template>
<div class="create-group">
<div class="form-group">
<button class="btn btn-defualt addGroup" v-on:click="addmember(member.value)">
<h3>Crate Group</h3>
</button>
<div class="member">
<div class="row">
<div class="col-lg-2" style="padding-right: 0;">
<h3>member in grroup :</h3>
{{member}}
</div>
<div class="col-lg-10 list" v-bind:class="{'list-member' : listActive}">
<div class="input-member" v-for="list in lists" v-bind:key="list.id">
<h3>{{list.count}}.</h3>
<div class="select">
<v-select :options="options" v-model="member"></v-select>
</div>
</div>
<div class="add-member" v-on:click="add()">
<h4>+ add member</h4>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
My data:
data() {
return {
member: [],
newitem: { name: "", type: [] },
options: [],
lists: [{ count: "1" }, { count: "2" }, { count: "3" }],
};
},
You are assigning selected value to member that is the same between all selectors. You need to use different variables for different lists, like this:
<v-select :options="options" v-model="member[list.id]"></v-select>
data() {
return {
member: {}, // <- This is object now
newitem: { name: "", type: [] },
options: [],
lists: [{ count: "1" }, { count: "2" }, { count: "3" }],
};
},

Custom radio component in latest Vue would not function at all in latest Safari

UPDATE: It is an issue of not being able to render the active class. Click does log data .
Expected
Current
RadioInput
<RadioInput
v-model="form.userType"
label="User Type"
:options="[
{ id: 'two', name: 'Investor', checked: true },
{ id: 'one', name: 'Entrepreneur', checked: false }
]"
/>
<template>
<ValidationProvider
class="mb-4 RadioInput"
tag="div"
data-toggle="buttons"
:rules="rules"
:name="name || label"
v-slot="{ errors, required, ariaInput, ariaMsg }"
>
<label
class="form-label"
:for="name || label"
#click="$refs.input.focus()"
:class="{ 'text-gray-700': !errors[0], 'text-red-600': errors[0] }"
>
<span>{{ label || name }} </span>
<small>{{ required ? " *" : "" }}</small>
</label>
<div class="btn-group btn-block btn-group-toggle" data-toggle="buttons">
<label
:for="item.id"
v-for="item in options"
class="btn btn-light"
:class="{ 'active': item.checked }"
>
<input
#click="update(item.name)"
type="radio"
:name="name || label"
class="form-control custom-control-input"
:id="item.id"
ref="input"
v-bind="ariaInput"
autocomplete="off"
:checked="item.checked"
/>
{{ item.name }}
</label>
</div>
<span
:class="{ 'invalid-feedback': errors[0] }"
v-bind="ariaMsg"
v-if="errors[0]"
>{{ errors[0] }}</span
>
</ValidationProvider>
</template>
<script>
import { ValidationProvider } from "vee-validate";
export default {
name: "RadioInput",
components: {
ValidationProvider
},
props: {
name: {
type: String,
default: ""
},
label: {
type: String,
default: ""
},
rules: {
type: [Object, String],
default: ""
},
options: {
type: Array,
default: []
}
},
methods: {
update(value) {
this.$emit("input", value);
}
},
created() {
this.$emit(
"input",
_.filter(this.options, { checked: true })[0]["name"]
);
}
};
</script>
<style scoped>
.invalid-feedback {
display: block;
}
</style>
const Wrapper = Vue.component("Wrapper", {
props: ["options", "activeIndex"],
template: `<div>
<div v-for="(item, index) in options" class="item" :class="{active: index === activeIndex}" #click="$emit('input', index)">{{item.name}}</div>
</div>`,
});
new Vue({
el: "#app",
template: `<div>
<Wrapper :options="options" :activeIndex="activeIndex" v-model="activeIndex"></Wrapper>
</div>`,
data() {
return {
options: [
{ id: "two", name: "Investor", checked: true },
{ id: "one", name: "Entrepreneur", checked: false }
],
activeIndex: -1
};
},
created() {
this.activeIndex = this.options.findIndex(i => i.checked);
}
});
body{
display: flex;
justify-content: center;
align-items: center;
}
.item{
width: 100px;
border: 1px solid;
padding: 10px;
}
.item.active{
background-color: rgba(0,0,0, 0.3);
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.0"></script>
<div id="app"></div>

How to make the fields independent?

I'm trying to make the fields/buttons independent of one another. Just like in the case of Adding more people. Example image
No matter how many fields I add, they'll not be linked to each other. I can remove/edit one of them and it'll only affect that field.
but I'm not sure how can I achieve similar behavior If I need to repeat same fields. If I click on Add Other City, a new city block would be created but with the previous data. Example image.
HTML:
<div id="app">
<div v-model="cities" class="city">
<form action="" v-for="(city, index) in cities">
<div class="column" v-for="(profile, index) in profiles">
<input type='text' v-model="profile.name" placeholder="Name">
<input type='text' v-model="profile.address" placeholder="Address">
<button type="button" #click="removeProfile">-</button>
</div>
<center><button type="button" #click="addProfile">Add More People</button></center>
<br><br><br>
</form>
<center>
<button type="button" #click="addCity">Add Other City</button>
<button type="button" #click="removeCity">Remove City</button>
</center>
</div>
</div>
JS:
new Vue({
el: '#app',
data: {
cities: [
{ name: '' }
],
profiles: [
{ name: '', address: '' }
],
},
methods: {
addProfile() {
this.profiles.push({
name: '',
address: ''
})
},
removeProfile(index) {
this.profiles.splice(index, 1);
},
addCity() {
this.cities.push({
// WHAT TO DO HERE?
})
},
removeCity(index) {
this.cities.splice(index, 1);
},
}
})
Here's a link to Jsfiddle: https://jsfiddle.net/91m8cf5q/4/
I first tried to do this.profiles.push({'name': '', 'address': ''}) inside this.cities.push({}) in addCity() but It's not possible (gives error).
What should I do to make them separate so that when I click Add Other City, new blank field would appear and removing fields from that city should not remove fields from the previous cities.
You should have profiles assigned to individual city
new Vue({
el: '#app',
data: {
cities: [],
},
mounted(){
this.cities.push({
name: '',
profiles: [
{
name: '',
address: '' }
]
});
},
methods: {
addProfile(index) {
this.cities[index].profiles.push({
name: '',
address: ''
})
},
removeProfile(index) {
this.profiles.splice(index, 1);
},
addCity() {
this.cities.push({
name: '',
profiles: [
{
name: '',
address: '' }
]
})
},
removeCity(index) {
this.cities.splice(index, 1);
},
}
})
#app form {
margin: 5px;
width: 260px;
border: 1px solid green;
padding-top: 20px;
}
.column {
padding: 5px;
}
.city {
width: 280px;
border: 1px solid red;
margin: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="city">
<form action="" v-for="(city, index) in cities">
<div class="column" v-for="(profile, index) in city.profiles">
<input type='text' v-model="profile.name" placeholder="Name">
<input type='text' v-model="profile.address" placeholder="Address">
<button type="button" #click="removeProfile">-</button>
</div>
<center><button type="button" #click="addProfile(index)">Add More People</button></center>
<br><br><br>
</form>
<center>
<button type="button" #click="addCity">Add Other City</button>
<button type="button" #click="removeCity">Remove City</button>
</center>
</div>
</div>
Profiles should be part of cities, like this
new Vue({
el: '#app',
data: {
cities: [{
name: '',
profiles: [
{ name: '', address: '' }
]}
]
},
methods: {
addProfile(city) {
city.profiles.push({
name: '',
address: ''
})
},
addCity() {
this.cities.push(
{ name: '',
profiles: [
{ name: '', address: '' }
]}
)
},
removeCity(index) {
this.cities.splice(index, 1);
},
}
})
HTML:
<div id="app">
<div class="city">
<form v-for="city in cities">
<div class="column" v-for="profile in city.profiles">
<input type='text' v-model="profile.name" placeholder="Name">
<input type='text' v-model="profile.address" placeholder="Address">
<button type="button" #click="removeProfile">-</button>
</div>
<center>
<button type="button" #click="addProfile(city)">Add More People</button>
</center>
<br><br><br>
</form>
<center>
<button type="button" #click="addCity">Add Other City</button>
<button type="button" #click="removeCity">Remove City</button>
</center>
</div>
<div>

Multi selected Filter in Vue.js

I have a selection of checkbox groups (hotel type and locations) and I want to filter the results based on what has been selected. How can I add my selectedLocations and types to the filteredHotels() method and get a filtered result. Sydney Hotels, Sydney Backpackers, Sydney or Melbourne hotels or all Hotels if only hotels is selected.
HTML
<div>
<div class="row pt-5">
<div class="col">
<h5>Locations</h5>
<label v-for="(value, key) in locations">
{{value}}
<input type="checkbox" :value="value" v-model="selectedLocations">
</label>
{{selectedLocations}}
</div>
</div>
<div class="row pt-5">
<div class="col">
<h5>Type</h5>
<label v-for="(value, key) in types">
{{value}}
<input type="checkbox" :value="value" v-model="selectedTypes">
</label>
{{selectedTypes}}
</div>
</div>
<transition-group class="row" style="margin-top:30px;" name="list" tag="div" mode="out-in">
<div class="col-sm-4 pb-3 list-item" v-for="(hotel, index) in filteredHotels" :key="index">
<div class="sau-card">
<i class="fal fa-server fa-3x"></i>
<h2>{{hotel.name}}</h2>
<p>{{hotel.type}}</p>
<p>{{hotel.location}}</p>
</div>
</div>
</transition-group>
</div>
Data
data() {
return {
locations:['Sydney','Melbourne'],
types:['backpackers','hotel','resort'],
selectedLocations:[],
selectedTypes:[],
hotels:[
{name:'a Hotel',location:'Sydney', type:'backpackers'},
{name:'b Hotel',location:'Sydney', type:'hotel'},
{name:'c Hotel',location:'Sydney', type:'resort'},
{name:'d Hotel',location:'Melbourne',type:'hotel'},
{name:'e Hotel',location:'Melbourne', type:'resort'},
{name:'f Hotel',location:'Melbourne', type:'hotel'},
]
}
},
Computed
computed:{
filteredHotels(){
if(this.selectedLocations.length){
return this.hotels.filter(j => this.selectedLocations.includes(j.location))
}
else if(this.selectedTypes.length){
return this.hotels.filter(j => this.selectedTypes.includes(j.type))
}
else{
return this.hotels;
}
}
}
Fiddle
Pass your data via binding this or using ()=>, e.g:
filteredHotels(){
return this.hotels.filter(function (el) {
return this.selectedLocations.includes(el.location)
|| this.selectedTypes.includes(el.type)
}.bind(this));
}
Fiddle
Here is a working example:
I've updated the condition under computed property as below: Include only location which is being selected. Instead of just returning the current selected location.
computed:{
filteredHotels(){
console.log(this.selectedLocations);
if(!this.selectedLocations.length) {
return this.hotels;
} else {
return this.hotels.filter(j => this.selectedLocations.includes(j.location))
}
}
}
new Vue({
el: "#app",
data: {
locations: ['Sydney', 'Melbourne'],
types: ['backpackers', 'hotel', 'resort'],
selectedLocations: [],
selectedTypes: [],
filtersAppied: [],
hotels: [{
name: 'a Hotel',
location: 'Sydney',
type: 'backpackers'
},
{
name: 'b Hotel',
location: 'Sydney',
type: 'hotel'
},
{
name: 'c Hotel',
location: 'Sydney',
type: 'resort'
},
{
name: 'd Hotel',
location: 'Melbourne',
type: 'hotel'
},
{
name: 'e Hotel',
location: 'Melbourne',
type: 'resort'
},
{
name: 'f Hotel',
location: 'Melbourne',
type: 'hotel'
},
]
},
methods: {
setActive(element) {
if (this.filtersAppied.indexOf(element) > -1) {
this.filtersAppied.pop(element)
} else {
this.filtersAppied.push(element)
}
},
isActive: function (menuItem) {
return this.filtersAppied.indexOf(menuItem) > -1
},
},
computed: {
filterApplied: function() {
if (this.filtersAppied.indexOf(element) > -1) {
this.filtersAppied.pop(element)
} else {
this.filtersAppied.push(element)
}
},
filteredHotels: function() {
return this.hotels.filter(hotel => {
return this.filtersAppied.every(applyFilter => {
if (hotel.location.includes(applyFilter)) {
return hotel.location.includes(applyFilter);
}
if (hotel.type.includes(applyFilter)) {
return hotel.type.includes(applyFilter);
}
});
});
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
<div class="row pt-5">
<div class="col">
{{filteredHotels}}
<h5>Locations</h5>
<label v-for="(value, key) in locations">
{{value}}
<input type="checkbox" :value="value" v-model="selectedLocations" :checked="isActive(value)" #click="setActive(value)">
</label> {{selectedLocations}}
</div>
</div>
<div class="row pt-5">
<div class="col">
<h5>Type</h5>
<label v-for="(value, key) in types">
{{value}}
<input type="checkbox" :value="value" v-model="selectedTypes"
:checked="isActive(value)"#click="setActive(value)">
</label> {{selectedTypes}}
</div>
</div>
<div class="row">
<div class="col-sm-3 pb-4" v-for="hotel in filteredHotels">
<div class="card text-center">
<h1>{{hotel.name}}</h1>
<p>{{hotel.location}}</p>
</div>
</div>
</div>
</div>
</div>
Hope this helps!