I have a simple TopbarComponent
which basically adds a bootstrapish navbar at the top of my view.
Since 90% of my templates should have this directive included, i want to handle it through my app.component
which looks like this:
import ...;
@Component({
selector: 'my-app',
templateUrl: 'app/app.component.html',
directives: [ROUTER_DIRECTIVES, TopbarComponent, ...],
providers: [ROUTER_PROVIDERS, ...]
})
@RouteConfig([
{
path: '/login',
name: 'Login',
component: LoginComponent
},
{
path: '/dashboard',
name: 'Dashboard',
component: DashboardComponent,
useAsDefault: true
}
])
with its template looking like this:
<my-topbar></my-topbar>
<div class="container-fluid">
<div class="row">
<router-outlet></router-outlet>
</div>
</div>
Now i want use ngIf
(or any other way except of hiding it with css) to hide the topbar on several routes, like /login
for example.
I tried several approaches using @CanAcitvate()
or implementing OnActivate
in my LoginComponent
(as well as trying it from my AppComponent
) but with no effects (having problems to even get the functions to fire).
The closest i got was using the Router
directly in my AppComponent
like this:
export class AppComponent implements OnInit{
showTopbar:boolean;
constructor(private _router:Router) {}
ngOnInit():any {
this.showTopbar = this._router.lastNavigationAttempt != '/login';
}
}
and in my app.component.html
i changed the directive to <my-topbar *ngIf="showTopbar"></my-topbar>
But this only works on initial load of my app, cause ngOnInit
isn't fired on each state change. Is there a similar method i can use (and just can't find) or am i moving in the wrong direction here?
At the moment, the answer of PierreDuc doesn't work for me, but i tried a similar approach using location.path()
like this:
constructor(private _location:Location) {}
private get _hideTopbar() : boolean {
switch(this._location.path()){
case '/register':
case '/login':
return true;
default:
return false;
}
};
Too bad that i can't use the data
property in the @RouteConfig
in this approach. It would feel better.
I've crashed my head on a similar problem for a while. The solution that i've found could be seen as tricky at first, but can resolve many similar problems of this type for me in the future.
Basically, you have to make the router-outlet
emit an event on route change, having your AppComponent
listening to that custom event.
The first step is to create a custom router-outlet:
@Directive({
selector: 'custom-router-outlet'
})
export class CustomRouterOutlet extends RouterOutlet {
private parentRouter:Router;
constructor(_elementRef: ElementRef,
_loader: DynamicComponentLoader,
_parentRouter: Router,
@Attribute('name') nameAttr: string) {
super(_elementRef, _loader, _parentRouter, nameAttr);
this.parentRouter = _parentRouter;
}
activate(nextInstruction: ComponentInstruction): Promise<any> {
//***YOUR CUSTOM LOGIC HERE***
return super.activate(nextInstruction);
}
}
This is a very basic implementation of a custom router-outlet. Your logic have to be implemented in the activate method, called on each route change. In this method you can check the data property in your route.
The main problem is that, right now, Directives can't emit events... They accept only inputs, no outputs can be fired...
I've found a workaround to this problem: creating a Service for communication between Components and Directives using EventEmitter
:
@Injectable()
export class PubsubService {
private _pubSubber: EventEmitter<any> = new EventEmitter();
routeisChanging(obj:any) {
this._pubSubber.emit(obj);
}
onRouteChanged() {
return this._pubSubber;
}
}
The AppComponent subscribes to the onRouteChanged
event:
subscription:any;
constructor(private _pubsubService:PubsubService) {
this.subscription = this._pubsubService.onRouteChanged().subscribe(data => {
/**YOUR CUSTOM LOGIC HERE*/});
}
The CustomRouterOutlet fires the event in the activate
method when needed:
activate(nextInstruction: ComponentInstruction): Promise<any> {
//***YOUR CUSTOM LOGIC HERE***
this._pubsubService.routeisChanging(nextInstruction.routeData.data['hideTopbar']);
return super.activate(nextInstruction);
}
In this way you can easily implement a communication between the router and the AppComponent
, with any logic.
You have to remember to inject the communication Service in the RootComponent
of your app.
Not sure if it is the right way, but you can add any data in the data
parameter of the @RouteConfig()
object. For instance you can place in the RouteDefinition
of Login
an object with a hideTopbar
setting. You would only have to place this in a route if it should be set to true:
warning, untested code ahead :)
@RouteConfig([{
path: '/login',
name: 'Login',
component: LoginComponent,
data : {hideTopbar : true}
},
//...
])
You can access this data in the AppComponent
class like:
export class AppComponent {
private get _hideTopbar() : boolean {
return this._data.get('hideTopbar');
};
constructor(private _data:RouteData) {}
}
And change the AppComponent
template to:
<my-topbar *ngIf="!_hideTopbar"></my-topbar>
<div class="container-fluid">
<div class="row">
<router-outlet></router-outlet>
</div>
</div>
Almost there, try this
Template
<my-topbar *ngIf="shouldShowTopbar()">
</my-topbar>
App Component
export class AppComponent implements OnInit{
hideWhen: Array<string> = ['Login', 'Help', 'SomeOtherRoute'];
// put all the route names where you want it hidden in above array
constructor(private _router:Router) {}
shouldShowTopbar() {
return (hideWhen.indexOf(this._router.currentInstruction.component.routeName) > -1);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With