I wanted to make the lower menu similar to VK, where you click on the icons and the content changes. I implemented different content, but I can't make each tab have its own icon.
I provide a screenshot and code. Do not pay attention to the style, I first need to understand the logic Using Vue CLI.
<template>
<div id="app">
<font-awesome-icon
icon="user-secret"
v-for="tab in tabs"
:key='tab'
#click="currentTab = tab"
:class="['tab-button', {active: currentTab === tab}]"
>
{{ tab }}
</font-awesome-icon>
<component v-bind:is="currentTabComponent"></component>
</div>
</template>
<script>
import Posts from './Posts.vue'
import List from './List.vue'
export default {
data () {
return {
currentTab: 'Posts',
tabs: ['Posts', 'List'],
icon: 'bell'
}
},
components: {
tabPosts: Posts,
tabList: List
},
computed: {
currentTabComponent() {
return 'tab-' + this.currentTab.toLowerCase()
}
}
}
</script>
<style scoped>
body {
overflow: auto;
padding: 0;
margin: 0;
}
#app {
position: relative;
width: 320px;
height: 400px;
border: solid 1px black;
}
.tab-button.active {
position: relative;
color: red;
height: 50px;
width: 50px;
}
.tab-button {
position: relative;
float: right;
bottom: 10px;
height: 50px;
width: 50px;
}
</style>
Try this:
<div
v-for="tab in tabs"
:key='tab'
#click="currentTab = tab"
:class="['tab-button', {active: currentTab === tab}]"
>
<font-awesome-icon >
icon="user-secret"
</font-awesome-icon >
</div>
also this #click="currentTab = tab" needs to be refactored
I'll assume that this issue has already been solved, but, in any case, here is my contribuition using fontAwesome so you can understand the logic:
Change the definition of your data tabs and currentTab to something like this:
currentTab: {title:'Post section',icon:'bell',page:'Post'},
tabs:{
title:'Post section',
icon:'bell',
page:'Post'
},
{
title:'List of posts',
icon:'list',
page:'List'
}
Convert your font-awesome-icon tag to:
<button v-for="tab in tabs" :key="tab"
:class="['tab-button', { active: currentTab === tab }]" #click="currentTab = tab"
title="tab.title">
<i class="fa" :class="'fa-'+tab.icon"></i>
</button>
Finally change you component tag to:
<component :is="currentTab.page" class="tab"></component>
And them you can ignore the whole compuded: {} section of your export default
Related
So I have a problem with VueJs. I created a "Confirmation Dialogue" and added it to a On-Click-Event on buttons. It worked fine.
Now I tried to copy the implementation to add it to another button on a different parent. It says "TypeError: this.$refs.confirmDialogue.show is not a function" in the Console whenever I try to click the button. The other button still works completly normal.
Am I missing something? I already tried to remove the working button, so only one component uses the Confirm Dialogue but that also didn't work.
I'm new to VueJs. Hope someone can help me with this problem.
Child PopupModal:
<template>
<transition name="fade">
<div class="popup-modal" v-if="isVisible">
<div class="window">
<slot></slot>
</div>
</div>
</transition>
</template>
<script>
export default {
name: 'PopupModal',
data: () => ({
isVisible: false,
}),
methods: {
open() {
this.isVisible = true
},
close() {
this.isVisible = false
},
},
}
</script>
<style scoped>
/* css class for the transition */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.popup-modal {
background-color: rgba(0, 0, 0, 0.5);
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: 0.5rem;
display: flex;
align-items: center;
z-index: 1;
}
.window {
background: #fff;
border-radius: 5px;
box-shadow: 2px 4px 8px rgba(0, 0, 0, 0.2);
max-width: 480px;
margin-left: auto;
margin-right: auto;
padding: 1rem;
}
</style>
Parent ConfirmDialogue:
<template>
<popup-modal ref="popup">
<h2 style="margin-top: 0">{{ title }}</h2>
<p>{{ message }}</p>
<div class="btns">
<button class="cancel-btn" #click="_cancel">{{ cancelButton }}</button>
<span class="ok-btn" #click="_confirm">{{ okButton }}</span>
</div>
</popup-modal>
</template>
<script>
import PopupModal from "../confirmDialogue/PopupModal.vue"
export default {
name: 'ConfirmDialogue',
components: { PopupModal },
data: () => ({
// Parameters that change depending on the type of dialogue
title: undefined,
message: undefined, // Main text content
okButton: undefined, // Text for confirm button; leave it empty because we don't know what we're using it for
cancelButton: 'Abbrechen', // text for cancel button
// Private variables
resolvePromise: undefined,
rejectPromise: undefined,
}),
methods: {
show(opts = {}) {
this.title = opts.title
this.message = opts.message
this.okButton = opts.okButton
if (opts.cancelButton) {
this.cancelButton = opts.cancelButton
}
// Once we set our config, we tell the popup modal to open
this.$refs.popup.open()
// Return promise so the caller can get results
return new Promise((resolve, reject) => {
this.resolvePromise = resolve
this.rejectPromise = reject
})
},
_confirm() {
this.$refs.popup.close()
this.resolvePromise(true)
},
_cancel() {
this.$refs.popup.close()
this.resolvePromise(false)
// Or you can throw an error
// this.rejectPromise(new Error('User cancelled the dialogue'))
},
},
}
</script>
<style scoped>
.btns {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.ok-btn {
padding: 0.5em 1em;
background-color: #1F51FF;
color: #fff;
border: 2px solid #0ec5a4;
border-radius: 5px;
font-size: 16px;
text-transform: uppercase;
cursor: pointer;
}
.cancel-btn {
padding: 0.5em 1em;
background-color: #d5eae7;
color: #000;
border: 2px solid #0ec5a4;
border-radius: 5px;
font-size: 16px;
text-transform: uppercase;
cursor: pointer;
}
</style>
Working button:
<td class="last-td last-row">
<div class="button-wrapper">
<div class="wrapper-edit">
<button class="button button-edit">Bearbeiten</button>
</div>
<div class="wrapper-cancel">
<button class="button button-cancel" #click="doDelete">Löschen</button> <!-- Here is the working button -->
<confirm-dialogue ref="confirmDialogue"></confirm-dialogue>
</div>
</div>
</td>
</tr>
</thead>
</template>
<script>
import ConfirmDialogue from '../confirmDialogue/ConfirmDialogue.vue'
export default {
name: "bookingElements",
components: { ConfirmDialogue },
methods: {
async doDelete() {
const ok = await this.$refs.confirmDialogue.show({
title: 'Buchung löschen',
message: 'Sind Sie sicher, dass Sie die Buchung löschen wollen?',
okButton: 'Buchung löschen',
})
if (ok) {
alert('Die Buchung wurde Erfolgreich gelöscht')
}
},
Button that isn't working:
<td id="buttonCell">
<button class="button" #click="doDelete">Buchen</button>
<confirm-dialogue ref="confirmDialogue"></confirm-dialogue>
</td>
</tr>
</tbody>
</template>
<script>
import ConfirmDialogue from "../confirmDialogue/ConfirmDialogue.vue"
export default {
name: "bookElements",
components: { ConfirmDialogue },
methods: {
async doDelete() {
const ok = await this.$refs.confirmDialogue.show({
title: 'Buchung löschen',
message: 'Sind Sie sicher, dass Sie die Buchung löschen wollen?',
okButton: 'Buchung löschen',
})
if (ok) {
alert('Die Buchung wurde Erfolgreich gelöscht')
}
},
I got found the mistake.
I created a for-each loop to create the Table.
At the working button there was no for-each loop though.
So VueJs tried to generate the Confirmation Dialogue 9 times and this resulted to an error.
So I just need to put the
<confirm-dialogue ref="confirmDialogue"></confirm-dialogue>
to the top of Template like this
<template>
<confirm-dialogue ref="confirmDialogue"></confirm-dialogue>
...
</template>
because it's vanished and only shows when the button is clicked.
I have a regular block I want to do so that when you click on an empty space, this block closes here is a link to codesandbox
<template>
<div>
<div class="wrapper"></div>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String,
},
};
</script>
<style scoped>
.wrapper {
width: 300px;
height: 150px;
background: green;
margin: auto;
}
</style>
Using an event bus to communicate between window click event listener and the component should be one way to go.
You can work on top of this codesandbox
You could do something like this. Using a backdrop and detects when it got clicked. Is this what you want?
App.vue
<template>
<div id="app" class="backdrop" #click.self="showBox = !showBox">
<img alt="Vue logo" src="./assets/logo.png" width="25%" />
<HelloWorld :showBox="showBox" msg="Hello Vue in CodeSandbox!" />
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name: "App",
components: {
HelloWorld,
},
data: function () {
return {
showBox: true,
};
},
};
</script>
<style>
#app {
text-align: center;
margin-top: 40px;
}
.backdrop {
z-index: 0;
position: fixed;
width: 100%;
height: 100%;
}
</style>
HelloWorld.vue
<template>
<div>
<div v-if="showBox" class="wrapper"></div>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String,
showBox: Boolean,
},
};
</script>
<style scoped>
.wrapper {
width: 300px;
height: 150px;
background: green;
margin: auto;
}
</style>
I have multiple dropdowns. Now when i open one dropdown. and then open the second one. Both are open and the content is the same. I want, if the user want to open a second one the first one will close.
{this.state.MainChannels.map(item => {
return (
<Category key={item.name} >
<CategoryContainer >
<CheckboxLabel>
<CheckboxInput type="checkbox" checked={item.followed} onChange={() => this.handleCheckAllSubCategories(item)} >
</CheckboxInput>
<CheckboxButton className="fa fa-check" >
</CheckboxButton>
</CheckboxLabel>
<CategoryTitle>
{item.name}
</CategoryTitle>
<ChannelImage style={{ backgroundImage: `linear-gradient(to right, rgba(27,31,31,1) , rgba(255,255,255,0)), url(${item.image})` }}>
</ChannelImage>
</CategoryContainer>
<OpenInput type="checkbox" onClick={() => this.handleChannel(item.name)}>
</OpenInput>
<OpenButton className="fa fa-chevron-down" >
</OpenButton>
{subChannels.map((item2, i): any => {
return (
<Subcategories key={i} >
<SubCheckboxLabel>
<SubCheckboxInput id={item2.name} type="checkbox" onChange={() => this.handleCheckbox(item2.name)} checked={item2.followed}>
</SubCheckboxInput>
<SubCheckboxButton className="fa fa-check" >
</SubCheckboxButton>
<SubCategoryTitle>
{item2.name}
</SubCategoryTitle>
<div>
</div>
</SubCheckboxLabel>
</Subcategories>)
})}
</Category>
);
}
const OpenInput = styled.input`
position: absolute;
top: 0px;
right: 0px;
height: 50px;
width: 40px;
z-index: 1000;
opacity: 0;
cursor:pointer;
&:checked ~ p {
transform: rotate(180deg);
}
&:checked ~ ul {
display: block;
}
const OpenInput = styled.input`
position: absolute;
top: 0px;
right: 0px;
height: 50px;
width: 40px;
z-index: 1000;
opacity: 0;
cursor:pointer;
&:checked ~ p {
transform: rotate(180deg);
}
&:checked ~ ul {
display: block;
}
`
My Problem is this last block. if the user clicks on Open input. The "ul" parts will be displayed. How can i replace this with some JS?
Here a picture what the problem is:
We see there is the same content. That should not be it should close one dropdown
It's hard to tell from this code how you're deciding what is open or closed as we can't see what your handlers are doing, but I'd suggest using state to keep track of which item is open based on the item name (assuming it is unique).
Then it would be as simple as passing a boolean to your dropdown, 'isOpen' for example, based on the current item in the state, which would be updated by the event handler.
Here is the example with accordions that i was talking about in the comment section. You can use the same principle to open/close your dropdown :
.ts :
import React, { Component } from "react";
import { render } from "react-dom";
import "./style.css";
const App = () => {
const toggleAccordion = e => {
Array.from(document.getElementsByClassName('accordion')).forEach(el => el.classList.add('hidden'));
e.target.classList.remove("hidden");
};
return (
<>
<div className="accordion hidden" onClick={toggleAccordion}>
accordion header
<div className="accordion-content-wrapper">
<div>Content</div>
<div>Content</div>
<div>Content</div>
</div>
</div>
<div className="accordion hidden" onClick={toggleAccordion}>
accordion header 2
<div className="accordion-content-wrapper">
<div>Content</div>
<div>Content</div>
<div>Content</div>
</div>
</div>
</>
);
};
render(<App />, document.getElementById("root"));
.css (not needed with your dropdown i think):
.accordion.hidden {
height: 18px;
overflow: hidden;
}
.accordion-content-wrapper{
margin-left: 10px;
border-left: 1px solid black;
}
Now, as i said it may not fit your needs since we don't have enough code to help you. As Rayan Wolton said, th way you handle the open/close aciton on your dropdown is important.
Hi all sorry for the poor English. I have very little experience using javaScript/ES6.
I am new to Vuejs I have a button in which if I click, it should start an animation of a bordered box from one div to another div which is fixed.
click button
<v-btn icon #click="doSomething()">
<v-icon>mdi-content-save-outline</v-icon>
</v-btn>
div which have to animate or can be used some animation from JS is fine as well.
<div id="divAnimation" style="border:1px solid #000000; width:50px; height:50px;"></div>
show when clicked and hide when reaced the fixed dive
Place where it should animate to.
<div id="animationReached" style="position:fixed;top:10%;left:0">I am Fixed</div>
You can achieve using jquery to add the classname when you click the button,
Here use the sample code,
Style
.mystyle
{padding: 25px; background-color: coral; color: white; font-size: 25px; box-sizing: border-box;}
Html Code
<button id="myDIV" onclick="myFunction()">Button Default</button>
Script
function myFunction() {
var element, name, arr;
element = document.getElementById("myDIV");
name = "mystyle";
arr = element.className.split(" ");
if (arr.indexOf(name) == -1) {
element.className += " " + name;
}
}
205/5000
I don't know if I understand your question well, but I think you want to toggle CSS settings on the click of a button, right? If so, look at these two examples changing by class and inline style (I think with Jquery it's not cool):
`
<template>
<div>
<button #click="changeStyle()">alternate 01 </button>
<div :style="styleDiv">
<span>
<h1>text 01</h1>
</span>
</div>
<div :class="styleDiv2">
<span>
<h1>text 02</h1>
</span>
</div>
</div>
</template>
<script>
export default {
data(){
return{
on: false,
styleOn:{
backgroundColor: 'green',
width: '100%',
height: '100%'
},
styleOff:{
backgroundColor: 'gray',
width: '50%',
height: '50%'
}
}
},
methods:{
changeStyle(){
this.on = !this.on
}
},
computed:{
styleDiv(){
return this.on ? this.styleOn : this.styleOff
},
styleDiv2(){
return this.on ? 'styleOn' : 'styleOff'
}
}
}
</script>
<style>
.styleOn{
background-color: green;
width: 100%;
height: 100%;
}
.styleOff{
background-color: gray;
width: 50%;
height: 50%;
}
</style>
`
I've just used Vue-dropzone to create a simple file uploader component. However, I would like to customise it by including an icon or a button called "Add more images" when there are already some images in the dropzone so that user can understand that they can upload multiple photos. How can I achieve this? Below are my code and a screenshot of what I want to achieve
Update I am still trying to implement this feature, any help will be greatly appreciated
Update2 I am still stuck with this, is anyone nice enough to guide me through?
<template>
<vue-dropzone
id="drop1"
:options="dropOptions"
:useCustomSlot="true"
#vdropzone-complete="afterComplete"
>
<div class="dropzone-custom-content">
<i class="fas fa-cloud-upload-alt fa-3x"></i>
<h4 class="dropzone-custom-title mb-0 mt-3">Drag & Drop</h4>
<div class="subtitle">or click to add your image</div>
</div>
</vue-dropzone>
</template>
import vueDropzone from "vue2-dropzone";
export default {
data() {
return {
dropOptions: {
url: "https://httpbin.org/post",
acceptedFiles: "image/*",
addRemoveLinks: true,
thumbnailWidth: 160, // px
thumbnailHeight: 160
}
};
},
components: {
vueDropzone
}
};
What I would like to achieve
It seems there is no official way to do this. But from this comment I modify his code to use with vue2-dropzone like below:
<template>
<div>
<vue-dropzone id='dropzone'
ref='myVueDropzone'
:options='dropzoneOptions'
#vdropzone-file-added='handleMoreThumbnail'
#vdropzone-removed-file='handleMoreThumbnail'>
</vue-dropzone>
<div class='more' ref='more'>+</div>
</div>
</template>
<script>
import vueDropzone from 'vue2-dropzone'
import 'vue2-dropzone/dist/vue2Dropzone.min.css'
export default {
components: {
vueDropzone
},
data () {
return {
dropzoneOptions: {
url: 'https://httpbin.org/post',
addRemoveLinks: true
}
}
},
mounted () {
this.$el.removeChild(this.$refs.more)
},
methods: {
handleMoreThumbnail () {
let dropzone = this.$refs.myVueDropzone.dropzone
dropzone.files.length > 0
? dropzone.element.appendChild(this.$refs.more)
: dropzone.element.removeChild(this.$refs.more)
}
}
}
</script>
<style>
.more {
display: inline-block;
margin: 16px;
border: 3px dashed lightgray;
width: 200px;
height: 200px;
box-sizing: border-box;
color: lightgray;
border-radius: 8px;
font-size: 60px;
text-align: center;
line-height: 200px;
pointer-events: none;
}
</style>