VueJS: Fetching image path from an API and displaying dynamic data and pictures - api

I am basically trying to fetching image paths and other data from an API and render data and pictures dynamically. I tried to nest the banner and the sidebar as a components in the Home.vue file which all worked fine after taking the html, css, js from an html template. Which all works fine. But, when I after fetching the data from my API and populate the it in the DOM it does populate but is not rendered. Am I missing something here?
Home.vue
<template>
<div>
<div class="body-content outer-top-xs" id="top-banner-and-menu">
<div class="container">
<div class="row">
<template>
<SideBar />
</template>
<div class="col-xs-12 col-sm-12 col-md-9 homebanner-holder">
<Banner />
<Scroller
:newPcts=newProducts
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import SideBar from '#/components/SideBar.vue'
import Banner from '#/components/Banner.vue'
import Scroller from '#/components/Scroller.vue'
// require('#/assets/images/sliders/01.jpg'),
// require('#/assets/images/sliders/02.jpg')
export default {
name: 'home',
components: {
SideBar,
Banner,
Scroller
},
data() {
return {
newProducts: this.getNewProducts(),
bannerImages: null,
}
},
methods:{
getNewProducts(){
// get all products
axios.get("http://localhost:4000/products")
.then((response) => {
this.newProducts = response.data
})
},
}
}
</script>
Banner.vue
<template>
<div id="hero">
<div id="owl-main" class="owl-carousel owl-inner-nav owl-ui-sm">
<div
class="item"
v-for="(image, index) in bannerImgs"
:key="index"
style="background-image: url(assets/images/sliders/02.jpg);"
>
<div class="container-fluid">
<div class="caption bg-color vertical-center text-left">
<div class="slider-header fadeInDown-1">Spring 2016</div>
<div class="big-text fadeInDown-1">
Women
<span class="highlight">Fashion</span>
</div>
<div class="excerpt fadeInDown-2 hidden-xs">
<span>
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut
odit aut fugit
</span>
</div>
<div class="button-holder fadeInDown-3"></div>
</div>
<!-- /.caption -->
</div>
<!-- /.container-fluid -->
</div>
</div>
<!-- /.owl-carousel -->
</div>
</template>
<script>
//require('#/assets/images/sliders/01.jpg')
export default {
data() {
return {
publicPath: process.env.BASE_URL,
bgimg: require('#/assets/images/sliders/01.jpg'),
bannerImgs: null
}
},
methods:{
getImgUrl(imgUrl) {
return require('#/'+imgUrl)
},
getBannerImages(){
// get active banners
fetch("http://localhost:4000/banners", {
method: "get"
})
.then((response) => {
return response.json()
})
.then((jsonData) => {
console.log(jsonData[0])
this.bannerImgs = jsonData[0]
// return jsonData[0]
})
}
},
created(){
// console.log("mounted ", this.publicPath)
// console.log(this.bannerImgs)
this.getBannerImages()
}
}
</script>
Scroller.vue
<template>
<div id="product-tabs-slider" class="scroll-tabs outer-top-vs wow fadeInUp">
<div class="more-info-tab clearfix">
<h3 class="new-product-title pull-left">New Products</h3>
<ul class="nav nav-tabs nav-tab-line pull-right" id="new-products-1">
<!-- <li class="active"> -->
<!-- <a data-transition-type="backSlide" href="#all" data-toggle="tab">All</a> -->
<!-- </li> -->
<!-- <li> -->
<!-- <a data-transition-type="backSlide" href="#smartphone" data-toggle="tab">Clothing</a> -->
<!-- </li> -->
</ul>
<!-- /.nav-tabs -->
</div>
<div class="tab-content outer-top-xs">
<!-- Tabs -->
<div class="tab-pane in active" id="all">
<div class="product-slider">
<div class="owl-carousel home-owl-carousel custom-carousel owl-theme" data-item="4">
<div
class="item item-carousel"
v-for="(item, index) in newPcts"
:key="index"
>
{{item.title}}
{{item.price}}
<!-- /.products -->
</div>
<!-- /.item -->
</div>
<!-- /.home-owl-carousel -->
</div>
<!-- /.product-slider -->
</div>
<!-- END TAB -->
</div>
<!-- /.tab-content -->
</div>
</template>
<script>
import axios from 'axios';
export default {
props:{
newPcts: Array
},
data(){
return {
// newProducts: []
}
},
methods:{
getProductImages(id){
let imgurl = "http://localhost:4000/mainpicture/"+id
axios.get(imgurl)
.then((response) => {
console.log(response.data)
return require('#/' + response.data)
})
}
},
computed:{
newProducts(){
axios.get("http://localhost:4000/products")
.then((response) => {
// this.newProducts = response.data
// console.log(response.data[0])
return response.data
})
}
},
async created() {
console.log('Scroller mounted')
console.log(this.newPcts)
},
mounted() {
console.log('Scroller mounted')
console.log(this.newPcts)
}
}
</script>

Related

How can I use a sidenavbar toggle function in another header component in Vue3

Im using Vue3 in Laravel 9 with Inertia.js and I´m trying to create a Sidenavbar with a headerbar.
I would like to toggle the Sidenavbar with a "Menu" Button in the Header Component.
But I have no idea how can i use the toggle function for my Sidenavbar in my headerbar.
The Toggle function in the Sidenavbar is working fine.
Screenshot with Header and Sidebar
Layout/App.vue
<template>
<Header />
<div class="app">
<Nav />
<slot />
</div>
</template>
<script>
import Nav from "./Nav";
import Header from "./header.vue";
export default {
components: { Nav, Header },
};
</script>
Sidenavbar Nav.vue
<template>
<aside :class="`${is_expanded ? 'is-expanded' : ''}`">
<h3>Menu</h3>
<div class="menu">
<router-link to="/" class="button">
<span class="material-symbols-rounded">home</span>
<span class="text">Home</span>
</router-link>
<router-link to="/about" class="button">
<span class="material-symbols-rounded">description</span>
<span class="text">About</span>
</router-link>
<router-link to="/team" class="button">
<span class="material-symbols-rounded">group</span>
<span class="text">Team</span>
</router-link>
<router-link to="/contact" class="button">
<span class="material-symbols-outlined">admin_panel_settings</span>
<span class="text">Administration</span>
</router-link>
</div>
<div class="flex"></div>
<div class="menu">
<router-link to="/settings" class="button ">
<span class="material-symbols-rounded">settings</span>
<span class="text">Settings</span>
</router-link>
</div>
<div class="menu-toggle-wrap">
<button class="menu-toggle" #click="ToggleMenu">
<span class="material-symbols-outlined menu-icon">menu</span>
<span class="material-symbols-outlined arrow-back">arrow_back</span>
</button>
</div>
</aside>
</template>
<script >
import {ref} from 'vue'
export default {
data() {
return {
is_expanded: ref(localStorage.getItem("is_expanded") === "true"),
visible: false
};
},
methods: {
ToggleMenu() {
this.is_expanded = !this.is_expanded;
localStorage.setItem("is_expanded", this.is_expanded);
}
},
mounted() {
console.log(`The initial count is ${this.is_expanded}.`);
}
}
</script>
Header Header.vue
<template>
<div class=" header border-2">
<div class="menu-toggle-wrap">
<button class="menu-toggle" #click="">
<span class="material-symbols-outlined menu-icon">menu</span>
</button>
</div>
</div>
</template>
<script>
export default {
}
</script>
Here is my app.js file
import { createApp, h } from 'vue'
import { createInertiaApp } from '#inertiajs/inertia-vue3'
export const toggleMenu = new Vue();
createInertiaApp({
resolve: name => require(`./pages/${name}`),
setup({ el, App, props, plugin }) {
createApp({ render: () => h(App, props) })
.use(plugin)
.mount(el)
},
})

Vue.js: How to change booleanfield in specific object from false to true #click in state with mutations and server with actions axios?

I want to change the boolean from false to true from one object(todo), when I do #click="updateTodoItem(todo.id, todo.title, todo.body)
thats how 1 object looks: {id: 1, title: "", body: "", done: false}
UPDATE (finished):
I figured it out and updated code here. I can only pass done one argument as payload to the store actions.
Home.vue
<template>
<div class="container">
<section class="hero is-link">
<div class="hero-body">
<div class="container">
<h1 class="title">
Hello Dudes
</h1>
<h2 class="subtitle">
Hamster ToDo List
</h2>
</div>
</div>
</section>
<section class="section">
<nav class="level">
<div class="level-item has-text-centered">
<div>
<p class="heading">All Tasks</p>
<p class="title">{{getTotalTodos}}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Tasks has to be done</p>
<p class="title">{{getTodosFalse}}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Tasks finished</p>
<p class="title">{{getTodosTrue}}</p>
</div>
</div>
<!-- <div class="level-item has-text-centered">
<div>
<p class="heading">Likes</p>
<p class="title">789</p>
</div>
</div> -->
</nav>
</section>
<section class="section">
<div class="columns">
<div class="column is-half">
<strong>Add ToDo</strong>
<div class="control">
<input class="input is-info" v-model="todoTitle" type="text" placeholder="Add Task Title">
<input class="input is-info" v-model="todoBody" type="text" placeholder="Add Task Body">
<button class="button is-link" #click="addTodoItem({title: todoTitle, body: todoBody, done: false}); empyInput()">Add</button>
</div>
</div>
</div>
<div class="tabs is-small">
<ul>
<li class="is-active"><a>All Tasks </a></li> <span class="tag is-link">{{getTotalTodos}}</span>
<li><a>Tasks still not done</a></li><span class="tag is-link">{{getTodosFalse}}</span>
<li><a>Finished Tasks</a></li><span class="tag is-link">{{getTodosTrue}}</span>
<!-- <li><a>Documents</a></li> -->
</ul>
</div>
<article class="media" v-for="todo in todoItems" :key="todo.id">
<figure class="media-left">
<!-- <p class="image is-64x64">
<img src="https://bulma.io/images/placeholders/128x128.png">
</p> -->
</figure>
<div class="media-content">
<div class="content">
<p>
<strong>{{todo.title}}</strong>
<br>
{{todo.body}}
</p>
</div>
<nav class="level is-mobile">
<div class="level-left">
<a class="level-item">
<span class="icon is-small"><i class="fas fa-edit"></i></span>
</a>
<a class="level-item" v-if="todo.done==false" #click="updateTodoToTrue(todo.id)">
<span class="icon is-small"><i class="fas fa-check"></i></span>
</a>
<a class="level-item" v-if="todo.done==true" #click="updateTodoToFalse(todo.id)">
<span class="icon is-small"><i class="fas fa-undo"></i></span>
</a>
<a class="level-item" #click="deleteTodoItem(todo.id)">
<span class="icon is-small"><i class="fas fa-trash"></i></span>
</a>
</div>
</nav>
</div>
<div class="media-right">
<button class="delete" #click="deleteTodoItem(todo.id)"></button>
</div>
</article>
</section>
</div>
</template>
<script>
// # is an alias to /src
import {mapGetters} from 'vuex';
import {mapActions} from 'vuex';
export default {
name: 'Home',
data() {
return{
todoTitle: '',
todoBody: '',
}
},
// updated() {
// this.$store.dispatch('getTodoItems');
// },
computed: {
...mapGetters(['todoItems', 'getTotalTodos', 'getTodosTrue', 'getTodosFalse'])
},
methods: {
...mapActions(['addTodoItem', 'deleteTodoItem', 'updateTodoToTrue', 'updateTodoToFalse']),
// addTodo() {
// return this.$store.actions.addTodoItem;
empyInput() {
this.todoTitle = '';
this.todoBody = '';
}
}
}
</script>
store
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios';
Vue.use(Vuex)
export default new Vuex.Store({
state: {
todoItems: []
},
// Keep in mind that response is an HTTP response
// returned by the Promise.
// The mutations are in charge of updating the client state.
mutations: {
UPDATE_TODO_ITEMS (state, payload) {
state.todoItems = payload;
},
ADD_TODO_ITEMS (state, payload) {
state.todoItems.push(payload)
},
DELETE_TODO_ITEMS (state, payload) {
state.todoItems.splice(payload, 1)
},
changeTodoStatus: (state, payload) => {
let index = state.todoItems.findIndex(el => {
return el.id == payload.id
})
state.todoItems[index].done = payload.done
}
},
actions: {
getTodoItems ({ commit }) {
axios.get('http://127.0.0.1:8000/api/').then((response) => {
commit('UPDATE_TODO_ITEMS', response.data)
});
},
addTodoItem ({ commit }, todoItem) {
axios.post('http://127.0.0.1:8000/api/', todoItem).then((response) => {
commit('ADD_TODO_ITEMS', response.data)
});
},
deleteTodoItem ({ commit }, todoItemId) {
axios.delete('http://127.0.0.1:8000/api/' + todoItemId + '/').then((response) => {
commit('DELETE_TODO_ITEMS', response.data)
});
},
updateTodoToTrue ({ commit }, todoItemId) {
var payload = {
done: true
};
axios.patch('http://127.0.0.1:8000/api/' + todoItemId + '/', payload).then((response) => {
commit('changeTodoStatus', response.data)
});
},
updateTodoToFalse ({ commit }, todoItemId) {
var payload = {
done: false
};
axios.patch('http://127.0.0.1:8000/api/' + todoItemId + '/', payload).then((response) => {
commit('changeTodoStatus', response.data)
});
},
},
getters: {
todoItems: state => state.todoItems,
getTotalTodos: state => state.todoItems.length,
getTodosTrue: state => state.todoItems.filter(todo => todo.done).length,
getTodosFalse: state => state.todoItems.filter(todo => !todo.done).length
},
modules: {
}
})
My problem was that I tried to pass down 3 arguments to the actions payload in the store. The action accepts accepts only 1 argument which is the payload. I changed the axios method from PUT to PATCH.
Home.vue
<template>
<div class="container">
<section class="hero is-link">
<div class="hero-body">
<div class="container">
<h1 class="title">
Hello Dudes
</h1>
<h2 class="subtitle">
Hamster ToDo List
</h2>
</div>
</div>
</section>
<section class="section">
<nav class="level">
<div class="level-item has-text-centered">
<div>
<p class="heading">All Tasks</p>
<p class="title">{{getTotalTodos}}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Tasks has to be done</p>
<p class="title">{{getTodosFalse}}</p>
</div>
</div>
<div class="level-item has-text-centered">
<div>
<p class="heading">Tasks finished</p>
<p class="title">{{getTodosTrue}}</p>
</div>
</div>
<!-- <div class="level-item has-text-centered">
<div>
<p class="heading">Likes</p>
<p class="title">789</p>
</div>
</div> -->
</nav>
</section>
<section class="section">
<div class="columns">
<div class="column is-half">
<strong>Add ToDo</strong>
<div class="control">
<input class="input is-info" v-model="todoTitle" type="text" placeholder="Add Task Title">
<input class="input is-info" v-model="todoBody" type="text" placeholder="Add Task Body">
<button class="button is-link" #click="addTodoItem({title: todoTitle, body: todoBody, done: false}); empyInput()">Add</button>
</div>
</div>
</div>
<div class="tabs is-small">
<ul>
<li class="is-active"><a>All Tasks </a></li> <span class="tag is-link">{{getTotalTodos}}</span>
<li><a>Tasks still not done</a></li><span class="tag is-link">{{getTodosFalse}}</span>
<li><a>Finished Tasks</a></li><span class="tag is-link">{{getTodosTrue}}</span>
<!-- <li><a>Documents</a></li> -->
</ul>
</div>
<article class="media" v-for="todo in todoItems" :key="todo.id">
<figure class="media-left">
<!-- <p class="image is-64x64">
<img src="https://bulma.io/images/placeholders/128x128.png">
</p> -->
</figure>
<div class="media-content">
<div class="content">
<p>
<strong>{{todo.title}}</strong>
<br>
{{todo.body}}
</p>
</div>
<nav class="level is-mobile">
<div class="level-left">
<a class="level-item">
<span class="icon is-small"><i class="fas fa-edit"></i></span>
</a>
<a class="level-item" v-if="todo.done==false" #click="updateTodoToTrue(todo.id)">
<span class="icon is-small"><i class="fas fa-check"></i></span>
</a>
<a class="level-item" v-if="todo.done==true" #click="updateTodoToFalse(todo.id)">
<span class="icon is-small"><i class="fas fa-undo"></i></span>
</a>
<a class="level-item" #click="deleteTodoItem(todo.id)">
<span class="icon is-small"><i class="fas fa-trash"></i></span>
</a>
</div>
</nav>
</div>
<div class="media-right">
<button class="delete" #click="deleteTodoItem(todo.id)"></button>
</div>
</article>
</section>
</div>
</template>
<script>
// # is an alias to /src
import {mapGetters} from 'vuex';
import {mapActions} from 'vuex';
export default {
name: 'Home',
data() {
return{
todoTitle: '',
todoBody: '',
}
},
// updated() {
// this.$store.dispatch('getTodoItems');
// },
computed: {
...mapGetters(['todoItems', 'getTotalTodos', 'getTodosTrue', 'getTodosFalse'])
},
methods: {
...mapActions(['addTodoItem', 'deleteTodoItem', 'updateTodoToTrue', 'updateTodoToFalse']),
// addTodo() {
// return this.$store.actions.addTodoItem;
empyInput() {
this.todoTitle = '';
this.todoBody = '';
}
}
}
</script>
STORE
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios';
Vue.use(Vuex)
export default new Vuex.Store({
state: {
todoItems: []
},
// Keep in mind that response is an HTTP response
// returned by the Promise.
// The mutations are in charge of updating the client state.
mutations: {
UPDATE_TODO_ITEMS (state, payload) {
state.todoItems = payload;
},
ADD_TODO_ITEMS (state, payload) {
state.todoItems.push(payload)
},
DELETE_TODO_ITEMS (state, payload) {
state.todoItems.splice(payload, 1)
},
changeTodoStatus: (state, payload) => {
let index = state.todoItems.findIndex(el => {
return el.id == payload.id
})
state.todoItems[index].done = payload.done
}
},
actions: {
getTodoItems ({ commit }) {
axios.get('http://127.0.0.1:8000/api/').then((response) => {
commit('UPDATE_TODO_ITEMS', response.data)
});
},
addTodoItem ({ commit }, todoItem) {
axios.post('http://127.0.0.1:8000/api/', todoItem).then((response) => {
commit('ADD_TODO_ITEMS', response.data)
});
},
deleteTodoItem ({ commit }, todoItemId) {
axios.delete('http://127.0.0.1:8000/api/' + todoItemId + '/').then((response) => {
commit('DELETE_TODO_ITEMS', response.data)
});
},
updateTodoToTrue ({ commit }, todoItemId) {
var payload = {
done: true
};
axios.patch('http://127.0.0.1:8000/api/' + todoItemId + '/', payload).then((response) => {
commit('changeTodoStatus', response.data)
});
},
updateTodoToFalse ({ commit }, todoItemId) {
var payload = {
done: false
};
axios.patch('http://127.0.0.1:8000/api/' + todoItemId + '/', payload).then((response) => {
commit('changeTodoStatus', response.data)
});
},
},
getters: {
todoItems: state => state.todoItems,
getTotalTodos: state => state.todoItems.length,
getTodosTrue: state => state.todoItems.filter(todo => todo.done).length,
getTodosFalse: state => state.todoItems.filter(todo => !todo.done).length
},
modules: {
}
})

How do I make the hoverable dropdown close when navigating to a differerent link in Bulma?

HERE is a SANDBOX with the issue on it
I have a hoverable dropdown inside a Navbar
When I move to a different page, the dropdown is still open
I have tried this in plain Bulma, the issue still remains
I am on Nuxt.js using SSR
I am using nuxt-link / equivalent of Vue router-link to navigate to a different page.
Here is the my default.vue file
<template>
<div class="ch-container">
<header class="ch-header">
<nav
class="navbar is-fixed-top"
role="navigation"
aria-label="main navigation"
>
<div class="navbar-brand">
<nuxt-link class="navbar-item" to="/">
<img
alt="CH Logo"
src="https://i.imgur.com/v35Kfc9.png"
width="28"
height="28"
/>
</nuxt-link>
<a
role="button"
class="navbar-burger burger"
aria-label="menu"
aria-expanded="false"
data-target="navbarBasicExample"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<nuxt-link class="navbar-item" to="/news">
News
</nuxt-link>
<nuxt-link class="navbar-item" to="/resources">
Resources
</nuxt-link>
<nuxt-link class="navbar-item" to="/tickers">
Tickers
</nuxt-link>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">
More
</a>
<div class="navbar-dropdown">
<a class="navbar-item">
FAQ
</a>
<nuxt-link class="navbar-item" to="/contact">
Contact
</nuxt-link>
<hr class="navbar-divider" />
<a class="navbar-item">
Feature Request
</a>
</div>
</div>
</div>
<div class="navbar-end">
<div class="navbar-item">
<a href="#">
<fa :icon="faMoon" />
</a>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link is-arrowless">
<fa :icon="faExclamationCircle" />
</a>
<div class="navbar-dropdown">
<a class="navbar-item">
No new notifications
</a>
</div>
</div>
<div class="navbar-item">
<div class="buttons">
<nuxt-link class="button is-primary" to="/signup">
<strong>Sign up</strong>
</nuxt-link>
<nuxt-link id="login" class="button is-light" to="/login">
Log in
</nuxt-link>
</div>
</div>
</div>
</div>
</nav>
</header>
<main class="ch-main">
<nuxt />
</main>
<footer class="ch-footer is-hidden-mobile">
<div class="level">
<div class="level-left">
<div class="level-item">
<a href="#">
<span class="icon">
<fa :icon="faFacebookSquare" />
</span>
</a>
<a href="#">
<span class="icon">
<fa :icon="faTwitterSquare" />
</span>
</a>
<a href="#">
<span class="icon">
<fa :icon="faRedditSquare" />
</span>
</a>
</div>
</div>
<div class="level-right">
<div class="level-item">
©ch, All Rights Reserved
</div>
<div class="level-item">
<nuxt-link to="/contact">Contact</nuxt-link>
</div>
<div class="level-item">
<nuxt-link to="/terms-of-service">Terms</nuxt-link>
</div>
<div class="level-item">
<nuxt-link to="/privacy-policy">Privacy</nuxt-link>
</div>
</div>
</div>
</footer>
</div>
</template>
<script>
import {
faFacebookSquare,
faTwitterSquare,
faRedditSquare,
} from '#fortawesome/free-brands-svg-icons'
import { faMoon, faExclamationCircle } from '#fortawesome/free-solid-svg-icons'
export default {
computed: {
faFacebookSquare() {
return faFacebookSquare
},
faTwitterSquare() {
return faTwitterSquare
},
faRedditSquare() {
return faRedditSquare
},
faMoon() {
return faMoon
},
faExclamationCircle() {
return faExclamationCircle
},
},
mounted() {
// Get all "navbar-burger" elements
const $navbarBurgers = Array.prototype.slice.call(
document.querySelectorAll('.navbar-burger'),
0
)
// Check if there are any navbar burgers
if ($navbarBurgers.length > 0) {
// Add a click event on each of them
$navbarBurgers.forEach((el) => {
el.addEventListener('click', () => {
// Get the target from the "data-target" attribute
const target = el.dataset.target
const $target = document.getElementById(target)
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
el.classList.toggle('is-active')
$target.classList.toggle('is-active')
})
})
}
},
}
</script>
<style></style>
Here is a GIF illustrating the problem
How to close the dropdown after you move to a different page?
Ok, the whole thing makes it complicated, because the hover is triggered by css and therefore the dropdown can always be seen when the mouse is over it. You have to overwrite this state and solve it with vue events. We also have to put a watcher on the route to reset the state.
CodeSandbox - Example
<template>
<div class="container">
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<!-- <a class="navbar-item" href="https://bulma.io"> -->
<img src="https://bulma.io/images/bulma-logo.png" width="112" height="28">
<a
role="button"
class="navbar-burger burger"
aria-label="menu"
aria-expanded="false"
data-target="navbarBasicExample"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item">Home</a>
<a class="navbar-item">Documentation</a>
<div
#mouseover="toggleDropdown(true)"
#mouseleave="toggleDropdown(false)"
class="navbar-item has-dropdown is-hoverable"
>
<a class="navbar-link">More</a>
<div class="navbar-dropdown" :style="{display: showDropdown ? 'block' : 'none' }">
<nuxt-link class="navbar-item" to="/about">About</nuxt-link>
<nuxt-link class="navbar-item" to="/jobs">Jobs</nuxt-link>
</div>
</div>
</div>
<div class="navbar-end">
<div class="navbar-item">
<div class="buttons">
<a class="button is-primary">
<strong>Sign up</strong>
</a>
<a class="button is-light">Log in</a>
</div>
</div>
</div>
</div>
</nav>
<Nuxt/>
</div>
</template>
<script>
export default {
data() {
return {
routeChange: false,
showDropdown: false
};
},
watch: {
$route() {
this.routeChange = true;
this.showDropdown = false;
}
},
methods: {
toggleDropdown(payload) {
if (this.showDropdown !== payload) this.routeChange = false;
if (!this.routeChange) {
this.showDropdown = payload;
}
}
}
};
</script>
This worked form me Bootstrap 5, in touch devices it uses click, on devices with mouse it uses hover
<template>
<span
v-if="item"
class="primary-navigation-list-dropdown"
#mouseover="isTouchscreenDevice ? null : openDropdownMenu()"
#mouseleave="isTouchscreenDevice ? null : closeDropdownMenu()"
>
<nuxt-link
to="#"
#click.prevent.native="openDropdownMenu"
v-click-outside="closeDropdownMenu"
:title="item.title"
:class="[
item.cssClasses,
{ show: isDropdownMenuVisible }
]"
:id="`navbarDropdownMenuLink-${item.id}`"
:aria-expanded="[isDropdownMenuVisible ? true : false]"
class="
primary-navigation-list-dropdown__toggle
nav-link
dropdown-toggle"
aria-current="page"
role="button"
data-toggle="dropdown"
>
{{ item.label }}
</nuxt-link>
<ul
:class="{ show: isDropdownMenuVisible }"
:aria-labelledby="`navbarDropdownMenuLink-${item.id}`"
class="
primary-navigation-list-dropdown__menu
dropdown-menu-list
dropdown-menu"
>
<li
v-for="item in item.children" :key="item.id"
class="dropdown-menu-list__item"
>
<NavLink
:attributes="item"
class="dropdown-menu-list__link dropdown-item"
/>
</li>
</ul>
</span>
</template>
<script>
import NavLink from '#/components/Navigation/NavLink';
export default {
name: "DropdownMenu",
props: {
item: {
type: Object,
required: true,
},
},
data() {
return {
isDropdownMenuVisible: false,
isTouchscreenDevice: false
};
},
mounted() {
this.detectTouchscreenDevice();
},
methods: {
openDropdownMenu() {
if (this.isTouchscreenDevice) {
this.isDropdownMenuVisible = !this.isDropdownMenuVisible;
} else {
this.isDropdownMenuVisible = true;
}
},
closeDropdownMenu() {
if (this.isTouchscreenDevice) {
this.isDropdownMenuVisible = false;
} else {
this.isDropdownMenuVisible = false;
}
},
detectTouchscreenDevice() {
if (window.PointerEvent && ('maxTouchPoints' in navigator)) {
if (navigator.maxTouchPoints > 0) {
this.isTouchscreenDevice = true;
}
} else {
if (window.matchMedia && window.matchMedia("(any-pointer:coarse)").matches) {
this.isTouchscreenDevice = true;
} else if (window.TouchEvent || ('ontouchstart' in window)) {
this.isTouchscreenDevice = true;
}
}
return this.isTouchscreenDevice;
}
},
components: {
NavLink
}
};
</script>
<style scoped lang="scss">
.primary-navigation-list-dropdown {
&__toggle {
color: $white;
&:hover {
color: $blue;
}
}
&__menu {
margin-top: 0;
}
&__dropdown {
}
}
.dropdown-menu-list {
&__item {
}
&__link {
&.active,
&.nuxt-link-exact-active {
border-bottom: 1px solid $blue;
}
}
}
</style>
NavLink.vue
<template>
<component
:is="attributes"
v-bind="linkAttributes(attributes.path)"
:title="attributes.title"
:class="[ attributes.cssClasses ]"
class="nav-link active_"
aria-current="page"
prefetch
>
{{ attributes.label }}
</component>
</template>
<script>
export default {
name: 'NavLink',
props: {
attributes: {
type: Object,
required: true
}
},
methods: {
linkAttributes(path) {
if (path.match(/^(http(s)?|ftp):\/\//) || path.target === '_blank') {
return {
is: 'a',
href: path,
target: '_blank',
rel: 'noopener'
}
}
return {
is: 'nuxt-link',
to: path
}
}
}
}
</script>
click-outside.js
import Vue from 'vue'
Vue.directive('click-outside', {
bind: function (el, binding, vnode) {
el.clickOutsideEvent = function (event) {
if (!(el == event.target || el.contains(event.target))) {
vnode.context[binding.expression](event);
}
};
document.body.addEventListener('click', el.clickOutsideEvent)
},
unbind: function (el) {
document.body.removeEventListener('click', el.clickOutsideEvent)
},
});
I’m not using Vue, but using Meteor (where similar route-calling trips up the Bulma drop-down removal).
Slightly hacky way to fix it, but the dropdown is made visible because the .has-dropdown element has the .is-hoverable class. So to fix, on any click on the dropdown’s items, I run this:
// Remove the hover effect = dropdown disappears
$(".has-dropdown").removeClass("is-hoverable");
// Tiny time later, put it back, so hover-drop works anew
setTimeout(function() {
$(".has-dropdown").addClass("is-hoverable");
}, 100);
Doesn’t really matter that it would hit all the dropdowns if you have more than one, because resetting them all is harmless when routing to a new page. But if you’re fussy you could target just the closest dropdown.
As Bulma’s mobile view doesn’t activate an on-hover effect anyway, this doesn’t break in the mobile “burger menu” version of my navbar.
Works OK for my project on Chrome, Safari, Firefox.

Vue: Data 'undefind' when component render

I would like to set a class to one of my elements when current language I equal to element id. But when method is fired to do this check then I have log "undefined".
Why? How to set that class properly?
Is data object loaded latter then component render?
<template>
<div>
<div class="star-box">
<div class="head">
<img :src="'images/flags/en.png'"
id="en"
class="student-img"
v-bind:class="{'activeLanguage': checkActiveLanguage('en')}"
alt=""
>
</div>
<div class="body">
<h5 class="heading">English</h5>
</div>
</div>
<div class="star-box">
<div class="head">
<img :src="'images/flags/de.png'"
id="de"
class="student-img"
v-bind:class="{'activeLanguage': checkActiveLanguage('de')}"
alt=""
>
</div>
<div class="body">
<h5 class="heading">Deutsch</h5>
</div>
</div>
</div>
</template>
JS
<script>
import {mapActions,mapGetters} from 'vuex';
export default {
name: 'Language',
data() {
return {
language: ''
}
},
methods: {
checkActiveLanguage: (lang)=> {
console.log(this.language);
if(lang==this.language) return true;
},
...mapGetters(['getCurrentLanguage']),
},
beforeMount(){
this.language = this.getCurrentLanguage();
}
}
</script>

render vuejs bindes in v-html from string

i'm generete this html for a chatbubble depending on a api call response. the html gets added to a local var. it shows by using <div v-html="messages">/<div> in the components template.
this only works when i call this.$forceUpdate(); after the html is added.
problem: there is a button
'<a v-on:click="askdialogflow" >'+classes["first"]+'</a>'
this works and renders when it's added directly to the template. when it renders from a string, it does not convert it.
problem: v-html does not render/compile the button
hardcoded call:
this.Questionclasses({first: "10010300", second: "02170705", third: "03070403", fourth: "18110000"});
Code:
<template>
<div class='chat-wrapper' id="chat-wrapper">
<div v-html = "messages" ></div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'App',
data: {
messages:"",
imageData: "" // we will store base64 format of image in this string
},
methods: {
checkImage() {
this.imageData =localStorage["image"]
axios.post('http://localhost:5000/api/start',{img:localStorage["image"].split(",")[1]})
.then(response =>{
this.Questionclasses(JSON.parse(response.data));
})
.catch(e => {
this.errors.push(e)
})
},
startBlock(){
let html = ` <div class='chat-message chat-message-sender'>
<img class='chat-image chat-image-default' src='./../../static/user.jpg' />
<div class='chat-message-wrapper'>
<div class='chat-message-content'>
<img class="startImage" src="` + this.imageData +`">
<p>Analyseer deze foto alstublieft.</p>
</div>
<div class='chat-details'>
<span class='chat-message-localisation font-size-small'>Time</span>
</div>
</div>
</div>`
this.messages = html;
},
Questionclasses(classes){
let html =` <div class='chat-message padding'>
<div class='chat-message chat-message-recipient'>
<img class='chat-image chat-image-default' src='./../../static/tvh_robot-pro.png' />
<div class='chat-message-wrapper'>
<div class='chat-message-content'>
<div class="classImage-wrapper">
<div>
<p>
<img src="./../../static/kip.jpg" class="classImage">
</p>
<p> <a v-on:click="askdialogflow" >`+classes["first"]+`</a> </p>
</div>
<div>
<p>
<img src="./../../static/kip.jpg" class="classImage">
</p>
<p><a>`+classes["second"]+`</a></p>
</div>
<div>
<p>
<img src="./../../static/kip.jpg" class="classImage">
</p>
<p><a>`+classes["third"]+`</a> </p>
</div>
<div>
<p>
<img src="./../../static/kip.jpg" class="classImage">
</p>
<p><a>`+classes["fourth"]+`</a> </p>
</div>
</div>
<p>geen van bovenstaande</p>
</div>
<div class='chat-details'>
<span class='chat-message-localization font-size-small'>Time</span>
</div>
</div>
</div>
</div>
`
console.log(html)
this.messages += html;
this.$forceUpdate();
},
askdialogflow(event){
console.log(event)
}
},
beforeMount(){
this.checkImage();
this.startBlock();
},
}
</script>