Angular 14 migration: default routes and endless loops - angular14

I have the following configuration:
In the app.routes.ts
export const routes: Routes = [
{
path: '', pathMatch: 'full', redirectTo: environment?.firstTab ? environment.firstTab : 'releases',
data: {isInMenuBar: false}
},
{
path: 'login', component: LoginComponent,
data: {isInMenuBar: false, allowed: false}
},
....
}
and for the CanActivate:
#Injectable()
export class CanActivateMyApp implements CanActivate {
constructor(private router: Router) { }
canActivate(
route: ActivatedRouteSnapshot,
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
if (this.userInfo) {
/*
* check further access
*/
}
return this.router.parseUrl('/');
}
Up till now (=Angular 13) this approached worked. If I don't have any kind of access ==> return to the top level path (=/) and continue the work. With Angular 14 this causes an endless loop.
Changing to return true or false works but I wonder if the approach is correct.
==> what is the correct way to handle the situation? Or did I miss something?

Related

How to fix NavigationDuplicated error in Internet Explorer?

I am developing a "Vue" application that consists of a form to make a purchase.
In all the browsers it makes me the complete cycle without any problem, managing to make the "post" at the end of the form.
On the other hand, when I try to do the flow in Internet Explorer, after filling in the last step of the form, it redirects to the next page but does not load the component, returning the error "Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: "/devis/resume".
this is my router
import Vue from 'vue'
import Router from 'vue-router'
import Devis from '../components/devis/devis.vue'
import Animal from '../components/devis/animal.vue'
import Create from '../components/devis/create.vue'
import Resume from '../components/devis/resume.vue'
import Service from '../components/devis/service.vue'
import Geo from '../components/extern/geolocalisation.vue'
import Message from '../components/extern/servicenotavailable.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
component: Devis, children: [
{
path: '/',
component: Animal
}
]
},
{
path: '/devis',
component: Devis, children: [
{
path: '/',
component: Animal
},
{
path: 'create',
component: Create
},
{
path: 'resume',
component: Resume
},
{
path: 'service',
component: Service
}
]
},
{
path: '/geo',
component: Geo
},
{
path: '/message',
component: Message
}
],
mode: 'history',
scrollBehavior() {
document.getElementById('app').scrollIntoView();
}
})
and here is the point at which I redirect from create component to the resume component
checkForm() {
if (
this.ownerFormInfo.civility !== "" &&
this.ownerFormInfo.firstName !== "" &&
this.ownerFormInfo.lastName !== "" &&
this.ownerFormInfo.adresse1 !== "" &&
this.ownerFormInfo.city !== "" &&
(this.ownerFormInfo.postalCode === "" ||
(this.ownerFormInfo.postalCode !== "" &&
this.validatePostalCode(this.ownerFormInfo.postalCode))) &&
(this.ownerFormInfo.phone === "" ||
(this.ownerFormInfo.phone !== "" &&
this.validatePhone(this.ownerFormInfo.phone))) &&
(this.ownerFormInfo.email === "" ||
(this.ownerFormInfo.email !== "" &&
this.validateEmail(this.ownerFormInfo.email)))
) {
this.onSubmit();
}
},
onSubmit() {
const formData = {
civility: this.ownerFormInfo.civility,
firstName: this.ownerFormInfo.firstName,
lastName: this.ownerFormInfo.lastName,
adresse1: this.ownerFormInfo.adresse1,
adresse2: this.ownerFormInfo.adresse2,
adresse3: this.ownerFormInfo.adresse3,
city: this.ownerFormInfo.city,
postalCode: this.ownerFormInfo.postalCode,
phone: this.ownerFormInfo.phone.indexOf('0') == 0 ? this.ownerFormInfo.phone.replace('0', '+33') : this.ownerFormInfo.phone,
email: this.ownerFormInfo.email
};
const owner = {
ownerCivility: formData.civility,
ownerLastname: formData.lastName,
ownerFirstname: formData.firstName,
ownerAddressFirstLine: formData.adresse1,
ownerAddressSecondLine: formData.adresse2,
ownerAddressThirdLine: formData.adresse3,
ownerPostalCode: formData.postalCode,
ownerCity: formData.city,
ownerPhone: formData.phone,
ownerEmail: formData.email,
country: "FR"
};
this.$store.dispatch("formOwnerStepInfo", formData);
const token = localStorage.getItem("token");
let config = {
headers: {
Authorization: "Bearer " + token
}
};
globalAxios
.post("/api/fr/estimations", owner, config)
.then(res => {
if (res.data.functional_id) {
this.$store.dispatch("setFunctionalId", res.data.functional_id);
}
})
.catch(error => console.log(error));
this.navigateToResume();
},
navigateToResume() {
this.$store.dispatch("setStep", this.step + 1);
this.$router.push("/devis/resume");
},
How can it be that in the rest of the browsers it works correctly?
What am I doing wrong?
I've been looking for information but I can't find a way to fix the error or reference it as being due to Internet Explorer.
Greetings and thank you all for your time and help in advance
I found several threads, thanks to the help of Yu Zhou in comments, referring to similar problems caused by the vue-router version. All of them suggest using the vue-router below version 3.0.
In my case I first lowered it to 2.8 and there was no difference , but then I lowered it to 2.6 and the problem was solved.

TypeError: e is not a function

Good day.
When running my app i get the following error related to my vue-router:
TypeError: e is not a function
however i have nothing ... i simply named "e" in my code.
I have a few before each options but nothing too big besides a few cookies deletions.
I have a few imports of apps and i am trying to use them in my router and they all work. So does the cookies. I did the beforeEach() method a few times to see if the error was there but so far no luck.
i got no idea of what is going on.
EDIT: When trying to figure this i was comenting on my code to see if i could find the error and when i removed most of the beforeEach() section i left on the next() function and a new error showed sayin "t is not a function", so i guess for some reason java script is only recognizing the last letters of my funcions, like t in next() and e in some().
EDIT2: After removing unecessary code that i copied from another project apperently the error happens in my next() function.
here's my router code:
import Vue from "vue";
import Router from "vue-router";
import Dashboard from "#/views/Dashboard.vue";
import Opcoes from "#/views/settings.vue";
import LoginForm from "#/components/component/loginForm.vue";
import store from "./store.js";
import Cookies from "js-cookie";
Vue.use(Router);
let router = new Router({
mode: "history",
base: process.env.BASE_URL,
routes: [
{
path: "/",
name: "loginForm",
component: LoginForm,
meta: {
guest: true
}
},
{
path: "/dashboard",
name: "Dashboard",
component: Dashboard,
meta: {
requiresAuth: true
}
},
{
path: "/opcoes",
name: "Opcoes",
component: Opcoes,
meta: {
requiresAuth: true
}
},
{
path: "/sair",
name: "Sair",
component: LoginForm,
meta: {
requiresAuth: true
}
}
]
});
router.beforeEach((to, from, next) => {
const expirationDate = Cookies.get("expireIn");
const now = new Date().getTime();
if (now >= expirationDate) {
Cookies.remove("expirationDate");
Cookies.remove("token");
Cookies.remove("userId");
store.dispatch('LOGOUT');
} else {
next();
}
});
export default router;
Heres a print of my stack trace:
My Big Fat Guess
The error message points to line 2203 (line 1921 is part of a generic error handler). This line is in the Router push() method
onComplete && onComplete(route);
My guess is, somewhere in your code not shown in your question (perhaps in your LoginForm component), you are calling this.$router.push() with a second argument that is not a function, eg
this.$router.push({ name: 'Dashboard' }, somethingNotAFunction)
Other problems (from previous versions of the question)
Your / route (loginForm) has no params so this...
next({
path: "/",
params: { nextUrl: to.fullPath }
});
is invalid. Did you perhaps mean to send nextUrl as a query parameter. If so, use
next({ name: 'loginForm', query: { nextUrl: to.fullPath } })
You also have
next({ path: "login" });
and
next({ name: "login" });
neither of which exist in your routes. You should only forward the route request to existing routes.
Finally, if you're using Vuex, you should not be directly assigning state values. In strict-mode, this
store.state.isLoggedIn = false
will trigger an error. You should be committing state mutations instead.

NestJS how to create new mongoose Model in unittest?

I try to unittest my NestJs Controller class. I already mocked my Service using ts-mockito but now I struggle to create the mongoose Objects I want to return and expect to get returned by the controller. How do I manage to create new Model Object to test with?
this is my Service:
#Injectable()
export class ProjectService {
constructor(
#InjectModel('Project') private readonly projectModel: Model<Project>,
private tagService: TagService,
) {} ...
This is my Model
let schema = new Schema({
name: {type: String, required: true},
description: String,
created: {type: Date, default: Date.now},
});
export const ProjectSchema = schema;
export interface Project extends Document {
readonly name: string,
readonly description: string,
readonly created: Date,
}
And this is my Module:
#Module({
imports: [
MongooseModule.forFeature([{ name: 'Project', schema: ProjectSchema }]),
],
controllers: [
ProjectController
],
providers: [
ProjectService,
],
})
export class ProjectModule {}
This is my Test:
describe('ProjectController', async () => {
let projectController: ProjectController;
let projectServiceMock: ProjectService = mock(ProjectService);
let projectModel: Model<Project>;
beforeAll(async () => {
projectModel = mock(Model);
const module: TestingModule = await Test.createTestingModule({
controllers: [ProjectController],
providers: [
{
provide: ProjectService,
useValue: instance(projectServiceMock)
},
{
provide: 'Project',
useValue: instance(projectModel)
}
]
}).compile();
projectController = module.get<ProjectController>(ProjectController);
});
Now I am trying to create a new Object of Project and return it from my service and expect it from the controller:
it('should return Project with id from projectService', async () => {
const project = new projectModel({name: 'ProjectName', description: 'ProjectDescription'});
let result = Promise.resolve(project);
when(projectServiceMock.getById('projectId')).thenReturn(result);
await expect(projectController.getById('projectId')).toEqual(result);
});
But I get this error:
Nest cannot find given element (it does not exist in current context)
25 |
26 | projectController = module.get<ProjectController>(ProjectController);
> 27 | projectModel = module.get<Model<Project>>('Project');
| ^
28 | });
29 |
30 | describe('getAll', async () => {
As I think I can read from the error message there must be something wrong with 'getting' the model to the test but I really don't know how I can get the Model without initiating a connection or so...
What can I do? Do you have some example code that worked for you?
Use getModelToken function exposed in #nestjs/mongoose:
import {getModelToken} from '#nestjs/mongoose';
const module = await Test.createTestingModule({
providers: [
{
provide: getModelToken('ModelName'),
useValue: ModelMock,
},
],
}).compile()
modelMock = module.get<mongoose.Model<any>>('ModelNameModel'); // The getModelFunction just appends 'Model' to the Model name

Aurelia - Hiding routes in navmenu ends up displaying nothing

I wanted to hide routes that were the user roles and I found THIS question on SO that is similar. I tried to implement it in my typescript project but its returning nothing and I am not sure why.
This is my implementation as it stands.
import { autoinject, bindable, bindingMode } from "aurelia-framework";
import { Router } from 'aurelia-router'
#autoinject
export class Navmenu {
public userName: string = 'anonymous';
private userRole = localStorage.getItem("user_role");
constructor(public authService: AuthService, public router: Router) {
this.userName = authService.getUserName();
console.log("userRole: ", this.userRole);
}
get routes() {
return this.router.navigation.filter(r => r.settings.roles === this.userRole );
}
}
My console.log shows "Admin" in the console but my filter doesnt filter it.
Here is how I have structured a route:
{
route: ["", "scheduler"],
name: "scheduler",
settings: {
icon: "scheduler",
auth: true,
roles: ["Employee", "Admin"], //These are my roles for this route.
pos: "left"
},
moduleId: PLATFORM.moduleName("../components/scheduler/scheduler"),
nav: true,
title: "scheduler"
},
Roles is an array.
How do I structure my filter so that it matches any userRole and thus returns a subset of filtered routes?
Look at this line in your router config:
roles: ["Employee", "Admin"]
Then at this in your getter:
r.settings.roles === this.userRole
roles is an array whereas this.userRole is a string, so the === operator will always return with false. Use indexOf or some instead:
return this.router.navigation.filter(r => r.settings.roles.indexOf(this.userRole) > -1);
or
return this.router.navigation.filter(r => r.settings.roles.some(t => t === this.userRole));

How to Load Certain Route on Electron App Activate Event

I'm having a really hard time to figure out how to load a particular route when the activate event is fired. I'm creating an Electron application using the Electron-Vue framework and I've these following routes:
export default [
{
path: '/',
name: 'login-page',
component: require('components/LoginPageView')
},
{
path: '/tracker',
name: 'tracker-page',
component: require('components/TrackerPageView')
},
{
path: '*',
redirect: '/'
}
]
Now, I'd like to load the /tracker route once the app.on('activate') is fired on the following:
app.on('activate', () => {
if (mainWindow === null) {
createWindow()
}
})
The main reason behind this is I'm creating a two window Electron application. The first window would be the login and the second window would be user profiles. When the user already logged in and closed the app using the system close button, the app stays in the Dock bar and when the app is clicked, the Electron activate event is fired and the login screen shows again. Since the user is already logged in, I don't want the user to show the login window again. Any suggestion would be must appreciated.
I was finally able to achieve this by using the Vue-Router Per-Route Guard beforeEnter method. Here's my draft:
let auth = true
export default [
{
path: '/',
name: 'login-page',
component: require('components/LoginPageView'),
meta: {
auth: false
},
beforeEnter: (to, from, next) => {
if (auth) {
next('/tracker')
} else {
next()
}
}
},
{
path: '/tracker',
name: 'tracker-page',
component: require('components/TrackerPageView'),
meta: {
auth: true
}
},
{
path: '*',
redirect: '/'
}
]
Any feedbacks are most welcome to improve this even better :)