previous v-expansion-panel not closing when another one is opened - vuetify.js

From what I've seen it should close without adding anything special but every time I click on another expandable item the previous one doesn't close.
<template>
<div d-flex p-0 m-0>
<div mb-3>
<div v-for="item in items" :key="item.Id">
<div v-if="item.HasChildren === true">
<div
class="scanList pa-2 font-weight-light"
v-ripple="{ class: 'success--text' }"
#click="swapComponent(item)"
>
{{ item.Name }}
</div>
</div>
<div v-else-if="item.HasChildren === false">
<v-expansion-panel>
<v-expansion-panel-content>
<template v-slot:header>
<div>{{ item.Name }}</div>
</template>
<v-card>
<QR justify-center :value="item.Id"></QR>
</v-card>
</v-expansion-panel-content>
</v-expansion-panel>
</div>
</div>
<div v-if="model != null && model.HasChildren === false">
<v-card>
<template v-slot:header>
<div>{{ item.FullPathName }}</div>
</template>
<QR justify-center :value="model.Id"></QR>
</v-card>
</div>
</div>
<div v-if="items !== initialItems">
<div class="backButton" #click="swapPrevious()">
<v-icon class="arrow">fa-arrow-left</v-icon>
</div>
</div>
</div>
</template>
I'm on Vuetify 1.5. Thanks for the help.

You are trying to create a separate expansion panel in the loop and its independent, explicitly we can define a logic to close the other panels
Working codepen here (using vuetify 1.5x)
Changes for HTML:
Added a event trigger on wrapper around expansion panel.
Added v-model for each expansion panel.
<div v-else-if="item.HasChildren === false" #click="closeOtherPanel(item.Id)">
<v-expansion-panel v-model="panel[item.Id]">
<v-expansion-panel-content>
<template v-slot:header>
<div>{{ item.Name }}</div>
</template>
<v-card>
<QR justify-center :value="item.Id"></QR>
</v-card>
</v-expansion-panel-content>
</v-expansion-panel>
</div>
Changes for script:
Add panel property inside data object.
Add a method to close other panels
data: {
panel: {},
},
methods: {
closeOtherPanel(id) {
var self = this;
Object.keys(this.panel).map(x => {
if (x != id) self.panel[x] = null;
});
}
}

Related

List recursive with checkbox component data into parent component update values

Help to list recursive with checkbox component data into parent component update values:
Parent File:
<TreeVue :items="props.allModules" :selectedItems="userForm.userModules"/>
TreeVue File:
<template>
<div v-for="item in items" :key="item.name" class="overflow-hidden rounded-lg bg-gray-50">
<div class="px-4 py-5 sm:p-6" >
<div class="flex items-center ">
<input :id="item.id" type="checkbox" :value="item.id" v-model="props.selectedItems" class="w-6 h-6 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
<label for="default-checkbox" class="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">{{ item.name }}</label>
</div>
<div v-if="item.children" class="ml-5">
<Tree :items="item.children" :selectedItems="selectedItems" />
</div>
</div>
</div>
</template>
<script setup>
import { defineProps } from "vue";
const props = defineProps({
items: Object,
selectedItems: Object,
});
</script>
ConsoleLog:
app.js:7839 [Vue warn] Set operation on key "selectedItems" failed: target is readonly. Proxy {items: Proxy, selectedItems: Proxy}
I need to update parent value after selected item on child TreeVue.
I read something about Emit, but I don't know how to implement it, because I'm new to Vue.

Auto import components Nuxtjs not working

I'm confused why it didn't work on my vue file when Im trying to auto import components in my view, the documentation says link you just need to set the components into true inside nuxt.config.js file, I tried but it doesn't work, do I properly import the custom vue?
this is my directory
Components
>Timeline.vue
>TimelineItem.vue
This will works fine but I want to automate and remove the importfrom script
<template id="timeline-template">
<ul class="timeline">
<li
is="TimelineItem"
v-for="(item, index) in items"
:key="index"
:item="item"
></li>
</ul>
</template>
<script>
import TimelineItem from './TimelineItem.vue'
export default {
components: { TimelineItem },
props: ['items'],
}
</script>
So I just updated my script like this I just removed the import file since I want to automate it
<script>
export default {}
</script>
Then in my nuxt.config.js I set the component into true
components: true,
This is my TimelineItem.vue
<template id="timeline-item-template">
<li class="timeline-item">
<div class="timeline-badge" :class="item.icon_status">
<i :class="item.icon_class"></i>
</div>
<div class="timeline-panel">
<div class="timeline-heading">
<h4 class="timeline-title">{{ item.title }}</h4>
<div class="timeline-panel-controls">
<div class="controls">
<a
v-for="(control, index) in item.controls"
:key="index"
:control="control"
>
</a>
</div>
<div class="timestamp">
<small class="text-muted">{{ item.created }}</small>
</div>
</div>
</div>
<div class="timeline-body">{{ item.body }}</div>
</div>
</li>
</template>
<script>
export default {
props: ['item'],
}
</script>
Updated
I tried using <TimelineItem/> instead using is="TimelineItem but it doesn't work
<template id="timeline-template">
<ul class="timeline">
<TimelineItem v-for="(item, index) in items" :key="index" :item="item" />
</ul>
</template>
<script>
export default {
props: ['items'],
}
</script>
OP's issue was fixed by importing TimelineItem inside of components.js!
Everything is working fine, as expected.

AlpineJS: How can I add a class to an element conditionally?

I have an array of messages being pulled into view thanks to Laravel Echo, Livewire, and AlpineJS.
<div class="mt-4 rounded-lg p-6"
x-data="{{ json_encode(['messages' => $messages, 'messageBody' => '']) }}"
x-init="
Echo.join('demo')
.listen('MessageSentEvent', (e) => {
#this.call('incomingMessage', e)
})
">
<template x-if="messages.length > 0">
<template
x-for="message in messages"
:key="message.id"
>
<div class="my-8">
<div class="flex flex-row justify-between border-b border-gray-200">
<span class="text-white-600 chat-block-author" x-text="message.user.name"></span>: <span class="text-white-800" x-text="message.body" style="margin-left:10px;"></span>
</div>
</div>
</template>
</template>
</div>
I want to dynamically add a class called chat-block-author when the rendered message belongs to the logged-in user. The Message model does contain user_id for each item, but I can't seem to get AlpineJS to play well with conditional logic like I could with Blade.
Any tips?
This does not work
<template x-if="message.user_id == {{ Auth::user()->id }}">
<div class="my-8">
<div class="flex flex-row justify-between border-b border-gray-200">
<span class="text-white-600 chat-block-author" x-text="message.user.name"></span>: <span class="text-white-800" x-text="message.body" style="margin-left:10px;"></span>
</div>
</div>
</template>
as it produces this error
Uncaught (in promise) ReferenceError: message is not defined
at eval (eval at tryCatch.el.el (alpine.js?df24:1), <anonymous>:3:36)
at tryCatch.el.el (alpine.js?df24:140)
at tryCatch (alpine.js?df24:127)
at saferEval (alpine.js?df24:135)
at Component.evaluateReturnExpression (alpine.js?df24:1747)
at eval (alpine.js?df24:1714)
at Array.forEach (<anonymous>)
at Component.resolveBoundAttributes (alpine.js?df24:1696)
at Component.updateElement (alpine.js?df24:1672)
at eval (alpine.js?df24:1628)
You did not mention where you want to add that class to, but in AlpineJS you can dynamically assign any attribute including classes by doing something like this:
<div :class="{ 'chat-block-author': message.user_id === {{ Auth::user()->id }} }" class="your-other-classes go-here">
...
</div>
Note that you can also use it with an existing class attribute, the attributes defined in :class are dynamically added to your class attribute if the specified condition is true.

Laravel/VueJs. How can I display button with different color based on column value passed in?

I'm looping through user table. And each user table has a button that toggle(update:Boolean type) user status on user table. And I want the button for each user to have red color when the status value for the user is 0 and green when the status is 1. Also I want the button to change the color(LIVE) based on the status value each time it clicked.
component: ToggleStatus.vue
<template>
<div>
<button type="button" v-bind:class="buttonStatus" #click="goOnline">
<span class="ion-ios-wifi" ></span>
</button>
</div>
</template>
<script>
export default {
props: ['userId', 'onlinestat'],
mounted() {
console.log('Component mounted.')
},
data: function() {
return {
status: this.onlinestat,
}
},
methods: {
goOnline() {
axios.post('/reservation/online/' + this.userId )
.then(response => {
this.status =! this.status;
console.log(response.data);
})
.catch(errors => {
if (errors.response.status == 401){
window.location = '/login';
}
});
}
},
computed: {
buttonStatus() {
return(this.status) ? 'btn btn-outline-danger text-danger' : 'btn btn-success ';
}
}
}
</script>
Controller:
$authUser = Auth::user()->business_id;
$employee = User::where('business_id', $authUser)->get()->sortBy('name');
return view('backend.reservation.index', compact('employee'));
View/Blade
<div class="container-fluid flex-grow-1 container-p-y">
<div class="uikit-example">
<div class="row">
#foreach ($employee as $key => $emp)
<div class="col-lg-4 col-md-4 col-sm-12">
<div class="card mb-4">
<div class="card-body">
<div class="media align-items-center">
<img src="{{ asset('Upload/Images/Media/Avatar/Thumb/'.$emp->image) }}" alt class="ui-w-60 rounded-circle">
<div class="media-body pt-2 ml-3">
<h5 class="mb-2">{{ $emp->name }}</h5>
<div class="text-muted small mb-2">{{ $emp->phone }}</div>
<div class="text-muted small mb-2">{{ $emp->email }}</div>
<div class="text-muted small mb-2"><span>ID: </span>{{ $emp->user_code }}</div>
</div>
<div class="">
<toggleonline user-id="{{ $emp->id }}" onlinestat="{{ $emp->onlinestatus }}"></toggleonline>
</div>
</div>
</div>
<div class="card-footer text-center py-3">
{{-- + Go Online --}}
{{-- <span class="ion ion-md-mail"></span> --}}
{{-- <div class="float-right"> --}}
My Reservations
{{-- </div> --}}
</div>
</div>
</div>
#endforeach
</div>
</div>
</div>
The result I'm getting is that the button does toggle but the toggle button of all the user come red after on refresh page or when the page loads.
try
buttonStatus() {
return(this.status==0) ? 'btn btn-outline-danger text-danger' : 'btn btn-success ';
}

Collapse using Transition Vue

I would like to use Vue's collapse in my code, but I have an error.
[Vue warn]: <transition-group> children must be keyed: <p>
My component:
<template xmlns:v-model="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<section style="background-color: #dedede;">
<div class="container-fluid">
<div class="Consult-faq container">
<div class="row">
<div class="col-sm-12">
<h2>Cursos</h2>
<a v-for="(course,id) in courses" v-on:click="course.show = !course.show">
<a v-on:click="show = !show">
<div class="col-xs-12" style="border-bottom: solid;border-bottom-color: #999999;border-bottom-width:1px ">
<div class="col-xs-12">
<h4>
<i v-if="course.show" class="fa fa-plus-square text-right" aria-hidden="true"/>
<i v-else class="fa fa-minus-square text-right" aria-hidden="true"/>
{{course.text}}
</h4>
</div>
</div>
<transition-group name="fade">
<p v-if="show">
<div class="col-xs-12">
<article v-for="n in 2" class="Module-content">
<div class=" col-sm-12 col-md-6" style="position: relative;">
<div v-for="(course, index) in course.courses">
<course-card v-if="index % 2 == n - 1" :course="course"></course-card>
</div>
</div>
</article>
</div>
</p>
</transition-group>
</a>
</a>
</div>
</div>
</div>
</div>
</section>
</template>
<script>
export default{
props : [
'courses'
],
data(){
return {
show: false
}
},
mounted() {
console.log(this.courses)
}
}
</script>
So, I'd like to know to collapse item per item. Like this in image.
When I click to expand, all courses expand or close all courses close.
Transition is irrelevant here (though you can get rid of that warning by using transition instead of transition-group, because the transition is only acting on a single node, not a group.)
Right now you're depending on a single variable show to control all of the elements' visibility, so they will all respond to clicks on any of them:
<a v-on:click="show = !show">
<p v-if="show" >
You need individual variables for each element if you want them to expand/collapse separately. You partially did this already, just change the remaining instances of show with course.show and you should be good to go.
(Probably want to clean up that nested <a> within <a> while you're at it; you can just remove the inner one.)
I solved this using vue-resource, I was using Guzzle in Laravel and require data in Controller make this not reactive. And I solved this problem using vue-resource in component.

Resources