Vue Accordion with dropdown select filter. How to bind data - vue.js

I am trying to get a local Vue project working where there is an E-Library Accordion with 3 types of media and a dropdown button to filter between the types of media. I am also trying to get a color coding system going and I attempted this through created a class and trying to bind it (not working). I am trying to filter the media with a method and then applying that method as an #click event to each dropdown button, but I know this is probably poor practice or just not the correct way to do this. Can anyone point me in the right direction as to how to get these two features working correctly? Much appreciated.
This is my page that I am routing to and creating the code on for the Vue project:
<template>
<label for="accordion">Choose type of media:</label>
<select name="accordion-types" id="types">
<option #click="filteredAccordion(index)" value="book">Book</option>
<option #click="filteredAccordion(index)" value="dvd">DVD</option>
<option #click="filteredAccordion(index)" value="stream">Streaming Video</option>
</select>
<div v-for="(accordion, index) in accordions" :key="index">
<button :class="{red: accordions.type === 'Book' }" #click="toggleOpen(index)">
{{ accordion.title }}
</button>
<div class="panel" :class="{open: accordion.isOpen}">
<p>{{ accordion.content }} </p>
<p>{{ accordion.type }}</p>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
accordions: []
};
},
created: function () {
const appData = this;
axios.get("data.json").then(function (response) {
appData.accordions = appData.addIsOpen(response.data.accordions);
});
},
methods: {
addIsOpen: function(items) {
return items.map(function(item) {
item.isOpen = false;
return item;
});
},
toggleOpen: function(index) {
this.accordions[index].isOpen = !this.accordions[index].isOpen;
}
},
computed: {
filteredAccordion: function(items) {
return this.accordions.filter(accordions => !accordions.type.indexOf(this.type));
}
}
};
</script>
<style>
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
padding: 18px;
width: 100%;
text-align: left;
border: none;
outline: none;
transition: 0.4s;
}
.active, .accordion:hover {
background-color: #ccc;
}
.panel {
padding: 0 18px;
background-color: white;
display: none;
overflow: hidden;
}
.panel.open {
display: block;
}
.red {
color: red;
}
</style>

Related

How can i change style of the body when my modal it will be open?

How can i change the body{overflow:hidden} when my modal it will be open?
for example it will be my modal, when its open, i would like to apply this style body{overflow:hidden}
<div v-if="dialogFoundation">
i am using vuejs3, i am using setup(){...}
The best performance would be to use javascript plain. You can add Eventlistener top the modal trigger Element. In my example i use a button. If it triggered then you can use classList and assign the body a class. In my example .dark.
Vue version
<!-- Use preprocessors via the lang attribute! e.g. <template lang="pug"> -->
<template>
<div id="app">
<h1>{{message}}</h1>
<p></p>
<button #click="doSomething">Modal</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Welcome to Vue!'
};
},
methods: {
doSomething() {
const b = document.querySelector('body');
b.classList.toggle('dark');
}
}
};
</script>
<!-- Use preprocessors via the lang attribute! e.g. <style lang="scss"> -->
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
a,
button {
color: #4fc08d;
}
button {
background: none;
border: solid 1px;
border-radius: 2em;
font: inherit;
padding: 0.75em 2em;
}
.dark {
background: black;
opacity: 0.4;
}
</style>
Vanilla JS
const btn = document.querySelector('button');
btn.addEventListener('click', () => {
const b = document.querySelector('body');
b.classList.toggle('dark');
})
.dark {
background: black;
opacity: 0.4;
}
<body>
<div></div>
<button>click</button>
</body>
You can use watchers in Vue.js for solving this problem.
When variables changes you can check whether it is true or not, and if true change overflow of body to hidden.
{
watch: {
dialogFoundation(dialogFoundation) {
document.body.style.overflow = dialogFoundation ? "hidden" : "auto"
}
}
}
But I think this is not good solution. You can set this styles to your app element
#app {
width: 100%;
height: 100%;
overflow: auto;
}
and you can change style of app element using Vue directives.
<template>
<div id="app" :class="{ hidden: dialogFoundation }">
Long text....
</div>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
const dialogFoundation = ref(true);
return { dialogFoundation };
},
};
</script>
<style>
html,
body,
#app {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
box-sizing: border-box;
}
#app {
overflow: auto;
}
#app.hidden {
overflow: hidden;
}
</style>
Code in codesandbox - https://codesandbox.io/s/immutable-glitter-rwc2iy?file=/src/App.vue

How to implement html drag and drop using vue 3 composition API

currently drag and drop feature is working with vue2, i want to achieve same feature using vue3 composition api.
vue2 code:
<div id="app">
<div id="box-droppable1" #drop="drop" #dragover="allowDrop">
<h3>Draggaable area 1:</h3>
<hr>
<div class="" draggable="true" #dragstart="onDragging" id="123">
<h2>Drag mee</h2>
<p>this is a text</p>
</div>
<img id="img-draggable" src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png" draggable="true" #dragstart="drag" width="336">
</div>
<div id="box-droppable2" #drop="drop" #dragover="allowDrop">
<h3>Droppable area 2:</h3>
<hr>
</div>
</div>
Here is vuejs code done using vuejs options API.
JS:
new Vue({
el: '#app',
data(){
return {
};
},
methods : {
onDragging(ev){
console.log(ev);
ev.dataTransfer.setData("text", ev.target.id);
//this.$store.commit('module/namespace', status);
},
allowDrop(ev) {
ev.preventDefault();
},
drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
},
drop(ev) {
ev.preventDefault();
let data = ev.dataTransfer.getData("text");
console.log(data);
ev.target.appendChild(document.getElementById(data));
}
},
})
css:
#app{
width: 100%;
display: flex;
#box-droppable1 {
width: 50%;
background-color: coral;
min-height: 300px;
height: 70px;
padding: 10px;
border: 1px solid #aaaaaa;
}
#box-droppable2 {
width: 50%;
min-height: 300px;
height: 70px;
padding: 10px;
border: 1px solid #aaaaaa;
}
}
---------------------#---------------------------------------------------------------------------------------#------------------
codepen
As the comments already mention, this is nothing that would be different in the composition API, which is just another way to define a component.
All the methods you have in the options API, you can just have them in the setup method and return them:
setup() {
const onDragging = (ev) => {
console.log(ev);
ev.dataTransfer.setData("text", ev.target.id);
};
const allowDrop = (ev) => {
ev.preventDefault();
};
const drag = (ev) => {
ev.dataTransfer.setData("text", ev.target.id);
};
const drop = (ev) => {
ev.preventDefault();
let data = ev.dataTransfer.getData("text");
console.log(data);
ev.target.appendChild(document.getElementById(data));
}
return {
onDragging,
allowDrop,
drag,
drop,
}
}
I would probably not directly append a child with vanila js but also do it the Vue way, but that's just a side note.

How can one access the state of other instances of a reusable component in Vue.js

Being new to Vue, came across the following use-case. I have an accordion component that is reused across the application. The accordion simply hides and reveals anything wrapped inside it.
How can I enable closing any other open instance when I open an instance? e.g if prototype accordion is open, then functions and objects accordion is clicked, prototype accordion should close.
const vue = Vue.createApp({});
vue.component('accordion', {
data() {
return {
open: false,
}
},
methods: {
toggle() {
this.open = !this.open;
},
},
template: `
<div class="accord">
<p #click="toggle" class="accord-header">Click me to open and close!</p>
<div v-if="open" class="accord-body">
<slot></slot>
</div>
</div>
`,
})
vue.mount('#main-app');
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
#main-app {
width: 50%;
margin: 0 auto;
}
.accord {
padding: 4px;
cursor: pointer;
}
.accord-header {
text-align: center;
padding: 4px 0;
}
.accord-body {
background-color: #eee;
padding: 8px;
}
<script src="https://unpkg.com/vue#next"></script>
<div id="main-app">
<accordion>
<p class="accord-text">Javascript Proptotypes and inheritance</p>
</accordion>
<accordion>
<p class="accord-text">Scopes and Closures</p>
</accordion>
<accordion>
<p class="accord-text">Functions and Objects</p>
</accordion>
</div>
You can change open to be a prop, then you can do something like activeAccordianId: 1 in the parent and have the prop be
<accordian :open="activeAccordianId === 1" />
<accordian :open="activeAccordianId === 2" />
...

How to add stripe elements in vue2 from?

In Laravel 5.8 / vuejs 2.6 / vuex / mysql app I need to add stripe elements from https://stripe.com/docs/stripe-js,
http://prntscr.com/phflkd
and for this in resources/views/index.blade.php I added line:
#include('footer')
<script src="{{ asset('js/jquery.min.js') }}"></script>
<script src="{{ asset('js/bootstrap.bundle.min.js') }}"></script>
<script src="{{ asset('js/waves.min.js') }}"></script>
<script src="{{ asset('js/jquery.slimscroll.min.js') }}"></script>
<script src="{{ asset('js/powerange.js') }}"></script>
<script src="{{ asset('js/appInit.js') }}"></script>
<script src="{{ asset('js/app.js') }}{{ "?dt=".time() }}"></script>
<script src="https://js.stripe.com/v3/"></script>
</html>
and in my vue form I init stripe in initStripe() method, which is called in mount event :
<template>
<div class="page-content col-md-offset-2">
<div class="sign-up container-fluid justify-content-center" style="max-width: 460px;">
<hr>
<hr>
<form action="/charge" method="post" id="payment-form">
<div class="form-row">
<label for="card-element">
Credit or debit card
</label>
<div id="card-element">
<!-- A Stripe Element will be inserted here. -->
</div>
<!-- Used to display form errors. -->
<div id="card-errors" role="alert"></div>
</div>
<button>Submit Payment</button>
</form>
<button type="button"
class="btn btn-outline-pink btn-round waves-effect waves-light cancel-btn mr-5" #click.prevent="cancelSelectedSubscription()">
<i :class="getHeaderIcon('cancel')"></i> Cancel
</button>
</div>
</div>
</template>
<script>
import {bus} from '../../../../app';
import appMixin from '../../../../appMixin';
import Vue from 'vue';
export default {
data: function () {
return {
is_page_loaded: false,
}
},
name: 'selectedSubscription',
created() {
if ( typeof this.currentLoggedUser.id != 'number' ) {
this.showPopupMessage("Access", 'Your session is expired !', 'error');
this.$store.commit('logout');
}
this.message = '';
}, // created) {
mounted() {
this.is_page_loaded = true
this.setAppTitle("Selected Subscription", 'Selected Subscription Details', bus);
this.initStripe();
}, // mounted() {
mixins: [appMixin],
methods: {
cancelSelectedSubscription() {
this.$router.push({path: '/personal-details'});
},
initStripe()
{
console.log("Stripe -1::")
// Create a Stripe client.
var stripe = Stripe('pk_test_NNNN');
console.log("Stripe -2::")
// Create an instance of Elements.
var elements = stripe.elements();
console.log("Stripe -3::")
// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
base: {
color: '#32325d',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
// Create an instance of the card Element.
var card = elements.create('card', {style: style});
console.log("Stripe -4::")
// Add an instance of the card Element into the `card-element` <div>.
card.mount('#card-element');
// Handle real-time validation errors from the card Element.
card.addEventListener('change', function (event) {
var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
console.log("Stripe -5::")
// Handle form submission.
var form = document.getElementById('payment-form');
form.addEventListener('submit', function (event) {
event.preventDefault();
stripe.createToken(card).then(function (result) {
if (result.error) {
// Inform the user if there was an error.
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server.
this.stripeTokenHandler(result.token);
}
});
});
}, // initStripe() {
// Submit the form with the token ID.
stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
var form = document.getElementById('payment-form');
alert( "stripeTokenHandler form::"+var_dump(form) )
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
},
}, // methods: {
computed: {
currentLoggedUser() {
return this.$store.getters.currentLoggedUser;
},
...
} //computed: {
}
</script>
<style lang="css">
/**
* The CSS shown here will not be introduced in the Quickstart guide, but shows
* how you can use CSS to style your Element's container.
*/
.StripeElement {
box-sizing: border-box;
height: 40px;
padding: 10px 12px;
border: 1px solid transparent;
border-radius: 4px;
background-color: white;
box-shadow: 0 1px 3px 0 #e6ebf1;
-webkit-transition: box-shadow 150ms ease;
transition: box-shadow 150ms ease;
}
.StripeElement--focus {
box-shadow: 0 1px 3px 0 #cfd7df;
}
.StripeElement--invalid {
border-color: #fa755a;
}
.StripeElement--webkit-autofill {
background-color: #fefde5 !important;
}
</style>
As result in browser's console I see console messages, “Credit or debit card” label and
uncolored “Submit Payment” button : https://imgur.com/a/TRWc23I
If to click on “Submit Payment” button I see :https://imgur.com/a/CdBSfMC
Is way I added Stripe to my vue form invalid? Which is valid way?
I suppose that I do not have to insert any additive elements/code in this block :
<div id="card-element">
<!-- A Stripe Element will be inserted here. -->
</div>
and they must be uploaded automatically on my form upload ?
and which method have I to use as in my app I save my data with axios?
I made all correctly, it was styles issue.
I replaced css from the example with lines :
#card-element {
line-height: 1.5rem;
margin: 10px;
padding: 10px;
}
.__PrivateStripeElement {
min-width: 300px !important;
min-height: 40px !important;
color: $text_color;
}
and my form was ready for payment!

Vue Accordion with transition

I'm trying to integrate the Accordion component with a body transition, but without success :( . All is working as well except the animation.
template:
<div class="accordion">
<div class="accordion-title" #click="isOpen = !isOpen" :class="{'is-open': isOpen}">
<span>{{title}}</span>
<i class="ic ic-next"></i>
</div>
<div class="accordion-body" :class="{'is-open': isOpen}">
<div class="card">
<slot name="body"></slot>
</div>
</div>
</div>
component:
props: {
title: {
type: String,
default: 'Title'
}
},
data() {
return {
isOpen: false
}
}
And styles:
.accordion-body {
font-size: 1.3rem;
padding: 0 16px;
transition: .3s cubic-bezier(.25,.8,.5,1);
&:not(.is-open) {
display: none;
height: 0;
overflow: hidden;
}
&.is-open {
height: auto;
// display: block;
padding: 16px;
}
}
.card {
height: auto;
}
I tried to use <transition> but it doesn't work with height or display properties.
Help please!
display:none will remove your content and avoid the animation, you should trick with opacity, overflow:hidden and height, but you ll be forced to do a method for that.
For example (not tested, but inspiring):
in template:
<div class="accordion" #click="switchAccordion" :class="{'is-open': isOpen}">
<div class="accordion-title">
<span>{{title}}</span>
<i class="ic ic-next"></i>
</div>
<div class="accordion-body">
<p></p>
</div>
</div>
in component (add a method):
methods: {
switchAccordion: function (event) {
let el = event.target
this.isOpen = !this.isOpen // switch data isOpen
if(this.isOpen) {
let childEl1 = el.childNodes[1]
el.style.height = childEl1.style.height
} else {
let childEl2 = el.childNodes[2]
el.style.height = childE2.style.height // or .clientHeight + "px"
}
}
}
in style:
.accordion {
transition: all .3s cubic-bezier(.25,.8,.5,1);
}
.accordion-body {
font-size: 1.3rem;
padding: 0 16px;
opacity:0
}
.is-open .accordion-body {
opacity:0
}
In this case, your transition should work as you want.
The javascript will change the height value and transition transition: all .3s cubic-bezier(.25,.8,.5,1); will do the animation