From my understanding, in Angular 2, if you want to pass values between unrelated components (i.e., components that don't share a route and thus don't share a parent-child relationship), you do so via a shared service.
So that's what I've set up in my Angular2 app. I am checking to see if a certain series of characters exist in a url and returning true if it does.
isRoomRoute(routeUrl) {
if ((routeUrl.includes('staff') || routeUrl.includes('contractors'))) {
console.log('This url: ' + routeUrl + ' is a roomRoute');
return true;
} else {
console.log('This url: ' + routeUrl + ' is NOT a room route');
return false;
}
}
In the constructor of the root app.component, I'm subscribing to routing events:
constructor(private routeService: RouteService,
private router: Router) {
this.router.events.subscribe((route) => {
let routeUrl = route.url;
this.routeService.sendRoute(routeUrl);
this.routeService.isRoomRoute(routeUrl);
});
}
... and then using those provided urls to check whether or not a url contains the specific string. This is evaluated every time the route changes.
So that's all working as expected.
However, I'm running into a problem in passing the result of that check to a different, non-related (non-parent-child) component.
Even though I'm using a shared service (routeService) in both the app.component and the un-related (room.component) component, what works in one doesn't work in the other. From my understanding, the "truthiness" of what's being checked here should be enough to return a true statement.
But in the secondary, unrelated component, I get an "undefined" error when I call the function, like this:
isRoomRoute() {
if (this.routeService.isRoomRoute(this.routeUrl)) {
return true;
}
}
So this is where I'm stuck. Basically the evaluation as to whether a url contains a certain string has already happened. Now I just need to pass the boolean result of that check to the secondary, non-related component. How can I best do this in Angular 2?
Your understanding is correct, an injectable
shared service is a common way of communication between multiple, unrelated components.
Here is the walk-through of such a use case.
Firstly, suiting your situation, we will listen the Router
events in AppComponent
, obtain the active route, and pass it to RouteService
so the service can manipulate it, and/or serve it to other components.
This is how the AppComponent
should look like:
export class AppComponent {
constructor(private _router: Router,
private _routeService: RouteService) {
this._router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
let url = event.urlAfterRedirects;
this._routeService.onActiveRouteChanged(url);
}
});
}
}
When it comes to the service, here we'll introduce the BehaviorSubject
as a delegate, so the components using the service can subscribe to a service data changes. For more information about BehaviorSubject
and other Subjects, visit: Delegation: EventEmitter or Observable in Angular2
Here is the implementation of our shared RouteService
(components need to use the single instance of the service, so make sure you've provided it at the root level):
@Injectable()
export class RouteService {
isRoomRouteSource: BehaviorSubject<boolean> = new BehaviorSubject(false);
constructor() { }
onActiveRouteChanged(url: string): void {
let isRoomRoute = this._isRoomRoute(url);
this.isRoomRouteSource.next(isRoomRoute);
// do other stuff needed when route changes
}
private _isRoomRoute(url: string): boolean {
return url.includes('staff') || url.includes('contractors');
}
}
The example of another component using the service, and subscribing to our BehaviorSubject
changes:
export class AnotherComponent {
isCurrentRouteRoomRoute: boolean;
constructor(private _routeService: RouteService) {
this._routeService.isRoomRouteSource.subscribe((isRoomRoute: boolean) => {
this.isCurrentRouteRoomRoute = isRoomRoute;
// prints whenever active route changes
console.log('Current route is room route: ', isRoomRoute);
});
}
}
If subscribing to isRoomRouteSource
changes isn't necessary, say we just need the last value stored, then:
export class AnotherComponent {
isCurrentRouteRoomRoute: boolean;
constructor(private _routeService: RouteService) {
this.isCurrentRouteRoomRoute = this._routeService.isRoomRouteSource.getValue(); // returns last value stored
console.log('Current route is room route: ', this.isCurrentRouteRoomRoute);
}
}
Hope this helped!
Just looking at your code it looks like something is incorrect here.
isRoomRoute() {
if (this.routeService.isRoomRoute(this.routeUrl)) {
return true;
}
}
this.routeUrl
in the above code will likely be undefined unless it is defined elsewhere and defined before . What you could do is instead set a property in the service on the route event and then in the isRoomRoute you would read that property.
@Injectable()
class routeService {
constructor(private router: Router) {
// subscribe to event
router.subscribe((url) => {
this.routeUrl = url;
// other things? sendRoute??
});
}
// Other methods for this class
isRoomRoute() {
return this.routeUrl && (this.routeUrl.includes('staff') || this.routeUrl.includes('contractors'));
}
}
// Usage later where this service has been injected
@Component({
// ... other properties
providers: [routeService]
})
class someComponent {
constructor(private routeService: routeService) {}
someMethod() {
this.routeService.isRoomRoute(); // Check if we are in a room route.
}
}
In a case like this, I am not sure why you can't simply get the URL and parse it when isRoomRoute called instead of setting something on routing events.
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