Ho to define vue-router components inside a html? - django

I'm using django + vue.js + vue-router.js to make my project. I'm trying to use vue-router in a single html page. I searched for a while, all examples are use .vue components, or define the component templates in js part simpely, just like this:
<body>
<div id="app">
<div>
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</div>
<div>
<router-view></router-view>
</div>
</div>
<script>
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes
})
const app = new Vue({
router
}).$mount('#app')
</script>
</body>
What I want is define the template outside the js part something like this:
<body>
<div id="app">
<div>
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</div>
<div>
<router-view></router-view>
</div>
</div>
<template id="Foo">
<div>this is Foo</div>
</template>
<template id="Bar">
<div>this is Bar</div>
</template>
<script>
const Foo = { template: '#Foo' }
const Bar = { template: '#Bar' }
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes
})
const app = new Vue({
router
}).$mount('#app')
</script>
</body>
I tried this, but not work. So how to define vue-router components inside a html? I'm new with vue..

You need to add <router-view/> in html file. e.g.
const Foo = { template: '<div>this is Foo</div>' }
const Bar = { template: '<div>this is Bar</div>' }
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes
})
const app = new Vue({
router
}).$mount('#app')
<div id="app">
<router-view/>
</div>

Related

How to make when a user clicks accept all cookies google analytics is enabled , but when user clicks only necessary google analytics is disabled

First thank you for reading my question and trying to help.
I'm trying to make a cookie accept footer and when user clicks on accept all I want to enable google analytics and google tag manager. But when user clicks on only necessary I only want google tag manager and I don't know how to make that.
This is my cookie footer :
import { useState } from "react";
import Cookies from 'js-cookie'
function FooterCookies() {
cosnt [ show, setShow ] = useState(true)
const content = {
text : 'This site uses services that uses cookies to deliver better experience and analyze traffic. You can learn more about the services we use at our ',
policy : 'Privacy Policy.'
}
const setCookies = () => {
Cookies.set('cookie', 'true', { expires: 2 })
}
return (
<>
{show ?
(
<div
className=" h-auto bg-black/30 fixed
bottom-0 text-cus-yellow rounded
py-4 px-8 flex justify-evenly items-center z-[100]
max75:flex-col max75:px-5 max75:py-3 max30:py-1 max30:px-1.5 max75:items-start">
<div className=" w-85% max70:text-sm flex items-center max75:w-100% max75:items-start max75:mb-3 " >
<p className=" text-[13px] pt-1">
{content.text}
<span className=" text-[16px] underline cursor-pointer ">
{content.policy}
</span>
</p>
</div>
<div className=" flex " >
<div onClick={() => setCookies()}>
<div onClick={() => setShow(false)}
className="border border-cus-yellow text-cus-yellow hover:bg-cus-yellow/60
transition-all hover:text-black w-28 h-80% bg-black/40 cursor-pointer
flex flex-col justify-center items-center whitespace-nowrap
duration-500 text-lg max75:text-base font-medium mx-2 px-1 py-2 max75:mx-0 max75:py-1 max75:px-0.5 "
>Accept All</div>
</div>
<div onClick={() => setShow(false)}
className="border border-cus-yellow text-cus-yellow hover:bg-cus-yellow/60
transition-all hover:text-black w-36 h-80% bg-black/40 cursor-pointer
flex flex-col justify-center items-center whitespace-nowrap
duration-500 text-lg max75:text-base font-medium mx-2 px-1 py-[13.76px] max75:mx-0 max75:ml-3 max75:px-0.5 max75:py-[8.75px] "
>Only Nessesary</div>
</div>
</div>
)
:
<></>
}
</>
)
}
This is my gtag.js :
export const GA_TRACKING_ID = 'XXXXXXXXX'
export const pageview = (url) => {
window.gtag('config', GA_TRACKING_ID, {
page_path: url,
})
}
export const event = ({ action, category, label, value }) => {
window.gtag('event', action, {
event_category: category,
event_label: label,
value: value,
})
}
This is myApp.js :
import '../styles/globals.css'
import 'tailwindcss/tailwind.css'
import { useEffect } from 'react'
import Script from 'next/script'
import { useRouter } from 'next/router'
import * as gtag from '../lib/gtag'
function MyApp({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleRouteChange = (url) => {
gtag.pageview(url)
}
router.events.on('routeChangeComplete', handleRouteChange)
router.events.on('hashChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)
router.events.off('hashChangeComplete', handleRouteChange)
}
}, [router.events])
return (
<>
<Script
strategy="afterInteractive"
src={`https://www.googletagmanager.com/gtag/js?id=${gtag.GA_TRACKING_ID}`}
/>
<Script
id="gtag-init"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${gtag.GA_TRACKING_ID}', {
page_path: window.location.pathname,
});
`,
}}
/>
<Script id="google-tag-manager" strategy="afterInteractive">
{`
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','XXXXXXXX');
`}
</Script>
<Component {...pageProps} />
</>
)
}
export default MyApp
This is my document.js :
import { Html, Head, Main, NextScript } from 'next/document'
import Script from 'next/script'
import { GA_TRACKING_ID } from '../lib/gtag'
export default function Document() {
return (
<Html>
<Head>
<Script
id='google tags manager'
dangerouslySetInnerHTML={{
__html:`(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','XXXXXXXX');`,
}}
/>
<Script
async src={`https://www.googlemanager.com/gtag/js?id=${GA_TRACKING_ID}`}
/>
</Head>
<body>
<noscript
dangerouslySetInnerHTML={{
__html: `<iframe src="https://www.googletagmanager.com/ns.html?id=XXXXXXX"
height="0" width="0" style="display:none;visibility:hidden" />`,
}}
/>
<Main />
<NextScript />
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
`,
}}
/>
</body>
</Html>
)
}

How do I detect change Sveltekit state

I am trying to add a class to an element in one component (nav) when an animation in a different component (logo) ends. In my $lib/Logo.svelte file I have the following code:
<script>
import { onMount } from 'svelte';
import { isLogoAnimationEnded } from './stores';
onMount(() => {
const body = document.querySelector('body');
const h1 = document.querySelector('.name');
h1?.addEventListener('animationend', () => {
isLogoAnimationEnded.update((n) => (n = true));
console.log($isLogoAnimationEnded);
body?.classList.add('shake');
});
return () => {
h1?.removeEventListener('animationend', () => {
isLogoAnimationEnded.set(false);
body?.classList.remove('shake');
});
};
});
</script>
<div class="logo-wrapper">
<h1 class="name">Tim Smith</h1>
<p class="title">Full Stack Web Engineer</p>
</div>
<style>
...
</style>
My
store is defined in $lib/stores.js:
import { writable } from 'svelte/store';
export const isLogoAnimationEnded = writable(false);
What I am trying to do is listen for a change in isLogoAnimationEnded in $lib/Nav.svelte and add a class to nav when isLogoAnimationEnded becomes true.
<script lang="ts">
import { onMount } from 'svelte';
import { isLogoAnimationEnded } from './stores';
let nav;
onMount(() => {
nav = document.querySelector('nav');
console.log('nav', nav);
});
if ($isLogoAnimationEnded) {
nav?.classList.add('fly-down');
}
</script>
<div class="nav-wrapper">
<nav aria-label="Main">
<ul>
<li>About</li>
<li>Projects</li>
<li>Contact</li>
</ul>
</nav>
</div>
<style>
...
</style>
My current setup does not work. Please help.
The code below is only going to run once
if ($isLogoAnimationEnded) {
nav?.classList.add('fly-down');
}
So I would delete it and write the class attribute on nav element like this:
<nav class={$isLogoAnimationEnded ? "fly-down" : ""} aria-label="Main">
This also gets rid of the onMount, nav variable and querySelector

NuxtJS , Unit Test language picker with Jest and nuxt-i18n

I have a component that switch Language of a nuxtjs application using nuxt-i18n as follows
<template>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link langpicker">{{ $t("language_picker") }} </a>
<div class="navbar-dropdown is-hidden-mobile">
<div>
<nuxt-link
v-if="currentLanguage != 'en'"
class="navbar-item"
:to="switchLocalePath('en')"
>
<img src="~/static/flags/us.svg" class="flagIcon" /> English
</nuxt-link>
<nuxt-link
v-if="currentLanguage != 'el'"
class="navbar-item"
:to="switchLocalePath('el')"
>
<img src="~/static/flags/el.svg" class="flagIcon" /> Ελληνικά
</nuxt-link>
</div>
</div>
</div>
</template>
<script>
export default {
name: "LangPicker",
computed: {
currentLanguage() {
return this.$i18n.locale || "en";
}
}
};
</script>
I want to write a Unit Test that test the correct language switch on 'nuxt-link' click.
So far I have the following
import { mount, RouterLinkStub } from "#vue/test-utils";
import LangPicker from "#/components/layout/LangPicker";
describe("LangPicker with locale en", () => {
let cmp;
beforeEach(() => {
cmp = mount(LangPicker, {
mocks: {
$t: msg => msg,
$i18n: { locale: "en" },
switchLocalePath: msg => msg
},
stubs: {
NuxtLink: RouterLinkStub
}
});
});
it("Trigger language", () => {
const el = cmp.findAll(".navbar-item")
});
});
cmp.find(".navbar-item") return an empty object.
I don't know how I must set up to "trigger" the click event.
const el = cmp.findAll(".navbar-item")[1].trigger("click");
make sure your find selector is correct.
const comp = cmp.find(".navbar-item");
comp.trigger('click');
you can use chrome dev tools selector utility.
Refer this link for detailed information.

Vue.js - Vuetify - Hard time to test a menu component

Given the following Toolbar component, I am trying to test the locale changing upon click on menu item ... but the wrapper content is not changing after a mouseover event on the menu selector ...
Toolbar.vue
<template>
<v-toolbar height="80px" fixed>
<v-toolbar-title>
<img src="#/assets/images/app_logo.png" alt="">
<v-menu bottom offset-y open-on-hover class="btn btn--flat" style="margin-bottom: 12px;">
<v-btn id="current-flag" flat slot="activator">
<img :src="flagImage(currentLocaleIndex)" width="24px">
</v-btn>
<v-list>
<v-list-tile v-for="(locale, index) in locales" :key="index" #click="switchLocale(index)">
<div class="list__tile__avatar avatar--tile" #click="switchLocale(index)">
<img :src="flagImage(index)" width="24px">
</div>
<div class="list__tile__title" v-html="flagTitle(index)"></div>
</v-list-tile>
</v-list>
</v-menu>
</v-toolbar-title>
<v-spacer></v-spacer>
<v-toolbar-items>
<!-- MENU HOME-->
<v-btn flat #click="$router.push(menuItems.home.link)">
<v-icon left>{{ menuItems.home.icon }}</v-icon>
<span>{{ menuItems.home.title | translate }}</span>
</v-btn>
<!-- ABOUT HOME-->
<v-btn flat #click="$router.push(menuItems.about.link)">
<v-icon left>{{ menuItems.about.icon }}</v-icon>
<span>{{ menuItems.about.title | translate }}</span>
</v-btn>
</v-toolbar-items>
</v-toolbar>
</template>
<script>
// import i18n from '#/locales'
export default {
name: "Toolbar",
props: ["appName"],
data() {
return {
menuItems: {
home: { icon: "home", title: "Home", link: "/" },
about: { icon: "info", title: "About", link: "/about" }
},
locales: [
{ id: "en", title: "English", flag: "#/assets/images/flag_en_24.png" },
{ id: "fr", title: "Français", flag: "#/assets/images/flag_fr_24.png" },
{ id: "br", title: "Português", flag: "#/assets/images/flag_br_24.png" }
]
};
},
computed: {
currentLocaleIndex: function() {
let index = this.locales.findIndex(o => o.id === this.$i18n.locale);
return index;
},
currentLocaleTitle: function() {
let obj = this.locales.find(o => o.id === this.$i18n.locale);
return obj.title;
},
currentLocaleFlag: function() {
let obj = this.locales.find(o => o.id === this.$i18n.locale);
return obj.flag;
}
},
methods: {
flagImage: function(index) {
return require("#/assets/images/flag_" +
this.locales[index].id +
"_24.png");
},
flagTitle: function(index) {
return this.locales[index].title;
},
switchLocale: function(index) {
this.$i18n.locale = this.locales[index].id;
}
},
mounted() {}
};
</script>
Toobar.spec.je
import Vue from "vue";
import router from "#/router";
import Vuetify from "vuetify";
import i18n from "#/locales";
import { mount, shallowMount } from "#vue/test-utils";
import Toolbar from "#/components/shared/Toolbar.vue";
describe("App.vue", () => {
let wrapper;
beforeEach(() => {
Vue.use(Vuetify);
Vue.filter("translate", function(value) {
if (!value) return "";
value = "lang.views.global." + value.toString();
return i18n.t(value);
});
const el = document.createElement('div');
el.setAttribute('data-app', true);
document.body.appendChild(el);
});
it("should change locale", () => {
// given
wrapper = mount(Toolbar, { router, i18n });
console.log('CURRENT LOCALE INDEX: ', wrapper.vm.currentLocaleIndex);
// console.log(wrapper.html());
const currentFlagBtn = wrapper.find("#current-flag");
console.log(currentFlagBtn.html())
currentFlagBtn.trigger('mouseover');
wrapper.vm.$nextTick( () => {
console.log(wrapper.html());
// const localesBtn = wrapper.findAll("btn");
});
// when
// localesBtn.at(1).trigger("click"); // French locale
// then
// expect(wrapper.wrapper.vm.currentLocaleIndex).toBe(1);
});
});
console.log
console.log tests/unit/Toolbar.spec.js:32
CURRENT LOCALE INDEX: 0
console.log tests/unit/Toolbar.spec.js:35
<button type="button" class="v-btn v-btn--flat" id="current-flag"><div class="v-btn__content"><img src="[object Object]" width="24px"></div></button>
console.log tests/unit/Toolbar.spec.js:38
<nav class="v-toolbar v-toolbar--fixed" style="margin-top: 0px; padding-right: 0px; padding-left: 0px; transform: translateY(0px);">
<div class="v-toolbar__content" style="height: 80px;">
<div class="v-toolbar__title">
<img src="#/assets/images/app_logo.png" alt="">
<div class="v-menu btn btn--flat v-menu--inline" style="margin-bottom: 12px;">
<div class="v-menu__activator">
<button type="button" class="v-btn v-btn--flat" id="current-flag">
<div class="v-btn__content">
<img src="[object Object]" width="24px">
</div>
</button>
</div>
</div>
</div>
<div class="spacer"></div>
<div class="v-toolbar__items">
<button type="button" class="v-btn v-btn--flat">
<div class="v-btn__content">
<i aria-hidden="true"class="v-icon v-icon--left material-icons">home</i>
<span>Home</span>
</div>
</button>
<button type="button" class="v-btn v-btn--flat">
<div class="v-btn__content">
<i aria-hidden="true" class="v-icon v-icon--left material-icons">info</i>
<span>About</span>
</div>
</button>
</div>
</div>
</nav>
I found a solution, but maybe someone can explain why :
btnFlags.at(1).vm.$emit('click'); // OK
and
btnFlags.at(1).trigger('click'); // NOT OK
this is the spec which is running fine :
import Vue from "vue";
import router from "#/router";
import Vuetify from "vuetify";
import i18n from "#/locales";
import { mount, shallowMount } from "#vue/test-utils";
import Toolbar from "#/components/shared/Toolbar.vue";
describe("Toolbar.vue", () => {
let wrapper;
beforeEach(() => {
Vue.use(Vuetify);
Vue.filter("translate", function(value) {
if (!value) return "";
value = "lang.views.global." + value.toString();
return i18n.t(value);
});
const el = document.createElement('div');
el.setAttribute('data-app', true);
document.body.appendChild(el);
});
it("should change locale", async () => {
// given
wrapper = shallowMount(Toolbar, { router, i18n });
console.log('CURRENT LOCALE INDEX: ', wrapper.vm.currentLocaleIndex);
const btnFlags = wrapper.findAll("v-list-tile-stub");
// when
btnFlags.at(1).vm.$emit('click');
await wrapper.vm.$nextTick();
// then
console.log('NEW LOCALE INDEX: ', wrapper.vm.currentLocaleIndex);
expect(wrapper.vm.currentLocaleIndex).toBe(1);
});
});

Why is the URL not updated when I click the link for that route

I am using EmberJs version 1.4.
When I click on one of the links I would expect the URL to include the id of the selected widget but nothing appears and when I look at the params parameter in the route model hook it has no properties and I would expect the id to be one of its properties so could someone help me to understand what am I missing?
In other words I would expect the URL to become awesome.html#/widgets/5 but it always is awesome.html#/widgets
Thank you!
This is my ember code:
window.Awesome = Ember.Application.create();
Awesome.Router.map(function() {
this.resource("awesome", {path: "/"}, function(){
this.route('login');
});
this.resource("widgets", function () {
this.resource('widget', { path: '/:widgetId' }, function () {
this.route('general', { path: 'info' });
this.route('configuration');
this.route('operations');
})
});
});
Awesome.WidgetsRoute = Awesome.AuthenticationRoute.extend({
model: function(){
//TODO: Call a service to get the model.
return { widgets: [{ widgetId: 1, widgetName: "Great Widget" }, { widgetId: 2, widgetName: "Fantastic Widget" }, { widgetId: 3, widgetName: "Brutal Widget" }] };
}
});
Awesome.WidgetIndexRoute = Awesome.AuthenticationRoute.extend({
model: function (params) {
var receivedWidgetId = params.widgetId;
return { widgetName: "Hardcoded Widget", widgetId: receivedWidgetId };
}
});
These are the templates:
<script type="text/x-handlebars" data-template-name="widgets">
<section class="span3 left-section">
<div class="btn-group-vertical btn-group-justified registration-actions-menu">
<button id="createNewWidget" class="btn btn-link">Create New Widget</button>
<button id="joinWidgetTeam" class="btn btn-link">Join Widget Team</button>
</div>
<div class="registered-widgets-menu">
<div class="btn-group-vertical">
{{#each widget in widgets}}
{{#link-to 'widget' widget class="btn btn-link"}}{{widget.widgetName}}{{/link-to}}
{{/each}}
</div>
</div>
</section>
<section class="span8">
{{outlet}}
</section>
</script>
<script type="text/x-handlebars" data-template-name="widget">
<div id="widgetOptions">
<!-- TODO: Change the anchors for handlebars link-to helpers. -->
<h1>{{widgetName}}</h1> <h5>{{widgetId}}</h5>
<ul id="widgetNavigation">
<li>Widget Info</li>
<li>Widget Configuration</li>
<li>Widget Operations</li>
</ul>
</div>
<div id="widgetContent">
<!-- TODO: Design some awesome widget content. -->
Some awesome widget content
</div>
</script>
I also have an authentication route from which the other routes inherit. Even though I don't believe it has something to do with the issue I'll include just in case I am wrong.
Awesome.AuthenticationRoute = Ember.Route.extend({
beforeModel: function(transition){
if(!Awesome.get('loggedUser')){
this.redirectToLogin(transition);
}
},
redirectToLogin: function(transition) {
var loginController = this.controllerFor('awesome.login');
loginController.set('attemptedTransition', transition);
this.transitionTo('awesome.login');
}
});
It looks like it's totally working to me, when you click on one of the widgets
http://emberjs.jsbin.com/mohex/1
Additionally it looks like you're mixing up the WidgetIndexRoute and WidgetRoute. The widget resource should be displayed like this (though this is unrelated to the issue you're describing)
Awesome.WidgetRoute = Awesome.AuthenticationRoute.extend({
model: function (params) {
var receivedWidgetId = params.widgetId;
return { widgetName: "Hardcoded Widget", widgetId: receivedWidgetId };
}
});