I've got this working, but I'm sure there's a better way to do this in Angular. Essentially I've the following:
Assume nested, inflected paths, e.g. /logos and /logo/:id
I have the following markup, which works:
<li class="nav-item">
<a class="nav-link" routerLinkActive="active" links="logo" routerLink="logos">
Logos
<span routerLink="logo" hidden></span>
</a>
</li>
This will properly cause the tab to activate on /logo/:id, however that span in there feels really hacky and incorrect. If the paths are not inflected, e.g. /logo and /logo/:id or /logos and /logos/:id it works fine. Do I just add another router link? Should I add some other directive? Do I need to go custom?
Thanks!
As your router is setup like this; (assuming logos is after the root, i.e. /logos)
{
path: 'logos',
component: LogosComponent
},
{
path: 'logo/:id',
component: LogoComponent,
}
There's only two types of links you need to have:
Link to all the logos:
<a routerLink='/logos'>All Logos</a>
Link to a single logo:
<a routerLink='/logo/specific-logo'>Specific Logo</a> , where "specific-logo" is the ID of the logo you want to go to.
If you want to have /logos seem active while you are in the /logo/specific-logo directory, I don't think it's possible (except for the workaround you found). However, you can simulate it using a computed value, i.e.:
<a class="nav-link" [class.active]="logoRouteActive" links="logo" routerLink="logos">
Logos
</a>
import { Router } from '#angular/router';
// ...
constructor(private router: Router) { /** ... */ }
get logoRouteActive(): boolean {
return this.router.isActive('/logo', false) || this.router.isActive('/logos', false);
}
Related
Angular Material Version: #angular/material#14.0.2
I am building a web application using the Angular framework for the first time.
The app-routing-module lazy loads a dashboard module that declares the desired components and imports the required modules. The dashboard module imports a dashboard routing module which holds the routes with the WrapperComponent as the parent and the side nav content as the children, split up into different components.
dashboard-routing.module.ts
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { AboutComponent } from './components/about/about.component';
import { DashboardComponent } from './components/dashboard/dashboard.component';
import { LoginComponent } from './components/login/login.component';
import { WrapperComponent } from './components/wrapper/wrapper.component';
const routes: Routes = [
{
path: '',
component: WrapperComponent,
children: [
{
path: 'dashboard', // --> localhost:4200/dashboard
component: DashboardComponent,
},
{
path: 'login', // --> localhost:4200/login
component: LoginComponent,
},
{
path: 'about', // --> localhost:4200/about
component: AboutComponent,
}
]
},
{
path: '**',
redirectTo: '/dashboard',
pathMatch: 'full'
}
];
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class DashboardRoutingModule { }
wrapper.component.html
<mat-sidenav-container>
<mat-sidenav #sideNav mode="side" opened="opened">
<app-side-nav>
</app-side-nav>
</mat-sidenav>
<mat-sidenav-content>
<router-outlet></router-outlet>
</mat-sidenav-content>
</mat-sidenav-container>
The app-side-nav component consists of the router links.
<div class="sidenav">
<div class="logo">
<a (click)="toggleMenuState()" class="simple-text logo-mini">
<div class="logo-img">
<img src="./assets/images/sample_logo.png" alt="logo">
</div>
</a>
</div>
<ul class="nav">
<li class="active nav-list-item"><a routerLink="/default-route"><i class="fa fa-dashboard fa-nav-icon"><span class="nav-item-text">Dashboard</span></i></a></li>
<li class="nav-list-item"><a routerLink="/some-route"><i class="fa fa-group fa-nav-icon"><span class="nav-item-text">Groups</span></i></a></li>
<li class="nav-list-item"><a routerLink="/some-route"><i class="fa fa-line-chart fa-nav-icon"><span class="nav-item-text">Charts</span></i></a></li>
<li class="nav-list-item"><a routerLink="/some-route"><i class="fa fa-book fa-nav-icon"><span class="nav-item-text">Portfolio</span></i></a></li>
<li class="nav-list-item"><a routerLink="/login"><i class="fa fa-user fa-nav-icon"><span class="nav-item-text">Login</span></i></a></li>
<li class="nav-list-item"><a routerLink="/some-route"><i class="fa fa-gear fa-nav-icon"><span class="nav-item-text">Settings</span></i></a></li>
</ul>
This seems to work fine because I can see the relevant content being loaded in the DOM depending on the button clicked. But for some reason, the content is not visible.
Here is a screenshot of the dashboard component loaded. For some reason, it is loading with a margin of 1920px but even when removed the content is still not visible.
The structure seems to work yet there is something not quite right otherwise the content would show. Would appreciate any thoughts, suggestions or further questions.
I think your proble is material think that your app-side-nav or mat-sidenav have a full screen width, on the screenshot as I can see, content have a 1920px margin-left, this means that your view out of the bound of screen.
There is my example with correct margin, pay attention to your nav component:
Stackblitz example
Hope this helps.
After checking the rendered template it seems that your code lacks the routing precision.
Your app-routing.module.ts says that for path "" it should use DashboardModule. In the module you say that for path "" it should render a WrapperComponent. And after this point it has no default route.
So, basically what happens is that your wrapper component is rendered, however none of its children matches the route. To fix it, you have two options:
provide handler for "" route in the wrappers children routes
add a redirect to one of the routes using a wildcard route as I did in the Stackblitz fork of your GitHub project.
Stackblitz fork
That is to fix that router outlet does not render anything.
Another problem is <mat-sidenav #sideNav mode="side" opened="opened">, as you are assigning a string to the boolean flag, which means that it will be always truthy and won't display what's underneath.
In result removing opened="opened" and adding a redirection to the dashboard page we can see the "dashboard works!"
P.S. Looking at the examples in the docs it doesn't look to be designed to work the way you want it to. Rather show or hide behavior. Not expand.
P.S.S. You could try using autoresize and toggle width manually for the ever open sidenav as suggested here
Not sure why, I need to close offcanvas after the user click on a <router-link> in my vue app.
I've imported the related component in this way
import { Offcanvas } from 'bootstrap/dist/js/bootstrap.esm'
And inside my methods I've created a method that will be called from the router link
closeOffcanvas() {
const offcanvasMenu = new Offcanvas(this.$refs.offcanvasNavbar)
console.log(offcanvasMenu)
offcanvasMenu.hide()
},
<li class="nav-item">
<router-link class="nav-link" :to="userProfileURL" #click="closeOffcanvas">Profilo</router-link>
</li>
I've tested the data-bs-dismiss="offcanvas" but this will prevent the navigation to the requested route.
Any suggestion or tip to fix this?
I dynamically created sidebar navigation list using router-link.
<template>
<div class="sidebarListItem bg-light">
<router-link
v-for="route in $router.options.routes"
:key="route.path"
:to="route.path"
class="list-group-item list-group-item-action bg-light">
{{route.title}}
</router-link>
</div>
</template>
<script>
export default {
name: "PageSidebarList",
computed: {
routes(){
return this.$router.options.routes
}
},
}
</script>
Each router-link is one map.
But then, I need to use <router-link> in some other place in my app, so I need to register new view (map) in router.js. The problem is I don't want this one view to be in sidebar list and it is automatically because of my code. I tried to separate routes in different files(one that I need for list and the rest of the views) and then importem them in router.js. but still it does't work. Or I don't know how to call them separatelly. I am new to vue and vue-router so please help. Is it possible to do what I want?
It's possible to add custom meta data to each route:
{
path: '/foo',
component: Foo,
meta: { hideInNav: true }
}
And then you can use a v-if on the router-link v-if="!route.meta.hideInNav"
Sorry to revive a question. Im using Angular 7 and Im trying to use Router Link.
This is my app-routing-module
const routes: Routes = [
{ path: 'locations' , component : LocationManagerComponent },
{ path: 'locations/create' , component : CreateEditLocationComponent },
{ path: 'locations/create/:id', component : CreateEditLocationComponent },
{ path: '404' , component : PageNotFoundComponent},
{ path: '**' , redirectTo: '/404'}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
This is router link:
<a [routerLink] = "['/locations']" routerLinkActive="active"> test link </a>
When I click on link, nothing happens. The URL on browser changed but component is not loaded.
If I press F5, component is loaded and from that point on, routers link works.
I've tryed a lot of stackoverflow solution like writing link in any sort of variant like
<a routerLink="/locations" ...
<a [routerLink]= ['/locations'] ...
<a [routerLink]= "['/locations']" ...
With or without LinkAttive attribute. Putting
<base href="/">
in index.html etc....
Following this topic: TOPIC I've tried to include Router in my Layout component:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
#Component({
selector: 'app-layout',
templateUrl: './layout.component.html',
styleUrls: ['./layout.component.css']
})
export class LayoutComponent implements OnInit {
constructor(
private route : ActivatedRoute
) { }
[...]
but nothing changes.
The strange part is that after an F5, all routes works, even route to component not yet loaded.
In this topic TOPIC 2 the user resolved removing css class. I've tried to put my link in a completely cleaned component HTML and it not working (but still works after a refresh).
<p>
dashboard works!
<a routerLink = '/locations' routerLinkActive="active"> test link </a>
</p>
UPDATE: This is layout.component where route tag is.
I can't figure out how to have a Sidenav without having route-outlet inside it.
<mat-sidenav-container fullscreen>
<mat-sidenav #sidenav mode="over">
<div class="profile_container">
<span> User Name </span>
</div>
<mat-nav-list>
<mat-list-item><a [routerLink]="['/locations']" routerLinkActive="active"> Locations
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<app-header (toggleSidenav)="sidenav.toggle()"></app-header>
<div style="padding: 20px 10px 10px 20px;">
<router-outlet></router-outlet>
</div>
</mat-sidenav-content>
</mat-sidenav-container>
Note: this answer is based on the previous version of your question, before you added the code of layout.component.html. So, instead of layout component, I am using the simplified dashboard component.
The below is working for me in Angular 8.1.
app.component.html
<app-dashboard></app-dashboard>
means that the DashboardComponent is contained within (is the child of) the AppComponent.
No change to the default app.component.ts
dashboard.component.html
<p>
dashboard works!
<a routerLink = '/locations' routerLinkActive="active">
Locations test link </a>
</p>
<p><a routerLink = '/locations/create' routerLinkActive="active">
Locations/create </a></p>
<p><a routerLink = '/locations/create/:id' routerLinkActive="active">
Locations/create/:id </a></p>
<p>router-outlet is below:</p>
<router-outlet></router-outlet>
All the links are working with click and with manually entering the url (eg: http://localhost:4200/locations/create/:id) in the browser and with reload (F5).
New Components
Generated using the ng generate component command:
Dashboard
LocationManager
CreateEditLocation
PageNotFound
app-routing-module.ts
The same as your file, but also added import statements for the newly generated components.
I figured what cause the problem but I can't unserstand why and I was not able to reproduce in StackBlitz.
This was my app.component.html, the root of all app:
<main>
<!-- Showing All Site Pages -->
<span *ngIf='isLogged()'>
<app-layout style="height:100%"></app-layout>
</span>
<!-- Showing Login Page -->
<div *ngIf='!isLogged()'>
<router-outlet></router-outlet>
</div>
</main>
The App-Layout code is above.
THIS NOT WORKS!
I changed it with a simply:
<main>
<app-layout style="height:100%"></app-layout>
</main>
As you see from my question, Layout has its own router-outlet.
I think the problem is the two router-outlet tag. Maybe Angular is not able to understand thats they are mutually exclusive. Maybe when I was clicking on menu, for some reason, Angular was updating the "first" router-outlet encountered and only after a refresh (F5), when the isLogged was already triggered and the app-layout was loaded directly, Angular knows which router-outlet to use.
In the new Way all pages, even Login, has to be child of AppLayout so every Layout component that's exists only if logged, has to be manually hide with *ngIf='!isLogged()'
A little price to pay to have routes works.
I am trying to yield a piece of html into the navbar of a particular route in Angular 2. So this is not a part of the body component but is a part of the layout in the header of the page. How di i do this in angular 2?
This is what I did in AngularJS.
//header.html .. which is a partial within the layout
<div class="nav-wrapper">
<ul class="nav navbar-nav" ui-view="navbar_options">
<li>
<a href="" trigger-resize="" ng-click="app.layout.isCollapsed = !app.layout.isCollapsed" class="hidden-xs"> <em class="fa fa-navicon"></em>
</a>
<a href="" ng-click="app.asideToggled = !app.asideToggled" class="visible-xs sidebar-toggle"> <em class="fa fa-navicon"></em>
</a>
</li>
</ul>
</div>
//routes.config.js
.state('app.xyz', {
url: '/jobs/:jobID',
views:{
'': {title: 'abc',
templateUrl: helper.basepath('path/xyz.html'),
controller: 'myController',
},
'navbar_options': {title: 'my_page',
templateUrl: helper.basepath('path/xyz_navbar.html'),
controller: 'myController'}
}
})
I want to achieve the same in Angular 2
If I understand you correctly, you are trying to display a component in the navbar based on a certain route, but the main body of the page is also going to be updated on the route change. If I were you, I would use a "Secondary Route". I believe they are also referred to as "Named Routes"
Normally, your web page would be something like this:
<div class="app-content">
<router-outlet></router-outlet>
</div>
This works great if you are updating only one portion of your web page. If you want to update more than one portion, you need to provide several routes and they are named. They look like this:
<router-outlet name="myroute"></router-outlet>
I am guessing you will want one of these named routers in your navbar and then you have to create a link which places data in the named route... That link would look something like this:
<button md-button [routerLink]="['/mainroute', {outlets: {'myroute': ['routnamehere']}}]">
Open Two Routes
</button>