Angular routing bug - this.router.navigateByUrl() and this.router.navigate() - javascript

I am creating angular application that will handle documentation for my GitHub apps, in more complex way than just readme files. I want to redirect user after clicking in topnav dropdown to selected route, but there's problem with router. (I need to redirect with some parameters, but it doesn't work even with simple path reditect). Those methods redirect to target route for like 1 seconds (like excepted), then user got redirected back to root page.
Code:
/* First element is project name, second is category/part of application name */
choices = ["typing_speed_test", "overview"]
json = json
constructor(private router: Router){}
onProjectClick($event){
this.choices[0] = $event.target.innerHTML.trim();
this.choices[1] = "overview";
this.redirect();
}
onCategoryClick($event){
this.choices[1] = $event.target.innerHTML.trim();
this.redirect();
}
redirect(){
this.router.navigateByUrl("404");
}
Routes:
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'project/:project_name/:category', component: SubpageComponent },
{ path: '404', component: NotFoundComponent },
//{ path: '**', redirectTo: '404', pathMatch: 'full' }
];
Link to gif with problem: https://imgur.com/a/x2mPxvh
Full code in github repo: https://github.com/Dolidodzik/DocsForMyApps (if you used code from here to answer this question, please point that in your answer)
I think I may did some stupid mistake, because I am pretty new in Angular, but I couldn't do it, because every google question showed me solves for ERRORS, when redirect doesn't work at all, not to bugs like in my situation.

You need to remove the href="#" from your anchor links in your navigation bar. It's causing the browser to reload:
<a class="dropdown-item" *ngFor="let item of categories() | keyvalue">
{{item.key}}
</a>
Also this is a bit weird solution:
this.choices[0] = $event.target.innerHTML.trim();
You better just send the item variable in your function call in your template, and you can then read this in your component event handler:
onProjectClick(item){
this.choices[0] = item.key;
this.choices[1] = "overview";
this.redirect();
}

the problem is your lins look like this:
link
default link behavior causes your app reload from the start. you should use [routerLink]="['someUrl']" instead of href. If you need that href in some cases, consider calling $event.preventDefault() to cancel the native browser navigation

Related

Angular component reloads when navigating

Routes for my Module:
const routes: Routes = [
{ path: ":level1/:level2/:level3", component: CategoriesComponent },
{ path: ":level1/:level2", component: CategoriesComponent},
{ path: ":level1", component: CategoriesComponent},
{ path: "", component: CategoriesComponent },
];
the Categories component Generates some links like so:
<a [routerLink]="['category1']" [relativeTo]="activatedRoute">My Link</a>
the ngOnInit & ngOnDestroy are called each time it navigates between those routes.
What I need is the component to stay mounted and not re-init each time.
Stackblitz link to illustrate the difference between using QueryParameters and RouteParameters: Angular example
NOTE: Dont suggest RouteReuseStrategy: that isn't the answer we are looking for. I have another angular application that doesn't reload the component between routes. And this is the official expected behaviour.
You need to dive into a RouteReuseStrategy and create your custom strategy with saving touched pages
Docs
RouteReuseStrategy allows you to tell Angular not to destroy a component, but in fact to save it for re-rendering at a later date. https://stackoverflow.com/a/41515648/15439733

Angular 7 children component route without parent route

I have a problem with routes in my Angular 7 application. I have a page frame, where I would like to switch to different components according to route without refreshing / changing main page frame.
In my page frame I have <router-outlet>.
My main route is http://localhost:4200/login.
When I go to this page, I would like to show component which is related to this route.
My second route which I would like to show is http://localhost:4200/forgot-password.
I would like to see on exactly the same page second component which is related to forgot password form.
I tried to achieve this with children and it works, but when I make a redirection it adds /forgot-password at the end of existing http://localhost:4200/login url. In this case I've always receive http://localhost:4200/login/forgot-password in url. What I'm trying to achieve is to show in url http://localhost:4200/forgot-password, without /login.
app.routes.ts
// Login Page
{
path: 'login',
component: LoginComponent,
canActivate: [ HTTPSGuard ],
children: [
{
path:'',
component: Login2ComponentComponent,
data: {
animation: 'Sign-In'
}
},
{
path: 'forgot-password',
component: ForgotPassword2Component,
data: {
animation: 'Forgot-Password'
}
}
]
}
In my login component I have a link:
<a [routerLink]="['/forgot-password']">Go to forgot</a>
But it adds me to the current /login route /forgot-password.
Maybe there is a router attribute which I can use in app.routes.ts to make this happened?
Thank you for help!

Change view/component but not url

I'm working in a 404 error page functionality, I got it to work with:
const router = new VueRouter({
routes: [
// ...
{
path: '*',
component: NotFound,
name: '404',
meta: {page_title: 'Vuejs&Material Demo | 404 NOT FOUND'}
},
]
});
// ... not found something
this.$router.push({name: '404'});
The problem with the above is that the url changes too, but I just want the view (component) to change.
I find very annoying when a simple typo redirects to a http://example.com/404, it makes me type the url again or go from the browser autofill, either way I don't like it.
I was wondering if there is some sort of method/logic to have something like this: https://www.facebook.com/sdfsd/sfsdf/asfasfsafastgtgregre .
Bottom line, I want for the view to change but not the url.
Is there any vue/vue-router way to do this?
This is a little hacky, but you could update the route via the router's history object's updateRoute method:
let route = this.$router.match({ name: '404' });
this.$router.history.updateRoute(route);

Angular 2 routes resolve different component

Here is my use case:
When i load url /product/123 i want to load component ProductComponent
This is my setup:
RouterModule.forRoot([
{
path: 'product/:productId',
component: ProductComponent
},
{
path: '**', component: NotFoundComponent
},
]),
Now I have added a resolver to check if that product id exists, so my setup looks like this:
RouterModule.forRoot([
{
path: 'product/:productId',
component: ProductComponent,
resolver: {
productResolver: ProductResolver
}
},
{
path: '**', component: NotFoundComponent
},
]),
My resolver checks if that productId parameter exists via api call. The problem i have is that when productId is not found I want to load NotFoundComponent rather than redirecting to different page (i dont want to change url like angular 2 documentation suggests).
Anyone knows how to do that? if not productId found via resolver load NotFoundComponent without changing url/navigate?
I think all you want to do is skip the location change when you navigate to your NotFoundComponent. I'm assuming you've injected the Router into your resolver and are doing something like this when the ID does not exist:
router.navigate(['someUrl']);
Or you might be using the navigateByUrl method. Either way, you can tell the router not to change the URL:
router.navigate(['someUrl'], {skipLocationChange: true});
Don't see why you'd need to load your component via router settings, I'd put it inside the Component that tries to fetch it from the service, and then if it doesn't get a result back toggle some boolean that controls whether the NotFoundComponent gets shown. Some pseudo-ish code below:
ngOnInit(){
if (this.route.snapshot.params && this.route.snapshot.params['id']){
myService.getTheThingById((success)=>{
this.isFound = true;
},(err)=> {
this.isFound = false;
});
}
Then assuming your NotFoundComponent has a selector in it like 'not-found-component' throw it in the template for the component that's calling the service.
<not-found-component *ngIf='!isFound'></not-found-component>
I once faced this problem.
What I did was, in the component, to create 2 other components (in your case, you have ProductComponent, NotFoundComponent, and the other one you want to navigate to, let's say ArticleComponent)
Then I inserted 2 of the components in the main one :
product.component.html
<app-notFound></app-notFound>
<app-article></app-article>
After that, in your ngOnInit, you see if the parameter is there. If he is, then you log it in a property, let's say isParam = true.
Your template becomes
<app-notFound *ngIf="!isParam"></app-notFound>
<app-article *ngIf="isParam"></app-article>
It may not be the best solution out there, but it worked for me !

Add Parameter to Angular 2 Route from Component without Navigating

With my current code I am attempting to do deep linking within my Accordion component.
By navigating to dev.local/#/accordion and then clicking on an accordion title, I want to update the route to look like:
dev.local/#/accordion/2
But I do not want to navigate to this path once it is set. Essentially, if someone were to hypothetically copy this URL it would return them to the exact accordion that was opened when they copied it.
The problem I'm running into is that I'm applying the following code to my accordion links to set the parameter:
<a [routerLink]="['Accordion',{tab:'4'}]"></a>
This works but it actually navigates and re-initializes the component. I need to be able to click this link, set the route to dev.local/#/accordion/4 without re-initializing the component by navigating to it.
Here are my current routes:
#RouteConfig([
{
path: '/accordion',
component: Accordion,
as: 'AccordionNew'
},
{
path: '/accordion/:tab',
component: Accordion,
as: 'Accordion'
}
]);
If the Aux Routes don't help, you can try my solution I used for a smiliar problem:
Build a RootAccordionComponent. Set a selector for your
AccordionComponent and use it as a tag in the RootAccordionComponent
template (e.g. ). Also place a somewhere in this template. For RootAccordionComponent you use "path: '/accordion/...'" RouteConfig.
(If not already done) build a AccordionService with a tabId attribute.
Build a TabIdAccordianComponent with an empty template. Use "path: '/accordion/:tab'" for it as RouteConfig. Now the only thing the component does, is to fetch the tabId from RouteParams and save it to the AccordionService.
When initializing your AccordianComponent, get the tabId from the AccordionService.
That way you can get the tabId when your AccordianComponent is initilized if someone goes directly to the url with an tabId, but it won't reload if the user clicks on an Accordian.
See this Plunker for a working example:
http://plnkr.co/edit/5HEgIUZGRP3Cfqh6LvzA?p=preview
If you launch the preview in a separate window you can also see the routes. E.g. if you load "http://run.plnkr.co/WOpQaPkFafJ7uU8Y/#/accordion/2" the selected Accordion will be set to 2.
Hope this helps, although it is not the cleanest solution.
RouteConfig to get to AccordionRoot:
#RouteConfig([
{path:'/accordion/...', name: 'AccordionRoot', component: AccordionRootComponent},
])
export class AppComponent { }
The actual AccordionRootComponent:
#Component({
template: `
<router-outlet></router-outlet>
<accordion-component></accordion-component>`,
directives: [ROUTER_DIRECTIVES, AccordionComponent]
})
#RouteConfig([
{ path: '/:tabId', name: 'AccordionTab', component: TabIdAccordionComponent, useAsDefault: true}
])
export class AccordionRootComponent { }
The TabIdAccordionComponent:
#Component({
template: ''
})
export class TabIdAccordionComponent {
constructor(private routeParams: RouteParams, private accordionService: AccordionService){
let tabId = +this.routeParams.get("tabId");
this.accordionService.tabId = tabId;
}
}
The AccordionComponent making use of the tabId from the service:
export class AccordionComponent implements OnInit {
constructor(private accordionService: AccordionService){}
selectedAccordionId: number;
ngOnInit(){
this.setSelectedAccordion(this.accordionService.tabId);
}
setSelectedAccordion(tabId: number){this.selectedAccordionId = tabId;}
}

Categories

Resources