Vue3 Dropdown Click ul show - vuejs3

I want to do something like this in my project below, but every time I click it, it seems as if each element is selected.
What I want to do is simply,
When handleShow(depth) is clicked;
I want to print the clicked depth value in selected and show its ul, but it does not satisfy the condition I wrote.
v-show="selected === depth"
<template>
<li v-if="items.children" class="item dropdown">
<router-link
:link="items"
:key="depth"
#click.stop="handleShow(depth)"
/>
<ul class="dropdown-menu" v-show="selected === depth">
<MenuItems
v-for="(child, childIndex) in items.children"
:key="childIndex"
:items="child"
/>
</ul>
</li>
<li v-else class="item">
<router-link
:link="items"
:key="depth"
/>
</li>
</template>
<script>
import {ref, defineComponent} from "vue"
export default defineComponent({
name: 'MenuItems',
components: {MenuLink},
props: {
items: {type: Object, required: true},
depth: Number,
},
setup() {
let selected = ref(null);
const handleShow = (depth) => {
selected.value = depth;
}
return {
handleShow,
selected
}
},
})
</script>

Related

Image keeps disappearing after refreshing the page

So I'm doing a web app in react and decided to do a navbar, I included an image there, but every time I refresh the page the image disappears. It disappears refreshing through the code or through the refresh button on the web. I really dont understand why but also Im really new to this part of programming.
import React, { Component } from "react";
import { MenuItems } from "./MenuItems";
import { Button } from "./Button";
import "./Navbar.css";
import config from "../../config";
class Navbar extends Component {
state = { clicked: false };
handleClick = () => {
this.setState({ clicked: !this.state.clicked });
};
render() {
const backHome = () => {
window.location.replace(config.PORTAL_URL);
return;
};
return (
<nav className="NavBarItems">
<img
src="image.png"
width="15%"
height="auto"
onClick={() => backHome()}
className="image"
/>
<div className="menu-icon" onClick={this.handleClick}>
<i className={this.state.clicked ? "fas fa-times" : "fas fa-bars"} />
</div>
<ul className={this.state.clicked ? "nav-menu active" : "nav-menu"}>
{MenuItems.map((item, index) => {
return (
<li key={index}>
<a className={item.cName} href={item.url}>
{item.title}
</a>
</li>
);
})}
</ul>
<Button>Sign up</Button>
</nav>
);
}
}
export default Navbar;
and the MenuItems class its just
import config from '../../config'
export const MenuItems = [{
title: "Home",
url: config.PORTAL_URL,
cName: 'nav-links'
},
{
title: "Services",
url: "#",
cName: 'nav-links'
},
{
title: "Products",
url: '#',
cName: 'nav-links'
},
{
title: "Contact us",
url: '#',
cName: 'nav-links'
},
{
title: "Sign up",
url: '#',
cName: 'nav-links-mobile'
}
]
The image is stored in the public folder.
I manage to find a solution. What i did was move the image to other folder other than public, in this case i decided to put it in the same folder as the class using it. And then i did the following:
import logo from "./image.png"
and then in the place where there was the url I put "logo"
return (
<nav className="NavBarItems">
<img
src={logo}
width="15%"
height="auto"
onClick={() => backHome()}
className="image"
/>
<div className="menu-icon" onClick={this.handleClick}>
<i className={this.state.clicked ? "fas fa-times" : "fas fa-bars"} />
</div>
<ul className={this.state.clicked ? "nav-menu active" : "nav-menu"}>
{MenuItems.map((item, index) => {
return (
<li key={index}>
<a className={item.cName} href={item.url}>
{item.title}
</a>
</li>
);
})}
</ul>
<Button>Sign up</Button>
</nav>
);
Go to developer console and click on the img source to open in new tab, See if the image opens in a new tab.

OverFlow on form tags

So i am using bootstrap-vue, and precisely i am using the Form-tag as it's what i exactly needs. The issue is that the dropdown is as the long as the list of options.
Here's what i mean :
Tag button
& the
Dropdown
What i actually want is a similar css to overflow:scroll but i can't seem to make it work.
here's the code :
<template>
<div>
<b-form-group label="Tagged input using dropdown">
<b-form-tags v-model="value" no-outer-focus class="mb-2">
<template v-slot="{ tags, disabled, addTag, removeTag }">
<ul v-if="tags.length > 0" class="list-inline d-inline-block mb-2">
<li v-for="tag in tags" :key="tag" class="list-inline-item">
<b-form-tag
#remove="removeTag(tag)"
:title="tag"
:disabled="disabled"
variant="info"
>{{ tag }}</b-form-tag>
</li>
</ul>
<b-dropdown size="sm" variant="outline-secondary" block menu-class="w-100">
<template v-slot:button-content>
<b-icon icon="tag-fill"></b-icon> Choose tags
</template>
<b-dropdown-form #submit.stop.prevent="() => {}">
<b-form-group
label-for="tag-search-input"
label="Search tags"
label-cols-md="auto"
class="mb-0"
label-size="sm"
:description="searchDesc"
:disabled="disabled"
>
<b-form-input
v-model="search"
id="tag-search-input"
type="search"
size="sm"
autocomplete="off"
></b-form-input>
</b-form-group>
</b-dropdown-form>
<b-dropdown-divider></b-dropdown-divider>
<b-dropdown-item-button
v-for="option in availableOptions"
:key="option"
#click="onOptionClick({ option, addTag })"
>
{{ option }}
</b-dropdown-item-button>
<b-dropdown-text v-if="availableOptions.length === 0">
There are no tags available to select
</b-dropdown-text>
</b-dropdown>
</template>
</b-form-tags>
</b-form-group>
</div>
</template>
<script>
export default {
data() {
return {
options: ['Apple', 'Orange', 'Banana', 'Lime', 'Peach', 'Chocolate', 'Strawberry'],
search: '',
value: []
}
},
computed: {
criteria() {
// Compute the search criteria
return this.search.trim().toLowerCase()
},
availableOptions() {
const criteria = this.criteria
// Filter out already selected options
const options = this.options.filter(opt => this.value.indexOf(opt) === -1)
if (criteria) {
// Show only options that match criteria
return options.filter(opt => opt.toLowerCase().indexOf(criteria) > -1);
}
// Show all options available
return options
},
searchDesc() {
if (this.criteria && this.availableOptions.length === 0) {
return 'There are no tags matching your search criteria'
}
return ''
}
},
methods: {
onOptionClick({ option, addTag }) {
addTag(option)
this.search = ''
}
}
}
</script>
If you could please help me... Thank you
Well, i did it with adding a div with the following css
#test{
max-height:500px;
overflow:auto;
}
here's the code if anyone need it :
<template>
<div>
<b-form-group label="Tagged input using dropdown">
<b-form-tags v-model="value" no-outer-focus class="mb-2">
<template v-slot="{ tags, disabled, addTag, removeTag }">
<ul v-if="tags.length > 0" class="list-inline d-inline-block mb-2">
<li v-for="tag in tags" :key="tag" class="list-inline-item">
<b-form-tag
#remove="removeTag(tag)"
:title="tag"
:disabled="disabled"
variant="info"
>{{ tag }}</b-form-tag>
</li>
</ul>
<b-dropdown size="sm" variant="outline-secondary" block menu-class="w-100">
<template v-slot:button-content>
<b-icon icon="tag-fill"></b-icon> Choose tags
</template>
<div id="test">
<b-dropdown-form #submit.stop.prevent="() => {}">
<b-form-group
label-for="tag-search-input"
label="Search tags"
label-cols-md="auto"
class="mb-0"
label-size="sm"
:description="searchDesc"
:disabled="disabled"
>
<b-form-input
v-model="search"
id="tag-search-input"
type="search"
size="sm"
autocomplete="off"
></b-form-input>
</b-form-group>
</b-dropdown-form>
<b-dropdown-divider></b-dropdown-divider>
<b-dropdown-item-button
v-for="option in availableOptions"
:key="option"
#click="onOptionClick({ option, addTag })"
>
{{ option }}
</b-dropdown-item-button>
<b-dropdown-text v-if="availableOptions.length === 0">
There are no tags available to select
</b-dropdown-text>
</div>
</b-dropdown>
</template>
</b-form-tags>
</b-form-group>
</div>
</template>
<script>
export default {
data() {
return {
options: ['Apple', 'Orange', 'Banana', 'Lime', 'Peach', 'Chocolate', 'Strawberry'],
search: '',
value: []
}
},
computed: {
criteria() {
// Compute the search criteria
return this.search.trim().toLowerCase()
},
availableOptions() {
const criteria = this.criteria
// Filter out already selected options
const options = this.options.filter(opt => this.value.indexOf(opt) === -1)
if (criteria) {
// Show only options that match criteria
return options.filter(opt => opt.toLowerCase().indexOf(criteria) > -1);
}
// Show all options available
return options
},
searchDesc() {
if (this.criteria && this.availableOptions.length === 0) {
return 'There are no tags matching your search criteria'
}
return ''
}
},
methods: {
onOptionClick({ option, addTag }) {
addTag(option)
this.search = ''
}
}
}
</script>

Vue v-if not reading variable

I am using Vue 2 on Laravel 5.3 (using bxslider for carousel)
The following code gives me a single image (img1) slider.
If I remove v-ifs it does give me 3 image (with img1 img2 img3 in the slider).
Does productChoiceSelected.img2 returns true if it is not null from the database?
EDIT (added full code of the component)
<div class="col-md-4">
<ul class="bxslider">
<li>
<img :src="productChoiceSelected.img1">
</li>
<li v-if="productChoiceSelected.img2">
<img :src="productChoiceSelected.img2">
</li>
<li v-if="productChoiceSelected.img3">
<img :src="productChoiceSelected.img3">
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
data(){
return{
product:[],
productChoiceSelected:[],
productChoices:[]
}
},
props:[
'productId'
],
mounted(){
this.getProduct()
this.getAllProductChoices()
},
methods:{
getProduct(){
var vm = this;
vm.$http.get('/getProduct/'+vm.productId).then((response)=>{
vm.product = response.data.data.product;
vm.productChoiceSelected = response.data.data.productChoiceDefault;
});
},
getAllProductChoices(){
var vm = this;
vm.$http.get('/getAllProductChoices/'+vm.productId).then((response)=>{
vm.productChoices = response.data.data.productChoices;
});
}
}
}
</script>
EDIT #2
I guess it is because productChoiceSelected.img2 is a url directory? it is http://localhost:8000/img/products/1-2.bmp
I can figure out few issues in your code:
You need to have img1 and these variables defined as these are to be set reactively to be used in vue templated.
You need to move $('.bxslider').bxSlider({}) code in the updated section as this needs to be executed once you get the data updated from backend.
Following are code changes:
var demo = new Vue({
el: '#demo',
data: function(){
return {
productChoiceSelected: {
img1:'',
img2:'',
img3:''
}
};
},
mounted(){
this.getProduct()
},
updated(){
$('.bxslider').bxSlider({});
},
methods:{
getProduct(){
var vm = this;
setTimeout(function () {
vm.productChoiceSelected.img1 = 'http://bxslider.com/images/730_200/hill_trees.jpg'
// vm.productChoiceSelected.img2 = 'http://bxslider.com/images/730_200/houses.jpg'
vm.productChoiceSelected.img3 = 'http://bxslider.com/images/730_200/hill_fence.jpg'
}, 300)
}
}
})
see updated fiddle.
Thanks for saurabh contribution above, as I am still unsure where the problem is, here is how I solve the problem by myself for you guys' reference.
Change in code:
<template>
<div class="col-md-5">
<div class="col-md-12">
<ul class="bxslider">
<li>
<img :src="productChoiceSelected.img1">
</li>
<li v-if="img2">
<img :src="productChoiceSelected.img2">
</li>
<li v-if="img3">
<img :src="productChoiceSelected.img3">
</li>
</ul>
</div>
</div>
.....
.....
.....
methods:{
getProduct(){
var vm = this;
vm.$http.get('/getProduct/'+vm.productId).then((response)=>{
vm.product = response.data.data.product;
vm.productChoiceSelected = response.data.data.productChoiceDefault;
if(vm.productChoiceSelected.img1){
vm.img1 = true
}
if(vm.productChoiceSelected.img2 != null){
vm.img2 = true
}
if(vm.productChoiceSelected.img3 != null){
vm.img3 = true
}
});
},
......
......
......

Vue JS - Rendering Multidemensional Obj

I'm new to VUE, and having a hard time figuring out how to render an Object where i don't know how many levels deep it will go.
i get this object returned from a web-api
var result = {
"1":{
"id":"1",
"parent_id":"-1",
"name":"Master",
"level":1,
"children":{
"2":{
"id":"2",
"parent_id":"1",
"name":"Category A",
"level":2,
"children":{
"5":{
"id":"5",
"parent_id":"2",
"name":"Category D",
"level":3
},
"6":{
"id":"6",
"parent_id":"2",
"name":"Category E",
"level":3,
"children":{
"10":{
"id":"10",
"parent_id":"6",
"name":"Category F",
"level":4
}
}
}
}
},
"3":{
"id":"3",
"parent_id":"1",
"name":"Category B",
"level":2,
"children":{
"4":{
"id":"4",
"parent_id":"3",
"name":"Category C",
"level":3
}
}
}
}
}
};
I ignore the first level and push it in my store. Now i'm looking for a recursive way to go over all the parent's and theire children.
(this would be an example of the desired output)
<ul>
<li>Master</li>
<ul>
<li>Category A</li>
<ul>
<li>Category D</li>
<li>Category E</li>
<ul>
<li>Category F</li>
</ul>
</ul>
<li>Category B</li>
<ul>
<li>Category C</li>
</ul>
</ul>
</ul>
However i do not get further then:
<ul>
<li>Master</li>
<ul>
<li>Category A</li>
<li>Category B</li>
</ul>
</ul>
And offcourse i could go on like this and then i'll reach the end of my obj. but in real situation i don't know how deep it will go, that's why i want a solution that doesn't care about that and continiu's till there are no more children.
I saw a little something in the doc's but that code is so different from mine that i cant really understand how to apply ( https://v2.vuejs.org/v2/guide/components.html#Circular-References-Between-Components )
My init code is basically this is everything (simplified) together :
store = function() {
var self = this;
self.hierarchies = [];
self.subView = 'hierarchies';
self.tree = [];
};
Vue.component('hierarchies', {
template: '#hierarchies',
props: ['store']
});
Vue.component('products', {
template: '#products',
props: ['store']
});
Vue.component('treeview', {
template: '#treeview',
props: ['store']
});
app = new Vue({
el: '#content',
data: function () {
return {
store: {},
tree: {}
}
},
created: function () {
this.store = new store();
}
});
// DO AXJAX REQUEST GET HIERARCHY'S
var response = // AJAX {};
app.store.tree.push(response[1]);
<template id="treeview">
<div>
<ul v-if="store.tree.length">
<li v-for="m in store.tree">
{{ m.name }}
<ul v-if="m.children">
<li v-for="c in m.children">
{{ c.name }}
</li>
</ul>
<data :tree="m"></data>
</li>
</ul>
</div>
</template>
All idea's suggestions are welcome :)
Best,
Bastian
Your data is in a pretty unpractical format since you cannot loop over object keys using the v-for directive. So in order to consume that format, you have to transform it into an array first. I’m using a computed property for that, but a better solution for the long run might be to transform the data once in the beginning or even changing the result format of your web service (if you can).
var result = {"1":{"id":"1","parent_id":"-1","name":"Master","level":1,"children":{"2":{"id":"2","parent_id":"1","name":"Category A","level":2,"children":{"5":{"id":"5","parent_id":"2","name":"Category D","level":3},"6":{"id":"6","parent_id":"2","name":"Category E","level":3,"children":{"10":{"id":"10","parent_id":"6","name":"Category F","level":4}}}}},"3":{"id":"3","parent_id":"1","name":"Category B","level":2,"children":{"4":{"id":"4","parent_id":"3","name":"Category C","level":3}}}}}};
Vue.component('tree-root', {
props: ['items'],
template: `
<ul>
<tree-item v-for="item in items" v-bind:item="item"></tree-item>
</ul>
`
});
Vue.component('tree-item', {
props: ['item'],
computed: {
children: function () {
return Object.keys(this.item.children).map(k => this.item.children[k]);
}
},
template: `
<li>
{{item.name}}
<tree-root v-if="item.children" v-bind:items="children"></tree-root>
</li>
`
});
var app = new Vue({
el: '#app',
data: {
rootItem: result['1']
}
});
<ul id="app">
<tree-item v-bind:item="rootItem"></tree-item>
</ul>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
What I’m doing here is use two components: tree-root which is responsible for rendering a list of items, and tree-item which is responsible for rendering a single item, eventually recursively rendering another tree-root for the children of each item.
The computed children property is used to transform the children object into a list of children.
Finally, in order to render the root component, we just render the result['1'] which is the root element as a tree-item. We could also render a tree-root instead with the whole result but then we would have to convert it first into an array (just like in the computed property), so I just opted with the simpler solution here.

Display User Data with React/Meteor Application

I have been trying various methods to display data from a Meteor fetch within a React template. I don't fully understand why this is so hard to do.
When I use getMeteorData within the template it doesn't display the data when using {this.data.user._id}
getMeteorData() {
return {
user: Meteor.user()
};
},
This is the same story when using componentDidMount.
I have followed the react & meteor tutorial on the meteor website and they use props. This however feels a lot of work for simply pulling and fetching data.
This is an example of an attempt
export var MenuLoggedIn = React.createClass({
displayName: 'MenuLoggedIn',
mixins: [
Router.State, Router.Navigation
],
getMeteorData() {
return {
user: Meteor.user()
};
},
getInitialState(){
return {
avatarImagePreview: null,
avatarImage: null
};
},
render: function() {
return (
<div className="container">
<div className="menu-structure col-md-12">
{this.data.user._id}
<div className="left-menu pull-left">
<img className="logo" src="/img/logo.png"/>
<h1 className="eventburger-title">eventburger</h1>
</div>
<div className="right-menu">
<div className="search-bar">
<i className="fa fa-search"></i>
<input type="text" name="Search" placeholder="Harpist, Photographer, Caterer..."/>
</div>
{this.data
? <img src="/img/avatar.png"/>
: <img src="/img/placeholder-person.jpg"/>}
<span>{this.data}
Feeney</span>
<a href="/app/inbox">
<i className="fa fa-envelope"></i>
</a>
<a className="head-spacing" href="#" onClick={this.logout}>LOG OUT</a>
</div>
</div>
</div>
);
},
logout: function(e) {
e.preventDefault();
Meteor.logout();
window.location.href = "/login"; //Need to be moved to History
}
});
You need to use the ReactMeteorData mixin to make getMeteorData() work.
mixins: [
Router.State, Router.Navigation, ReactMeteorData
],
https://atmospherejs.com/meteor/react-meteor-data

Resources