Angular2.0 router works for components not modules? - express

I am trying to modify an existing angular app to fit into the structure of this Starter Project. Anyway, so I have my app module with a submodule (tutorial). Which looks like this:
When landing on the root domain and then navigating with the router links to http://localhost:3000/tutorial/chapter/0, everything works fine. However, if I refresh the page or try to go directly to that link, I get the error:
Unhandled Promise rejection: Template parse errors:
'my-app' is not a known element:
1. If 'my-app' is an Angular component, then verify that it is part of this module.
2. If 'my-app' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '#NgModule.schema' of this component to suppress this message. ("
<body>
[ERROR ->]<my-app>
<div class="valign-wrapper">
<div class="preloader-wrapper active valign"): a#30:4 ; Zone: <root> ; Task: Promise.then ; Value: Error: Template parse errors:(…) Error: Template parse
So I believe this is happening because rather than that url linking to the appcomponent, with the tutorial submodule as a child, it's just linking to the TutorialModule, and then the <my-app></my-app> tags from the index.html aren't recognised. This worked before, so I am not sure what aspect of this new configuration has broken this.
Here is my app.routes.ts:
import { homeComponent } from './components/home/home.component';
import { pluginsComponent } from './components/plugins/plugins.component';
import { Routes, RouterModule } from '#angular/router';
const appRoutes: Routes = [
{ path: '', component: homeComponent },
{ path: 'tutorial', loadChildren: 'tutorial/tutorial.module', pathMatch: 'prefix'},
{ path: 'plugins', component: pluginsComponent }
];
export const appRoutingProviders: any[] = [];
export const routing = RouterModule.forRoot(appRoutes);
and my tutorial.routes.ts:
import { Routes, RouterModule } from '#angular/router';
import { tutorialComponent } from './tutorial.component';
import { chapterComponent } from './chapter/chapter.component';
const tutorialRoutes: Routes = [
{
path: 'tutorial',
component: tutorialComponent,
children: [
{ path: 'chapter/:id', component: chapterComponent },
{ path: '', redirectTo: 'chapter/0', pathMatch: 'full'},
]
}
];
export const tutorialRouting = RouterModule.forChild(tutorialRoutes);
finally in my app.ts where I define the express routes I have:
app.all(/^\/tutorial$/, (req, res) => {
res.redirect('/tutorial/');
});
app.use('/tutorial/', (req, res) => {
res.sendFile(resolve(__dirname, '../public/index.html'));
});
to serve the angular index for the tutorial component.
The whole repo is here

The issue was the index.html file, I had <base href="."> where it should have been <base href="/">. I have a bug report here

#micronyks already said this in the comment but what you need to do a setup your web server to redirect all requests to your index.html page. Then your app will load and navigate to your "deep" link.

Related

Vue.js (v3) use error component without changing URL

I've been searching and tinkering for a while now without any luck. I'm looking to be able to catch errors and show an "Oops" page. Just to be clear, this isn't about 404 pages (which work fine).
I've reduced this for simplicity but in the below, before a page loads, it "attempts something which may fail". When that fails, I navigate to /error which shows an error page:
const router = createRouter({
routes: [
{
path: '/',
component: Index
},
{
path: '/something',
component: Something
},
{
path: '/error',
component: Error
},
{
path: '/:catchAll(.*)',
component: NotFound
}
]
})
router.beforeEach(async (to, from, next) => {
// attempt something which may fail
})
router.onError(() => router.push('/error'))
This all works fine, but it means that the /error path is navigable, and that a path change occurs. What I'd prefer is a way to be able to show an error component (Error) if an error occurs while keeping the url path the same.
Say I was on / and then I navigated to /something but "something failed", the url path would equal /something but the Error component would be used, rather than the Something component.
Any ideas? It seems like this should be manageable but so far I'm coming up blank.
Thanks
As Duannx commented above, a solution to this was to use a state to control this. Here's how I did it:
I created a Pinia store to hold an error variable:
import { defineStore } from 'pinia'
export const useGlobalStore = defineStore({
id: 'global',
state: () => ({
_error: false
}),
getters: {
error: (state) => state._error
},
actions: {
async setError(value) {
this._error = !!value
}
}
})
In the router, I catch any errors:
import { createRouter, createWebHistory } from 'vue-router'
import { useGlobalStore } from '../stores/global'
import routes from './routes'
const history = createWebHistory(import.meta.env.BASE_URL)
const router = createRouter({ history, routes })
router.beforeEach(async (to, from, next) => {
const globalStore = useGlobalStore()
try {
// Things which may fail here
next()
} catch (error) {
globalStore.setError(true)
return next()
}
})
export default router
In the root component (App.vue), I then check for the error state, and either show that or the router view based on whether or not the error state is set the true.
<template>
<Error v-if="globalStore.error" />
<RouterView v-else />
</template>
This works great, thanks Duannx.

How to setup `vite-plugin-pages`, cannot find module `~pages`

I'm trying to set up file system based routing for a Vue 3 application using Vite with the help of vite-plugin-pages.
I created the project using yarn create vite with vue-ts as the options and added the plugin via yarn add vite-plugin-pages --dev, yarn add vue-router.
Following the readme on the github, I have added the following to my vite.config.ts:
import Pages from 'vite-plugin-pages'
export default {
plugins: [
// ...
Pages(),
],
}
However, at the next step, in main.ts:
import { createRouter } from 'vue-router'
import routes from '~pages'
const router = createRouter({
// ...
routes,
})
I cannot seem to import from ~pages. I cannot find the module. vue-router itself is working fine, as I can create a router fine, declaring the routes myself. In a vite template, they seem to be using import routes from 'virtual:generated-pages' instead and I have no idea how either works.
So, the question is, how would I go about generating the dynamic routes and as a whole, set up the usage of vite-plugin-pages?
You can try like this:
import Pages from "vite-plugin-pages"
export default defineConfig({
plugins: [
Pages({
pagesDir: [
{dir: 'src/pages', baseRoute: ''},
],
extensions: ['vue'],
syncIndex: true,
replaceSquareBrackets: true,
extendRoute(route) {
if (route.name === 'about')
route.props = route => ({query: route.query.q})
if (route.name === 'components') {
return {
...route,
beforeEnter: (route) => {
// eslint-disable-next-line no-console
// console.log(route)
},
}
}
},
}),
],
});
Then in main.js
import { createRouter, createWebHistory } from 'vue-router';
import generatedRoutes from 'virtual:generated-pages';
const router = createRouter({
history: createWebHistory(),
routes: generatedRoutes,
});
You can also declare the reference in any of your type declarations.
/// <reference types="vite-plugin-pages/client" />
// tsconfig.json
"compilerOptions": {
...
"types": ["vite-plugin-pages/client"]
}

Process 404 page when there is no parameter in Vue

Dynamic routing is in use.
If there is no device data in vuex, I want to go to 404 page.
How should I implement it?
router/index.js
const routes = [
{
path: '/',
name: 'Main',
component: Main
},
{
path: '/:device',
name: 'Detail',
component: Detail,
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: NotFound
},
]
When the device-detail page is implemented as follows, it does not move to the 404 page.
const deviceName = route.params.device
const storedDeviceList = computed(() => store.state.stationName)
if (!storedDeviceList.value.includes(deviceName)) {
router.push({
name: 'NotFound'
})
}
I think the first problem is, that you declare router two times in your project, according to your github repo. You declared your routes in your router/index.js and imported it into your main.js. So importing it again in About.vue from vue-router instead of router.js causes, that this instance has no routes. The second problem is the same with your store, as you import store/index.js to your main.js but import a new instance from vuex to your About.vue.
If you would use the composition API, you could call the already in main.js imported modules with this, like:
this.$router.push({
name: 'NotFound'
})
You also would get your states from your store like this:
this.$store.state.stationName
So, in composition API, use something like this in your About.vue:
<script>
export default {
methods: {
checkDevice() {
if (!this.$store.state.deviceList.includes(this.$route.params.device)) {
this.$router.push({
name: 'NotFound'
})
}
}
},
created() {
this.checkDevice()
}
}
</script>

router is undefined in vuejs

I create done vue js app. In which i have main index.js file for routes and i made different route file for other view and all my child file extend in main index.js route file.
index.js (Main route file)
Below I import my child routes in this file
import test1 from './test1'
import test from './test'
My child route file test1
export default [{
path: '/roles',
component: Layout2,
children: [{
path: '/',
component: () => import('#/views/test/test1'),
meta: {
auth: true
},
beforeEnter(to, from, next) {
if(checkPermission("readUser")){
router.push({
name: 'unauthorized'
})
}
}
}
}]
}]
Now issue is i am trying push unauthorized in url by using before route, but it gives me error like router is not defined. How can i solve this issue?
I think you should creater router instance first, and export it.
export const router = new VueRouter(routes)
const routes = {
path: '/roles',
component: Layout2,
... etc.
}
Then import router into main js file.

Vue Router, refresh shows blank page

Im trying to figure out why Vue.js routing, after I refresh my admin page re-directs back to the home component, only showing a blank page, and after a second refresh shows the home component again. I am still logged in as I can still go directly with the url to my admin-page. Meaning the session is still active. Is there a way to force the page to stay on the admin home page when I press F5? I tried things like history mode etc, but cant figure it out.
This is my router.js layout in the root and alos my main router file
import Vue from 'vue'
import Router from 'vue-router'
import firebase from "firebase/app";
import Home from './views/user/Home.vue'
import adminRoute from "./components/routes/admin-routes";
Vue.use(Router);
const router = new Router({
routes: [
{
path: '*',
redirect: '/',
},
{
path: '/',
name: 'home',
component: Home,
meta: {
requiresAuth: false,
}
},
...adminRoute
],
});
router.beforeEach((to, from, next) => {
if (to.matched.some(r => r.meta.requiresAuth === false)) {
next()
} else if (!firebase.auth().currentUser) {
next(false)
} else {
next()
}
});
export default router
and I have my admin-routes.js
import AdminHome from "../../views/admin/AdminHome";
import Users from "../../views/admin/Users";
const adminRoute = [
{
path: '/admin-home',
name: 'AdminHome',
component: AdminHome,
meta: {
requiresAuth: true
},
},
{
path: '/users',
name: 'Users',
component: Users,
meta: {
requiresAuth: true,
}
}
];
export default adminRoute;
I do want to mention that my main page is under views/user/Home.vue and my AdminHome page is views/admin/AdminHome.vue
This problem is return to the server not the built app
you have to manage your 404 not found root
Here you can find the solution
and if you work With Netlify you just create a file in the root of the project ( same place as package.json) name netlify.toml containing
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
in vue4 they did away with the 'next' argument(see here), this is working for me to send people back to the login page, where I'm using firebaseui, I have a simple boolean flag in my vuex store named "isAuthenticated" that I flip on when a user signs on.
NOTE: the following is a boot file for quasar v2 but the logic will
work where ever you have access to the store
import { computed } from 'vue'
export default ({ app, router, store }) => {
console.log('nav.js:')
const isAuthenticated = computed(() => store.state.auth.isAuthenticated)
console.log('isAuthenticated',isAuthenticated.value)
router.beforeEach((to, from) => {
console.log('nav.js:beforeEach:to',to)
console.log('nav.js:beforeEach:from',from)
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!isAuthenticated.value) {
console.log('nav.js:beforeEach:to.redirect')
return '/login'
}
}
})
}