Cannot get cookie in vue-router to work. Undefined "this" - javascript

The problem is that I cannot get cookie in vue-router. When I try to do this: this.$cookie.get('token'); I get this undefined. Even though I can use this.$cookie in vue components. This is my code snippet:
Vue-Router:
This is a function to check if I get token from cookie (but it doesnt work):
function isLoggedIn (to, from, next) {
console.log(this.$cookie.get('token'));
}
And this is the route and I use that isLoggedIn in it:
{
path: '/',
name: 'login',
component: Login,
beforeEnter: isLoggedIn()
},
Maybe someone knows how to get that cookie ? It it setted up in vue component correctly just don't know how to pass it to vue router.
EDIT
So by using Vue.cookie.get('token'); I get that next is not a function. This is the current function:
function isLoggedIn (to, from, next) {
if(Vue.cookie.get('token')) {
next({name: 'game'});
} else {
next();
}
}
Okay so when I added this function directly into route like this it worked:
{
path: '/',
name: 'login',
component: Login,
beforeEnter: (to, from, next) => {
if(Vue.cookie.get('token')) {
next('/game');
} else {
next;
}
}
},

If you are using the vue-cookie plugin from npm then you will need to use this syntax outside of components:
function isLoggedIn (to, from, next) {
console.log(Vue.cookie.get('token'));
}
With regards to your next is not a function error, this is because you are invoking your function with no arguments, isLoggedIn(), rather than passing the function reference, isLoggedIn, which will be invoked by the router with the appropriate arguments.
Try this instead:
{
path: '/',
name: 'login',
component: Login,
beforeEnter: isLoggedIn // No parentheses here
},
I hope this helps.

If you look at the typings, you'll see that both to and from are Route's. Here's their interface declaration:
export interface Route {
path: string;
name?: string;
hash: string;
query: Dictionary<string | (string | null)[]>;
params: Dictionary<string>;
fullPath: string;
matched: RouteRecord[];
redirectedFrom?: string;
meta?: any;
}
If look at the Definition of BeforeEnter:
beforeEnter?: NavigationGuard;
If you look at the Definition of NavigationGuard:
export type NavigationGuard<V extends Vue = Vue> = (
to: Route,
from: Route,
next: (to?: RawLocation | false | ((vm: V) => any) | void) => void
) => any
So as we can see, the beforeEnter returns a closure that exposes 3 methods to us: (to, from, next).
As we can see the typings of both to and from are just Route Interfaces and next is a function that we pass arguments into, we can safely determine that the scope of this is indeed, undefined.
So you can either define it in the meta of the Route declaration and access it with this.$meta.cookies, or you can import the cookie package directly and use it with cookies.get('token') or you can augment the return type of the beforeEnter method:
beforeEnter: (to, from, next) => isLoggedIn(to, from, next, $cookies)
You still, cannot, use the in component guards as they do not expose this to you because they execute before the component has been created

Related

How is $route object initialized?

I tried to access this.$route on created() hook, but as I log it to the console it always returns an empty object
{path: '/', name: undefined, params: {…}, query: {…}, hash: '', …}
fullPath: "/"
hash: ""
matched: []
meta: {}
name: undefined
params: {}
path: "/"
query: {}
redirectedFrom: undefined
[[Prototype]]: Object
I guess it is because the loading is asynchronous? Then how can it locate the path from the beginning ??
I tried to do this and it worked. I wonder if anybody could explain what happens under the hood.
localhost:8080/?server=BlackHole
// index.js
{
path: '/network-error',
name: 'NetworkError',
component: NetworkError
},
{
path: '/:server',
},
// App.vue
created() {
setTimeout(() => {
// △ Request to unreachable server
if (this.$route.query.server == 'BlackHole')
{this.$router.push({ name: 'NetworkError' })}}, 1)},
Thanks in advance :)
When you'r on the /network-error route, the app is above that, so you can't say in the app where are you in my app ?
Because your application at this time created and mounted the view app and after go inside the route detected.
The good and normal practice is to fetch every route before like a middleware, inside your router.js or routes/index.js use this function:
router.beforeEach(async (to, from, next) => {
if (to.name === 'BlackHole') {
next({ name: 'NetworkError' })
}
next()
})
This maybe doesn't not exactly match your redirect because i miss some information, but i think you understand what you have to do.
The problem was because the route load is an async process, I found a very quick solution here, just add this line of config to main.js:
router.isReady().then(() => {app.mount('#app')})
https://www.youtube.com/watch?v=a6gT6qHtch8&ab_channel=VueMastery

Angular - Dynamic routing with router param vaiable

I want to do something like this in my routing.module.ts (see lines marked by-->)
export const routes: Routes = [
path: 'test/:action',
component: CreateComponent,
--> canActivate: ':action' == 'read' ? [Guard1] : [Guard1, Guard2],
data: {
--> screenAction: ':action' == 'read' ? 'read' : ':action',
}
]
I have to use the variable :action because it is used later in the router param. Thanks for your help !
Well, what you ask is possible but implementing it might be a little complex.
First of all, you need to define multiple routes instead of 1. Where you can apply the conditions you require.
const routes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'home', component: AppComponent },
{
path: 'dummy1/:action',
component: Dummy1Component,
canActivate: [GuardRotaterGuard],
},
{
path: 'dummyx/:action',
component: Dummy2Component,
canActivate: [Guard1Guard],
},
{
path: 'dummyz/:action',
canActivate: [Guard1Guard, Guard2Guard],
component: Dummy2Component,
},
];
The route dummy1/:action is like a gateway route. Every request to dummy2 component should go from here. And then you need a decider/rotator guard that can decide and rotate the route depending on the route parameters.
It should look like below :
canActivate(
next: ActivatedRouteSnapshot,
statex: RouterStateSnapshot
): Observable<boolean> | Promise<boolean> | boolean {
const { action } = next.params;
const state = { comesFrom: 'rotater' };
console.log("rotater",action);
if (action === 'read') { // you can decide whatever you want here
this.router.navigate(['/dummyx/read'], { state });
}else{
this.router.navigate(['/dummyz', action], { state }); // pass the action to the other route as a parameter
}
return true;
}
And here is a Stackblitz example in action.

Vue, is there a way to pass data between routes without URL params?

I am looking how to pass data secretly between two separate components (not parent and child) without using URL params in my Vue2 app. This doesn't mean I am passing secrets but rather I just dont want the user to see it (only for UI considerations).
I know Vue has Props but they are meant for passing data between parent and child component.
In my case, my URL will change but I don't want to pass data via visible params.
Someone claimed to use props without URL params here but I haven't been able to reproduce a working solution (getting undefined each time).
I also checked out these options but they are all using either URL or query params which as we know are visible.
An ugly solution would be to write the data to local storage and then read it there but this creates a lot of overhead and complexity (like what if I only want this data to be read once, etc).
Is there a more elegant solution to this problem?
Thanks!
make props: true for the destination route -- in the index.js file of router
{
path: '/home',
name: 'home',
component: taskChooser,
props: true,
}
define prop in the component e.g props: ['myprop'], - note the quotes
copy the variable you want to pass from the source route into the same name as your prop - in this case myprop
myprop = theVariableThatYouWantToPass
this.$router.replace({name:'home', params:{myprop}});
Make sure that the name of prop and variable are same - the prop is in quotes.
It's working for me.
Thanks #Mayank for pointing me in the correct direction.
Here is the correct syntax that worked for me.
Notice the props in In router index
{
path: '/componentPath',
name: 'componentName',
props: {
header: true,
content: true
},
}
In the component you are redirecting to, define the props as following:
props: {
myProperty: {
type: <DATATYPE>
},
}
Perform redirect as following:
this.$router.push({
name: 'componentName',
params: {
myProperty: <VARIABLE>
}
})
Access props with the this. convention from created or in later lifecycle event.
In this case, the variable name and property name do not have to be the same as it is a simple map. That naming convention would be a curious design choice anyway.
I haven't tested this in Vue 2, but in Vue 3, you can pass a stringified object through the props when you click on a link:
Add props: true to your routes file, for the route.
{
path: 'receipt',
name: 'receipt',
component: () => import('../pages/Receipt.vue'),
props: true,
beforeEnter(to, from, next) {
if (!to.params.receiptData) {
return next({
name: 'dashboard',
params: {
locale: from.params.locale ? from.params.locale : 'en',
},
});
}
return next();
},
},
Include your stringified object as a param for router.push().
const receiptData = {
transferType: 'default',
recipient: receiver.value.name,
referenceNumber: '#B3423424234',
amountSent: formAmount,
transferFee: 0,
};
router.push({
name: 'receipt',
params: {
receiptData: JSON.stringify(receiptData),
},
});
Declare the props as instance data in the component.
<script setup>
import { computed } from 'vue';
const props = defineProps({
receiptData: {
type: String,
required: true,
},
})
console.log('receiptData', props.receiptData);
const parsedReceiptData = computed(() => JSON.parse(props.receiptData));
</script>
I haven't tested an upper limit for size, so be careful about passing a huge object through, and you'll notice I showed a beforeEnter middleware on the route too because, if the user presses F5 to refresh the page, the props will be lost, so in my case, I redirect the user away from the page because the receipt is for one time use only.

How to get data before Router is loaded after Auth Service using angular 2+?

I hope this is not duplicated.
I have this component to Login with a user, that calls a service and gives me a Token.
When the login is success I save the token to localStorage and I get the user Data with getUserData service and save it to a localStorage with saveUderData service.
onLoginSuccess(token: Token) {
this.authService.saveToken(token);
this.authService.getUserData().subscribe(
userData => this.onGetUserDataSuccess(userData)
);
}
onGetUserDataSuccess(userData: UserDataModel) {
this.authService.saveUserData(userData);
this.router.navigate(['/'])
}
The problem is that when I go to the main component the data is not
loaded and I have to refresh the page.
I have this in my main component.
if (localStorage.getItem('_UserToken') && localStorage.getItem('_UserData') && (!this.authService.userToken || !this.authService.userToken.access_token)) {
this.authService.saveToken(JSON.parse(localStorage.getItem('_UserToken')));
this.authService.saveUserData(JSON.parse(localStorage.getItem('_UserData')));
this.userData = this.authService.userData;
}
with this.userData I get the data.
I solved it in another component with Resolve but here I can't do it because i don't know how.
I tried this in onLoginSuccess(token: Token)
this.route.data.subscribe(
userData => console.log(userData['user'])
);
but the userData is undefined
here my routes.
const appRoutes: Routes = [
{path: '', canActivate: [AuthGuardService], data: { title: 'Inicio'}, resolve: {user: UserDataResolve},
children: [
{path: '', component: HomeComponent},
{path: '', component: MenuComponent},
]},
{path: 'login', component: LoginComponent, data: { title: 'Iniciar Sesión'} },
];
and here is my Resolve
#Injectable()
export class UserDataResolve implements Resolve<UserDataModel> {
constructor(private authService: AuthService) {}
resolve(route: ActivatedRouteSnapshot): Observable<UserDataModel> {
return this.authService.getUserData();
}
}
I hope you can understand me, my english is not the best. thanks =).
Try using a getter instead of a simple property in your component:
get userData(): string {
this.authService.userData;
}
That way any time changes are detected it will get the current userData from your service.

loadChildren syntax - what is hash part

Angular in the docs says the following:
{
path: 'admin',
loadChildren: 'app/admin/admin.module#AdminModule',
},
I'm curious about the syntax. As I understand, this part:
app/admin/admin.module
defines the path to load a module. But what is this #AdminModule?
I'm reading this article, and there is the following path:
loadChildren: 'contacts.bundle.js',
so, as you can see, there is nothing with hash.
The hash part denotes the exported module name. So, inside app/admin/admin.module the AdminModule is exported:
export class AdminModule {}
However, if default export is used, there is no need to use hash.
Here is the relevant part from the sources system_js_ng_module_factory_loader.ts:
private loadAndCompile(path: string): Promise<NgModuleFactory<any>> {
let [module, exportName] = path.split(_SEPARATOR);
if (exportName === undefined) exportName = 'default';
return System.import(module)
.then((module: any) => module[exportName])
.then((type: any) => checkNotEmpty(type, module, exportName))
.then((type: any) => this._compiler.compileModuleAsync(type));
}

Categories

Resources