I'm using framework7 with vuejs i.e framework7-vue. Everything is working good. When doing the routes for browser i used pushState="true" and pushStateSeperator="", This removes the "#!" from the url bar but the problem is when i visit the url lets say localhost:8080/about it gives me an error cannot get /about.
Now if i don't set pusStateSeperator to " ", it would work fine the url would turn to http://localhost:8080/#!/about and now when i hit the same url from browser directly the page loads without error.
So any solution how to remove the "#!" and make links work ?
i want my link to work like this localhost:8000/about and if i reload it should not give me error "Cannot get /about"
routes.js
import HomePage from './pages/home.vue';
import AboutPage from './pages/about.vue';
import TermPage from './pages/terms.vue';
import FormPage from './pages/form.vue';
import DynamicRoutePage from './pages/dynamic-route.vue';
import NotFoundPage from './pages/not-found.vue';
import PanelLeftPage from './pages/panel-left.vue';
import PanelRightPage from './pages/panel-right.vue';
import ViewSchool from './pages/school/viewschool.vue'
export default [
{
path: '/',
component: HomePage,
},
{
path: '/panel-left/',
component: PanelLeftPage,
},
{
path: '/panel-right/',
component: PanelRightPage,
},
{
path: '/about',
component: AboutPage,
},
{
path: '/terms/',
component: TermPage,
},
{
path: '/form/',
component: FormPage,
},
{
path: '/dynamic-route/blog/:blogId/post/:postId/',
component: DynamicRoutePage,
},
{
path: '/viewschool/:school_id',
component:ViewSchool,
props:true,
},
{
path: '(.*)',
component: NotFoundPage,
},
];
app.js
// Import Vue
import Vue from 'vue';
// Import F7
import Framework7 from 'framework7/framework7.esm.bundle.js';
// Import F7 Vue Plugin
import Framework7Vue from 'framework7-vue/framework7-vue.esm.bundle.js';
// Import F7 Styles
import Framework7Styles from 'framework7/css/framework7.css';
// Import Icons and App Custom Styles
import IconsStyles from './css/icons.css';
import AppStyles from './css/app.css';
// Import App Component
import App from './app.vue';
// Init F7 Vue Plugin
Framework7.use(Framework7Vue)
// Init App
new Vue({
el: '#app',
template: '<app/>',
// Register App Component
components: {
app: App
}
});
app.vue
<template>
<!-- App -->
<f7-app :params="f7params">
<!-- Statusbar -->
<f7-statusbar></f7-statusbar>
<!-- Left Panel -->
<f7-panel left reveal>
<f7-view url="/panel-left/"></f7-view>
</f7-panel>
<!-- Right Panel -->
<f7-panel right cover theme-dark>
<f7-view url="/panel-right/"></f7-view>
</f7-panel>
<!-- Main View -->
<f7-view id="main-view" url="/" main ></f7-view>
<!-- Popup -->
<f7-popup id="popup">
<f7-view>
<f7-page>
<f7-navbar title="Popup">
<f7-nav-right>
<f7-link popup-close>Close</f7-link>
</f7-nav-right>
</f7-navbar>
<f7-block>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Neque, architecto. Cupiditate laudantium rem nesciunt numquam, ipsam. Voluptates omnis, a inventore atque ratione aliquam. Omnis iusto nemo quos ullam obcaecati, quod.</f7-block>
</f7-page>
</f7-view>
</f7-popup>
<!-- Login Screen -->
<f7-login-screen id="login-screen">
<f7-view>
<f7-page login-screen>
<f7-login-screen-title>Login</f7-login-screen-title>
<f7-list form>
<f7-list-item>
<f7-label>Username</f7-label>
<f7-input name="username" placeholder="Username" type="text"></f7-input>
</f7-list-item>
<f7-list-item>
<f7-label>Password</f7-label>
<f7-input name="password" type="password" placeholder="Password"></f7-input>
</f7-list-item>
</f7-list>
<f7-list>
<f7-list-button title="Sign In" login-screen-close></f7-list-button>
<f7-block-footer>
<p>Click Sign In to close Login Screen</p>
</f7-block-footer>
</f7-list>
</f7-page>
</f7-view>
</f7-login-screen>
</f7-app>
</template>
<script>
// Import Routes
import router from './routes.js'
export default {
data() {
return {
// Framework7 parameters here
f7params: {
id: 'io.framework7.testapp', // App bundle ID
name: 'Framework7', // App name
theme: 'auto', // Automatic theme detection
// App routes
routes: router,
view:
{
pushState:"true",
}
},
}
}
}
</script>
The first link where #! is coming and things work fine even when i refresh page
Now this happens when i set pushStateSeperator="", now when u refresh the link you get this error.
I don't know if you have found the solution already. For anyone else looking for this:
if your app lies at https://example.com/myapp
<f7-view id="main-view" main url="/"
:push-state="true"
push-state-separator="/myapp"
></f7-view>
push-state-separator is the key here whose default value is #!/
Try specifying the router mode to 'history';
let router = new Router({
mode: 'history',
routes: [
{
name: 'About',
path: '/about',
component: About
}
]
})
Related
I'm stuck on getting my Vue-router to work as I want. I have two <route-view /> Named Views in my templates. One is the main view for navigation on the top nav bar, which is working fine. The other doesn't work. I have defined my router as such (shortened for clarity):
import { createRouter, createWebHistory } from 'vue-router';
import HomeView from '../views/HomeView.vue';
const routes = [
{
path: '/',
name: 'home',
components: { main: HomeView },
},
{
path: '/bpvapp',
name: 'bpvapp',
components: { main: () => import('../views/BpvApp.vue') },
},
{
path: '/bpvapp/projects',
name: 'projects',
components: { bpvapp: HomeView }, // HomeView component for testing purposes
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;
My App.vue has the main route defined in the template:
<template>
<div class="main-container">
<navigation-bar
:isLoggedIn="isLoggedIn"
:profileImageUrl="userImage"
:signOut="signOut"
/>
<suspense>
<router-view name="main" />
</suspense>
</div>
</template>
My BpvApp.vue component, which is a CSS Grid layout, uses the second route view (shortened for clarity):
<template>
<div class="bpv--app-container">
<div class="bpv--app-toolbar">TOOLBAR</div>
<div class="bpv--app-sidebar">
<bpv-button
text="Projects"
icon="folder-star-multiple"
styling="info"
class="bpv--bs-1"
:active="activeSection === 'projects'"
to="/bpvapp/projects"
/>
</div>
<div class="bpv--app-content">
<suspense>
<router-view name="bpvapp" />
</suspense>
</div>
<div class="bpv--app-assist">
RIGHT Lorem ipsum dolor sit amet consectetur, adipisicing elit. Laboriosam
placeat deserunt quidem fugiat dicta repellat nobis mollitia! Consectetur
laudantium dolor, odio adipisci at qui, deserunt minus facere rerum,
voluptates maiores.
</div>
<div class="bpv--app-footer">FOOTER</div>
</div>
</template>
The div with class bpv--app-content should show the content from the component I push to the router. You can see the button will push to /bpvapp/projects, which in my router definition should invoke the router with name bpvapp and show the content.
For the sake of completeness, my button component handles the to prop as follows:
...
methods: {
// this.to has the correct value '/bpvapp/projects', I double-checked
buttonClicked(e) {
if (this.to) {
this.$router.push(this.to);
} else if (this.action) {
this.action();
} else {
this.$emit('click', e);
}
},
},
...
What happens now is that I get a blank screen. When I inspect the HTML, the entire structure is not there, except the nav bar.
What am I doing wrong here? I've tried removing the <suspense> tag and loading static content, but that made no difference.
One issue is the two routes are configured as siblings, but the second one should be a nested child route of the first, using the route's children option:
// router.js
const routes = [
{
path: '/',
name: 'home',
components: { main: HomeView },
},
{
path: '/bpvapp',
name: 'bpvapp',
components: { main: () => import('#/views/BpvApp.vue') },
👇
children: [
{
path: 'projects',
name: 'projects',
components: { bpvapp: () => import('#/views/BpvProject.vue') },
},
],
},
]
Note: When there's only one router-view at a particular level, you don't need to name it.
The other issue is you don't need <suspense> for this. <suspense> is intended for async components declared with an async setup() option or within defineAsyncComponent() (which is not appropriate for a route's components option). While the router components are technically loaded asynchronously via the dynamic imports, they're not actually the async components that <suspense> expects.
demo
I am not exactly sure how to phrase this question. I am attempting to port one of my Vue2 global components to Vue3 (Options API). In short, I am trying to mount a custom dialog component globally, i.e. creating it in the boot folder (And have added it to quasar.conf.js boot section) and then be able to call it in any of my Vue files in my project, without having to import it in every vue file i.e. similar to this.$axios.
The original plug-ins file using an earlier version of Vue was similar to
import Vue from 'vue';
import dialog from '#/components/dialog.vue';
export default ({ app }, inject) => {
const $dialog = Vue.extend( dialog );
const vm = new $dialog({ i18n: app.i18n }).$mount();
document.body.appendChild(vm.$el);
inject('dialog', vm);
};
I found my answer to work around the deprecation of Vue.extend here and used a component that implements the suggested solution by pearofducks on github, which appears to mount the component correctly.
Back to my boot file
boot_dialog.js
import { boot } from 'quasar/wrappers'
import { mount } from 'mount-vue-component'
import customDialog from '../~global/scripts/debug/debug'
export default boot( async ({ app }) => {
const element = document.createElement('div');
element.setAttribute('id','modal-dialog-div');
document.body.appendChild(element);
const { vNode, destroy, el } = mount(customDialog, { app, element, props: { i18n: app.i18n }});
app.config.globalProperties.$dialog = customDialog;
// ^^^ Believe the error is here
});
I know that the component gets mounted as on mount in debug.vue I call the this.show() which displays the q-dialog correctly.
devug.vue
<template>
<q-dialog v-model="alert">
<q-card>
<q-card-section>
<div class="text-h6">Alert test</div>
</q-card-section>
<q-card-section class="q-pt-none">
Lorem ipsum dolor sit amet
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="OK" color="primary" v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script>
export default {
data () {
return {
alert: false,
}
},
methods: {
show (params) {
console.log('Show method called',params);
this.alert = true;
},
},
mounted() {
this.show('Parameters');
}
};
</script>
But calling this.$dialog.show('A sample parameter') in any of my main project vue files causes an error. How do I work back from the vNode const to an actual component which I can call in any of the projects vue files ?
I think what you want is a custom plugin:
https://v3.vuejs.org/guide/plugins.html#writing-a-plugin
I want to exclude the h1 tag every time I go to different route in my Vue application.
Here's my app.vue:
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<h1>LANDING PAGE</h1>
<router-view></router-view>
<!--Path for login.vue-->
<button #click="$router.push('/login')">LOGIN</button>
</div>
</template>
<script>
export default {
name: 'app',
}
</script>
And here's my login page, where I want only to display my design for login page only:
<template>
<div>
<h1>Login Page</h1>
</div>
</template>
<script>
export default {
name: "Login"
}
</script>
My route.js:
import Login from './components/LandingPage/Login';
import Register from './components/LandingPage/Register';
export default [
{
path: '/login', component: Login
},
{
path: '/register', component: Register
}
]
and lastly my main.js:
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import Routes from './routes';
import VueResource from 'vue-resource'
Vue.config.productionTip = false;
Vue.use(VueRouter);
Vue.use(VueResource);
const router = new VueRouter({
routes: Routes,
/* To remove # in the URL */
mode: 'history'
});
new Vue({
render: h => h(App),
router: router
}).$mount('#app');
I didn't include the register.vue because it's just the same with login.vue.
Conditional rendering based on the landing page url:
<h1 v-if="$route.path === '/landing-page-url'">LANDING PAGE</h1>
Actually, there are three approaches to solve your problem:
Just drop your <h1></h1> into your landing page component.
You can use conditional rendering, like Psidom answered (just changed path to name):
<h1 v-if="$route.name === 'Landing'">Landing page</h1>
You can have only one <h1></h1> in your main layout, and render current page title dynamically. Route Meta Fields come in rescue.
import Login from './components/LandingPage/Login'
import Register from './components/LandingPage/Register'
export default [
{
path: '/login',
component: Login,
meta: {
title: 'Login',
},
},
{
path: '/register',
component: Register,
meta: {
title: 'Register',
},
},
]
And then in your template:
<h1>{{ $route.meta.title }}</h1>
P.S. To navigate to another route in your template use <router-link></router-link> instead of button with click event.
I'm learning VueJs,and i want to develop a register page, but I think something is wrong here, I have a click event to submit the data and when I click in the button to submit it, I print the data in the console, and what I have noticed is the query string is in the middle of hostname and path name, and I dont know if that is normal.
This is how URL looks like:
http://localhost:8080/?email=asas%40mail.com&password=asdsa#/register
This is some of my code:
router.js
import Vue from 'vue'
import Router from 'vue-router'
import home from './components/home'
import register from './components/register'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: home
},
{
path: '/register/',
name: 'register',
component: register
}
]
})
Register.vue
<template>
<div>
<form>
<input type="text" name="email" placeholder="Email" v-
model="email">
<input type="password" name="password" placeholder="Password"
v-model="password">
<button v-on:click="clickRegister">Submit form</button>
</form>
</div>
</template>
<script>
export default {
data(){
return{
email:'',
password:''
}
},
methods:{
clickRegister: function() {
console.log(this.email)
}
}
}
</script>
<style scoped>
</style>
App.vue
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './registerServiceWorker'
Vue.config.productionTip = false
new Vue({
router,
render: function (h) { return h(App) }
}).$mount('#app')
This is a normal behavior as the default behavior for a SPA in Vue is to use the # to route your application.
The query string is actually not before your path, it's shown before the anchor which contains a path.
You can get rid of the # and rely on the browser history instead and therefore use a normal path by setting your router to use the history mode :
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'home',
component: home
},
{
path: '/register/',
name: 'register',
component: register
}
]
})
Source : https://router.vuejs.org/guide/essentials/history-mode.html
I have a form divided in 5 components and the user can navigate through them via steppers (I'm using vue-material for my project). I use vue-router for that. However, I'm having a serious issue here: components lose all the information in the store (I'm using vuex) when they come back to a route they already filled. So to make it clear: if a user fills the first step of the form and then goes to step two, when he wants to come back to step one data is no longer available and the form is totally empty (and the state in vuex is also reset). What am i doing wrong?
import Vue from 'vue'
import Router from 'vue-router'
import Projet from '#/components/Fiches/Projet/Projet'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Projet
},
//other routes here
]
})
And this is the html code
<template>
<div class="project-steppers">
<md-steppers md-dynamic-height md-alternative>
<md-step id="first" to="/Projet" md-label="Projet" />
// other steps here
</md-steppers>
</div>
</template>
And an example of one of the inputs I use:
<md-field>
<label for="project-name">Nom du projet</label>
<md-input id="project-name"
v-model="project.projectName"
name="project-name"
#change="updateProjectName"/>
</md-field>
[...]
methods: {
updateProjectName () {
this.$store.commit(projectStore.MUTATE_PROJECTNAME, this.project.projectName)
}
More information: when I fill the different inputs I see that the store is updated with the new values, so the mutation is working.
First of all, Vuex does not store data in the browser - just in memory. That means that you could either install a third party plugin such as vuex persisted state or write your own methods to set and get the items from your storage, e.g.:
const storage = localStorage.getItem('key');
new Vuex({
state: {
yourProp: storage ?
? JSON.parse(storage.yourDataKey)
: 'default-value'
},
actions: {...}
mutations: {...}
})
I think to should use router-link or $router.push().
Vue:
import Vue from 'vue'
import Router from 'vue-router'
import Projet1 from '#/components/Fiches/Projet/Projet1'
import Projet2 from '#/components/Fiches/Projet/Projet2'
import Projets from '#/components/Fiches/Projet/Projet' //with props
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Projet1 // default project
},
{
path: '/Projet1', // url for the same component
name: 'Projet1',
component: Projet1
},
{
path: '/Project2',
name: 'Projet2', // url for the another component
component: Projet2
},
{
path: '/Project/:id',
name: 'Projets', // url for a component with props
component: Projet,
props: true
}
]
})
HTML: A way to call Projet without reloading with router-link
<template>
<router-link to="/Home"></router>
<router-link to="/Projet1"></router>
<router-link to="/Projet2"></router>
</template>
js: I would add a router push
updateProjectName () {
this.$store.commit(projectStore.MUTATE_PROJECTNAME, this.project.projectName)
this.$router.push('/' + this.project.projectName)
}
Your question looks like the issue opened by kristianmandrup:
menu or tabs with router links!?