Hi
I try to use "kt.menu.dropdown.show" to catch when the user dropdown menu is open.
But on vue3 and Metronic 8.0.38 I get this javascript error:
Uncaught (in promise) TypeError: _layout_header_Topbar_vue__WEBPACK_IMPORTED_MODULE_1__.default?.reinitialization is not a function
onMounted(() => {
nextTick(() => {
const toolbarElement = document.getElementById(
"kt_header_user_menu_toggle"
) as HTMLInputElement | null;
console.log(toolbarElement);
KTTopbar?.reinitialization();
KTTopbar?.createInstance(toolbarElement);
const toolbar = KTTopbar?.getInstance(toolbarElement);
console.log(toolbar);
toolbar?.on("kt.menu.dropdown.show", function () {
console.log("open");
return false;
});
});
});
Hi
I added the MenuComponent component and changed the code like this:
onMounted(() => {
nextTick(() => {
const toolbarElement = document.getElementById(
"user_menu"
) as HTMLInputElement;
console.log(toolbarElement);
const toolbar = MenuComponent?.getInstance(toolbarElement);
console.log(toolbar);
toolbar?.on("kt.menu.dropdown.show", function () {
console.log("open");
return false;
});
});
});
const toolbar = MenuComponent?.getInstance(toolbarElement);
MenuComponent?.reinitialization();
MenuComponent?.createInsance("user_menu");
Hi,null
is returned because MenuComponent instance wasn't created.reinitialication
and createInsance
functions are doing the same thing, they create MenuComponent instances for your elements, so instead of calling both function you can just call createInsance.
Regards,
Lauris Stepanovs,
Keenthemes Support Team
Hi
If I use only:
MenuComponent?.createInsance("user_menu");
MenuComponent?.reinitialization();
I think I have found the problem.
In "kt.menu.dropdown" events if I remove the line with "return false;" "User menu" works correctly.
Here is the correct code:
onMounted(() => {
nextTick(() => {
const toolbarElement = document.getElementById(
"user_menu"
) as HTMLInputElement;
console.log(toolbarElement);
MenuComponent?.reinitialization();
MenuComponent?.createInsance("user_menu");
const toolbar = MenuComponent?.getInstance(toolbarElement);
console.log(toolbar);
toolbar?.on("kt.menu.dropdown.show", function () {
console.log("open");
});
toolbar?.on("kt.menu.dropdown.hide", function () {
console.log("close");
});
});
});
Sorry I didn't notice return true;
in your code.
Here is an example of how you can achieve the same thing without calling createInsance
function.
onMounted(() => {
nextTick(() => {
const item: HTMLElement = document.getElementById(
"instance_id"
) as HTMLElement;
MenuComponent.reinitialization();
const menu = MenuComponent.getInstance(item);
menu?.on("kt.menu.dropdown.show", function () {
console.log("show");
});
menu?.on("kt.menu.dropdown.hide", function () {
console.log("hide");
});
});
});
Hello
I have another problem.
In the user menu I have a dropdown item.
On the over of this element the same event is triggered.
I would like the event to be triggered only at the opening and closing of the first dropdown.
Is there any way to do this?
Here is the HTML part:
<template>
<!--begin::Menu-->
<div class="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-600 menu-state-bg-light-primary fw-bold py-4 fs-6 min-w-275px w-auto" data-kt-menu="true" id="user_menu">
<!--begin::Menu item-->
<div class="menu-item px-3">
<div class="menu-content d-flex align-items-center px-3">
<!--begin::Avatar-->
<div class="symbol symbol-50px me-5">
<img alt="Logo" :src="user.avatar">
</div>
<!--end::Avatar-->
<!--begin::Username-->
<div class="d-flex flex-column">
<div class="fw-bolder d-flex align-items-center fs-5">
{{ user.first_name }} {{ user.last_name }}
</div>
<span class="fw-bold text-muted text-hover-primary fs-7">{{ user.email }}
</span>
</div>
<!--end::Username-->
</div>
</div>
<!--end::Menu item-->
<!--begin::Menu separator-->
<div class="separator my-2"></div>
<!--end::Menu separator-->
<!--begin::Menu item-->
<div class="menu-item px-5">
<router-link to="/profile-settings" class="menu-link px-5">
{{ $t("my_profile") }}
</router-link>
</div>
<!--end::Menu item-->
<!--begin::Menu separator-->
<div class="separator my-2"></div>
<!--end::Menu separator-->
<!--begin::Menu item-->
<div class="menu-item px-5" data-kt-menu-trigger="hover" data-kt-menu-placement="left-start" data-kt-menu-flip="center, top">
<a class="menu-link px-5">
<span class="menu-title position-relative">
{{ $t("language") }}
<span class="fs-8 rounded bg-light px-3 py-2 position-absolute translate-middle-y top-50 end-0">
{{ language.name }}
<img class="w-15px h-15px rounded-1 ms-2" :src="language.flag" alt="language">
</span>
</span>
</a>
<!--begin::Menu separator-->
<div class="separator my-2"></div>
<!--end::Menu separator-->
<!--begin::Menu sub-->
<div class="menu-sub menu-sub-dropdown w-175px py-4">
<!--begin::Menu item-->
<div class="menu-item px-3">
<a class="menu-link d-flex px-5" :class="{ active: language.locale === "en" }">
<span class="symbol symbol-20px me-4">
<img class="rounded-1" src="media/flags/united-states.svg" alt="en">
</span>
{{ $t("english") }}
</a>
</div>
<!--end::Menu item-->
<!--begin::Menu item-->
<div class="menu-item px-3">
<a class="menu-link d-flex px-5" :class="{ active: language.locale === "it" }">
<span class="symbol symbol-20px me-4">
<img class="rounded-1" src="media/flags/italy.svg" alt="it">
</span>
{{ $t("italian") }}
</a>
</div>
<!--end::Menu item-->
</div>
<!--end::Menu sub-->
</div>
<!--end::Menu item-->
<!--begin::Menu item-->
<div class="menu-item px-5">
<a class="menu-link px-5"> {{ $t("sign_out") }} </a>
</div>
<!--end::Menu item-->
</div>
<!--end::Menu-->
</template>
If you added event listening code to this component it will be triggered on every component instance, to avoid this you can move your code from this component to component where you are using it.
For example, you have two usage places, the first is in Toolbar and second is in the Header, you want to trigger the event only when the user clicks the menu in the Toolbar, then you need to move code to the Toolbar level.
Regards,
Lauris Stepanovs,
Keenthemes Support Team
Hi,
the previous javascript code was already in Toolbar.vue
But the events is also triggered on the "Language" dropdown element in "UserMenu.vue"
Did you removed MenuComponent?.createInsance("user_menu");
from your component?
Regards,
Lauris Stepanovs,
Keenthemes Support Team
yes
here the final code:
onMounted(() => {
nextTick(() => {
const toolbarElement = document.getElementById(
"user_menu"
) as HTMLInputElement;
MenuComponent?.reinitialization();
const toolbar = MenuComponent?.getInstance(toolbarElement);
toolbar.on("kt.menu.dropdown.show", function () {
console.log("open");
});
toolbar.on("kt.menu.dropdown.hide", function () {
console.log("close");
});
});
});
Hi,
Do you have a second component instance in your toolbar or in the toolbar child components?
If in your toolbar you have only one component instance with id user_menu then events should trigger only when you click the menu inside in toolbar component.
Regards,
Lauris Stepanovs,
Keenthemes Support Team
How can I check this?
here the HTML code of Topbar.vue
<template>
<!--begin::Toolbar wrapper-->
<div class="d-flex align-items-stretch flex-shrink-0">
<!--begin::Notifications-->
<div class="d-flex align-items-center ms-1 ms-lg-3"></div>
<!--end::Notifications-->
<!--begin::User-->
<div class="dropdown d-flex align-items-center ms-1 ms-lg-3" id="kt_header_user_menu_toggle">
<!--begin::Menu-->
<div class="cursor-pointer symbol symbol-30px symbol-md-40px" data-kt-menu-trigger="click" data-kt-menu-attach="parent" data-kt-menu-placement="bottom-end" data-kt-menu-flip="bottom">
<img :src="user.avatar" alt="metronic">
</div>
<ktusermenu></ktusermenu>
<!--end::Menu-->
</div>
<!--end::User -->
</div>
<!--end::Toolbar wrapper-->
</template>
If you have the same structure as it is in Metronic 8 then you should have only one instance in the Topbar component. In which file do you use your second KTUserMenu component instance?
Regards,
Lauris Stepanovs,
Keenthemes Support Team
KTUserMenu is imported only in Topbar.vue
here the complete component Topbar.vue:
<template>
<!--begin::Toolbar wrapper-->
<div class="d-flex align-items-stretch flex-shrink-0">
<!--begin::Notifications-->
<div class="d-flex align-items-center ms-1 ms-lg-3"></div>
<!--end::Notifications-->
<!--begin::User-->
<div class="dropdown d-flex align-items-center ms-1 ms-lg-3" id="kt_header_user_menu_toggle">
<!--begin::Menu-->
<div class="cursor-pointer symbol symbol-30px symbol-md-40px" data-kt-menu-trigger="click" data-kt-menu-attach="parent" data-kt-menu-placement="bottom-end" data-kt-menu-flip="bottom">
<img :src="user.avatar" alt="metronic">
</div>
<ktusermenu></ktusermenu>
<!--end::Menu-->
</div>
<!--end::User -->
</div>
<!--end::Toolbar wrapper-->
</template>
<script lang="ts">
import { defineComponent, onMounted, nextTick } from "vue";
import KTUserMenu from "@/layout/header/partials/UserMenu.vue";
import { useAuthStore } from "@/store/AuthStore";
import { storeToRefs } from "pinia";
import { MenuComponent } from "@/assets/ts/components";
export default defineComponent({
name: "header-topbar",
components: {
KTUserMenu,
},
setup() {
const authStore = useAuthStore();
const { user } = storeToRefs(authStore);
onMounted(() => {
nextTick(() => {
const toolbarElement = document.getElementById(
"user_menu"
) as HTMLInputElement;
MenuComponent?.reinitialization();
const toolbar = MenuComponent?.getInstance(toolbarElement);
toolbar.on("kt.menu.dropdown.show", function () {
console.log("open");
});
toolbar.on("kt.menu.dropdown.hide", function () {
console.log("close");
});
});
});
return { user };
},
});
</script>
<template>
<!--begin::Menu-->
<div class="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-600 menu-state-bg-light-primary fw-bold py-4 fs-6 min-w-275px w-auto" data-kt-menu="true" id="user_menu">
<!--begin::Menu item-->
<div class="menu-item px-3">
<div class="menu-content d-flex align-items-center px-3">
<!--begin::Avatar-->
<div class="symbol symbol-50px me-5">
<img alt="Logo" :src="user.avatar">
</div>
<!--end::Avatar-->
<!--begin::Username-->
<div class="d-flex flex-column">
<div class="fw-bolder d-flex align-items-center fs-5">
{{ user.first_name }} {{ user.last_name }}
</div>
<span class="fw-bold text-muted text-hover-primary fs-7">{{ user.email }}
</span>
</div>
<!--end::Username-->
</div>
</div>
<!--end::Menu item-->
<!--begin::Menu separator-->
<div class="separator my-2"></div>
<!--end::Menu separator-->
<!--begin::Menu item-->
<div class="menu-item px-5">
<router-link to="/profile-settings" class="menu-link px-5">
{{ $t("my_profile") }}
</router-link>
</div>
<!--end::Menu item-->
<!--begin::Menu separator-->
<div class="separator my-2"></div>
<!--end::Menu separator-->
<!--begin::Menu item-->
<div class="menu-item px-5" data-kt-menu-trigger="hover" data-kt-menu-placement="left-start" data-kt-menu-flip="center, top">
<a class="menu-link px-5">
<span class="menu-title position-relative">
{{ $t("language") }}
<span class="fs-8 rounded bg-light px-3 py-2 position-absolute translate-middle-y top-50 end-0">
{{ language.name }}
<img class="w-15px h-15px rounded-1 ms-2" :src="language.flag" alt="language">
</span>
</span>
</a>
<!--begin::Menu separator-->
<div class="separator my-2"></div>
<!--end::Menu separator-->
<!--begin::Menu sub-->
<div class="menu-sub menu-sub-dropdown w-175px py-4">
<!--begin::Menu item-->
<div class="menu-item px-3">
<a class="menu-link d-flex px-5" :class="{ active: language.locale === "en" }">
<span class="symbol symbol-20px me-4">
<img class="rounded-1" src="media/flags/united-states.svg" alt="en">
</span>
{{ $t("english") }}
</a>
</div>
<!--end::Menu item-->
<!--begin::Menu item-->
<div class="menu-item px-3">
<a class="menu-link d-flex px-5" :class="{ active: language.locale === "it" }">
<span class="symbol symbol-20px me-4">
<img class="rounded-1" src="media/flags/italy.svg" alt="it">
</span>
{{ $t("italian") }}
</a>
</div>
<!--end::Menu item-->
</div>
<!--end::Menu sub-->
</div>
<!--end::Menu item-->
<!--begin::Menu item-->
<div class="menu-item px-5">
<a class="menu-link px-5"> {{ $t("sign_out") }} </a>
</div>
<!--end::Menu item-->
</div>
<!--end::Menu-->
</template>
<script lang="ts">
import { defineComponent } from "vue";
import AuthService from "@/core/services/AuthService";
import { useAuthStore } from "@/store/AuthStore";
import { useLocaleStore } from "@/store/LocaleStore";
import { useRouter } from "vue-router";
import { storeToRefs } from "pinia";
import { useI18n } from "vue-i18n/index";
export default defineComponent({
name: "kt-user-menu",
components: {},
setup() {
const router = useRouter();
const signOut = async () => {
await AuthService.logout();
router.push({ name: "SignIn" });
};
const i18n = useI18n({ useScope: "global" });
const localeStore = useLocaleStore();
const { language } = storeToRefs(localeStore);
const setLang = (locale: string, name: string, flag: string) => {
const language = { locale, name, flag };
localeStore.setLanguage(language);
i18n.locale.value = locale;
};
const authStore = useAuthStore();
const { user } = storeToRefs(authStore);
return {
signOut,
setLang,
language,
user,
};
},
});
</script>
Hi,
Right now the same event will be triggered when you click on main menu on all submenus.
If you need to execute your code only on main menu show event, you can do the following changes.
sub
menu variable declaration on top of the function const sub = this._getItemSubElement(item)
.if (EventHandlerUtil.trigger(this.element, "kt.menu.dropdown.show", sub) === false) {
return
}
EventHandlerUtil.trigger(this.element, "kt.menu.dropdown.shown", sub)
toolbar?.on("kt.menu.dropdown.show", function (target) {
if (target === toolbarElement) {
console.log("show");
}
});