IonContent scrollToTop function not working when I change the route - vue.js

I'm using ionic-vue to create a mobile app.
IonContent scrollToTop function is not working when I switch routes.
such as:
http://localhost:8100/tabs/q/s/20 -> http://localhost:8100/tabs/q/s/21
Details:
On the original page, when I click the scrollToTop button, it works fine. But if I go forward a link or backwards a link and then click the scrollToTop button, it doesn't work. However, if I go back to the original link, then the scrollToTop button works.
I don't understand why it's acting this way.
This is the code for the button (ScrollToTopButton.vue):
<template>
<div id="myBtn">
<ion-button #click="scrollToTop">
<ion-icon :icon="arrowUpCircleOutline" color="success"></ion-icon>
</ion-button>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { IonButton, IonIcon } from '#ionic/vue'
import { arrowUpCircleOutline } from 'ionicons/icons';
export default defineComponent({
components: {
IonButton,
IonIcon,
},
setup () {
const scrollToTop = () => {
let scrollContent = document.querySelector('ion-content')!;
console.log(scrollContent)
//scrollContent.scrollToTop(500);
console.log(scrollContent.scrollToTop(600))
}
return { arrowUpCircleOutline, scrollToTop };
}
})
</script>
Here is the code for IonContent (in TabLayout.vue):
<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<slot name="actions-start"></slot>
<ion-back-button :default-href="pageDefaultBackLink" v-if="pageDefaultBackLink !== ''"></ion-back-button>
</ion-buttons>
<ion-title>
<div class="ion-text-wrap pageTitle">
{{title}}
</div>
</ion-title>
<ion-buttons slot="end">
<slot name="actions-end"></slot>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content :fullscreen="true" class="bindingScrollContent" :scroll-events="true">
<slot/>
</ion-content>
</ion-page>
</template>
This is the code where I use the ScrollToTop button inside of the IonContent (in Surah.vue):
<template>
<TabLayout :title="title" pageDefaultBackLink=''>
...
<template v-slot:actions-end>
<ScrollToTopButton/>
</template>
<SurahContainer :surah="cur_surah"/>
</TabLayout>
</template>
I tried many things but it's hard to debug this since the console log statements for data from the button doesn't change even on different routes.
Any idea or help would be awesome.

add the two lines as a comment to solve the problem
const ScrollToBottom = () => {
let scrollContent = document.querySelector('ion-content');
// eslint-disable-next-line #typescript-eslint/ban-ts-comment
//#ts-ignore
scrollContent.scrollToTop(400);
};

Related

Ionic Vue: Router Link isn’t working/doing anything

I’m developing an app and getting some of the basics down, however, when I copy the tutorial I’m using and copied the official documentation example, I can’t get router link to work at all, it does nothing. I have no idea what I’m doing wrong but it neither make a component clickable or leads anywhere. I know the pages I’ve linked to work and are reachable because I’ve gotten to them by changing the url while testing on Google Chrome. Here is my code.
First Page I’m navigating from
> <template> <base-layout page-title="Student List/MainTemp">
> <ion-list>
> <ion-item router-link="/studentList/1">Billy Click click Click click</ion-item>
> <ion-item>Michael</ion-item>
> <ion-item>Gabby</ion-item>
> <ion-item>Nick</ion-item>
> <ion-item>Virginia</ion-item>
> </ion-list>
> <ion-button router-link="/studentListTest.vue" :router-animation="myAnimation">Click Me</ion-button> </base-layout>
> </template>
>
> <script>
> import { IonButton, IonList, IonItem } from "#ionic/vue";
> export default {
> IonButton, IonList, IonItem
> };
> </script>
Pages I’m tring to navigate to:
<template>
<base-layout>
<h2>the student details</h2>
</base-layout>
</template>
<script>
export default {
}
</script>
Second Test Page
<template>
<base-layout page-title="Student List Test">
<ion-list>
<ion-item router-link="/studentList/1">Jimmyyy tick tick Click click</ion-item>
<ion-item>Michaasdfel</ion-item>
<ion-item>Gabbasdfy</ion-item>
<ion-item>Nickasdf</ion-item>
<ion-item>West Virginia</ion-item>
</ion-list>
<ion-button router-link="/" :router-animation="myAnimation">Click zzMe</ion-button>
</base-layout>
</template>
<script>
import {
IonButton,
IonList,
IonItem
} from "#ionic/vue";
export default {
IonButton,
IonList,
IonItem
};
</script>
Router index.js
import { createRouter, createWebHistory } from '#ionic/vue-router';
import studentList from '../pages/studentList.vue';
import studentListTest from '../pages/studentListTest.vue';
const routes = [
{
path: '/',
redirect: '/studentList'
},
{
path: '/studentList',
component: studentList
},
{
path: '/studentListTest',
component: studentListTest
},
{
path: '/studentList/1',
component: () => import('../pages/studentDetails.vue')
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
And the base-layout for extra context
<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title> {{ pageTitle }} </ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<slot />
</ion-content>
</ion-page>
</template>
<script>
import {
IonPage,
IonHeader,
IonTitle,
IonToolbar,
IonContent,
} from "#ionic/vue";
export default {
props: {
pageTitle: String
},
IonPage,
IonHeader,
IonTitle,
IonToolbar,
IonContent,
};
</script>
Vue Router renders the current paht's component in the router-view component.
Include <router-view></router-view> where you want the content to render.
I got an answer somewhere else I'll post here.
I didn't include this in the script section.
components: {
**insert components here**
}
And that prevented it from working, so here's the script section fixed
<script>
import { IonButton, IonList, IonItem } from "#ionic/vue";
import baseLayout from "#/components/base/baseLayout.vue"; (I exported this globally but I'll include it here anyway)
export default {
components: {
baseLayout,
IonButton,
IonList,
IonItem,
},
};

vue RouterLink is not working properly in ionic

when Im using router-link in ionic vue, the first navigation is ok, but when I try to navigate for the second time or even when I press browser back button I get this error:
Uncaught TypeError: can't access property "classList", enteringEl is undefined
these are my components that I tried testing:
Products:
<template>
<master-layout pageTitle="Login Form">
<ion-card-header>
<ion-card-title>Product Page</ion-card-title>
</ion-card-header>
<ion-card-content>
Test<br/>
<RouterLink to="/Warehouses">Warehouses</RouterLink>
</ion-card-content>
</master-layout>
</template>
<script>
import {
IonCardHeader, IonCardTitle, IonCardContent,RouterLink
} from "#ionic/vue";
export default {
components: {
IonCardHeader, IonCardTitle, IonCardContent,RouterLink
}
};
</script>
warehouses:
<template>
<master-layout pageTitle="Login Form">
<ion-card-header>
<ion-card-title>Warehouse Page</ion-card-title>
</ion-card-header>
<ion-card-content>
Warehouse<br/>
<RouterLink to="/Products">Products</RouterLink>
</ion-card-content>
</master-layout>
</template>
<script>
import {
IonCardHeader, IonCardTitle, IonCardContent,RouterLink
} from "#ionic/vue";
export default {
components: {
IonCardHeader, IonCardTitle, IonCardContent,RouterLink
}
};
</script>
now when I start navigating from products, when I click on warehouses, its ok and I navigate to warehouses, but when I click back button or products link, I get the error I said above.
any suggestion in what is causing this problem and how should I fix it?
for those who have the same problem, this is the solution:
wrap your template in <ion-page></ion-page> so it would be:
<template>
<ion-page>
...
</ion-page>
</template>

How to get $auth value in component outside of registered vue-router component

I have a layout theme/default which has vue-router inside like this
<template>
<div id="app">
<component :is = "layout">
<router-view></router-view>
</component>
</div>
</template>
<script>
const default_layout = "theme";
export default {
computed: {
layout(){
return ( this.$route.meta.layout || default_layout) + '-layout';
}
},
};
</script>
And then the theme layout is like this:
<template>
<div class="app-home">
<nav-bar/>
<div class="container-fluid section">
<div class="left-fixed">
<side-bar/>
</div>
<div class="right-card">
<slot />
</div>
</div>
</div>
</template>
<script>
import NavBar from './Navbar'
import SideBar from './Sidebar'
export default {
data() {
return {
}
},
mounted(){
},
components: {
NavBar,
SideBar
}
}
</script>
Now I have to pass current auth user in Navbar and Sidebar for logout and current user role which can be obtained from vue-auth $auth but only inside router component. Can anybody help it to fix this.
Using vuex I had made a state which I call as computed property and I set whenever User logged in.

Vue2-leaflet map not showing properly in BoostrapVue modal

Here's my problem - a Vue2 leaflet map does not render correctly in BootstrapVue modal.
Here's what it looks like visually (it should show just the ocean)
<template>
<div>
<b-modal size="lg" :visible="visible" #hidden="$emit('clear')" title="Event details">
<div class="foobar1">
<l-map :center="center" :zoom="13" ref="mymap">
<l-tile-layer :url="url" :attribution="attribution"></l-tile-layer>
<l-marker :lat-lng="center"></l-marker>
</l-map>
</div>
<template slot="modal-footer">
<b-btn variant="danger" #click="deleteEventLocal(event.id)">Delete</b-btn>
</template>
</b-modal>
</div>
</template>
<script>
import * as moment from "moment";
import { LMap, LMarker, LTileLayer } from "vue2-leaflet";
import { deleteEvent } from "./api";
import "vue-weather-widget/dist/css/vue-weather-widget.css";
import VueWeatherWidget from "vue-weather-widget";
export default {
data() {
return {
center: L.latLng(event.latitude, event.longitude),
url: "http://{s}.tile.osm.org/{z}/{x}/{y}.png",
attribution:
'© OpenStreetMap contributors'
};
},
props: {
visible: {
type: Boolean
},
event: {
required: true,
type: Object
}
},
methods: {
async deleteEventLocal(id) {
await deleteEvent(id);
this.$emit("refresh");
this.$emit("clear");
}
},
components: {
weather: VueWeatherWidget,
LMap,
LMarker,
LTileLayer
}
};
</script>
As you can see there aren't any CSS rules that could make the map spill outside the modal as it does. Which is weird.
I'm kinda asking this question to answer it myself as I couldn't find a solution before.
There were 3 issues because of which this was happening.
1. First - I forgot to load the leaflet css into main.js - this is why the leaflet map was somehow outside the modal.
//src/main.js
import '#babel/polyfill';
import Vue from 'vue';
import './plugins/bootstrap-vue';
import App from './App.vue';
import router from './router';
import store from './store';
//above imports not important to this answer
import 'leaflet/dist/leaflet.css'; //<--------------add this line
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app');
2. Now the map may disappear. Set a width and height on the l-map component's container. I used a class but you can use style="" etc.
<div class="foobar1"> <!-- <--- Add a class on l-map's container -->
<l-map :center="center" :zoom="13">
<l-tile-layer :url="url" :attribution="attribution"></l-tile-layer>
<l-marker :lat-lng="center"></l-marker>
</l-map>
</div>
<style lang="scss">
.foobar1 { /* <--- class we added above */
width: 100%;
height: 400px;
}
</style>
3. Now your map will render within the modal but if you move the map's view, you'll see that leaflet does not download the map's squares in time.
You will see something like this:
To fix this:
create an event handler on b-modal for the #shown event.
<b-modal
#shown="modalShown"
#hidden="$emit('clear')"
size="lg"
:visible="visible"
title="Event details"
>
I called mine modalShown.
Then, add a ref attribute to your l-map. I called mine mymap.
<l-map :center="center" :zoom="13" ref="mymap"> <!-- ref attribute added to l-map -->
<l-tile-layer :url="url" :attribution="attribution"></l-tile-layer>
<l-marker :lat-lng="center"></l-marker>
</l-map>
Then, create a modalShown method in the Vue methods for your view/component and call invalidateSize() inside.
export default {
data() {
//some data here
}
methods: {
modalShown() {
setTimeout(() => {
//mapObject is a property that is part of leaflet
this.$refs.mymap.mapObject.invalidateSize();
}, 100);
}
}
}
Now everything should be fine:
map should not spill outside the modal
map should be visible (duh)
map squares should be downloaded when within map body
Here's my full code, it contains some stuff specific to my app but overall it contains all of the code snippets above.
Addtional to Artur Tagisow answer
You can also use this approach to your parent component if your map is in child component.
export default {
data() {
//some data here
}
methods: {
modalShown() {
setTimeout(() => {
window.dispatchEvent(new Event("resize"));
}, 100);
}
}
}
For vue.js and nuxt.js developers , probably it's because of using v-show or v-if
!in your case display none happening by bootstrap modal
but dont worry the only thing u have to do is using client-only (its like ssr but for new version of js frameworks like nuxt or vue):
<client-only>
<div id="bootstrapModal">
<div id="map-wrap" style="height: 100vh">
<l-map :zoom=13 :center="[55.9464418,8.1277591]">
<l-tile-layer url="http://{s}.tile.osm.org/{z}/{x}/{y}.png"></l-tile-layer>
<l-marker :lat-lng="[55.9464418,8.1277591]"></l-marker>
</l-map>
</div>
</div>
</client-only>
ps: if still not loaded in iphone browsers it's probably because of geolocation

Vue.js v-if toggle for show/hide nav

I am trying to make a nav that you can show/hide toggle by clicking on an button. Can anyone tell me what I am doing wrong here? I am still learning Vue so any help would be appreciated. Thanks.
Here's the code from navigation.vue:
<template>
<div>
<nav v-if="seen">
<ul>
<li>front</li>
<li>contact</li>
</ul>
</nav>
<p><button v-on:click="seen = !seen">Toggle</button></p>
</div>
</template>
<script>
export default {
data: {
seen: true
}
}
</script>
Importing the navigation in App.vue:
<template>
<main>
<app-navigation></app-navigation>
<app-front></app-front>
<app-footer></app-footer>
</main>
</template>
<script>
import Navigation from './components/navigation.vue'
import Front from './components/front.vue'
import Footer from './components/footer.vue'
export default {
components: {
'app-navigation': Navigation,
'app-front': Front,
'app-footer': Footer
},
data () {
return {
}
}
}
</script>
With components, your data property needs to be a function.
data(){
return {
seen: true
}
}