In Angular 2, you can specify a @CanActivate
annotation for a component where you can determine if the component should be activated or not. The reason it's not an interface is because the callback is called before the component is even instantiated. The problem is, I can't figure out a way to get dependencies injected into that callback. And I need my service that tells me whether I'm logged in or not (and as whom) to determine whether routing to a particular component is allowed or not.
How can I inject a dependency into a @CanActivate callback? I'm using ES5, and this doesn't work:
app.ListsComponent = ng.router.CanActivate([
app.SessionService,
function(session, instruction) {
console.log(session);
return true;
}
])(app.ListsComponent);
Edit: Alternatively, I can use the routerOnActivate
lifecycle event on the component, and use this.router.navigate
to send the user away if they're not supposed to be there. The downside there is that it breaks browser history: If I redirect you asynchronously every time you arrive at a particular URL, you can't use the Back button in your browser very usefully. Is there a way to have router.navigate
use history.replaceState
instead of history.pushState
for this kind of situation?
Most solutions here will present problems with loading sub-dependencies from elsewhere in the hierarchy, because they create a new injector. Also, this results in additional (non-shared) services being instanced. I recommend following the pattern provided by Brandon in https://github.com/angular/angular/issues/4112
He references this Plunk: http://plnkr.co/edit/SF8gsYN1SvmUbkosHjqQ?p=preview
Its main idea is a singleton injector, which he saves when the app initializes. This provides access to the root dependencies you already have configured, and further allows your services to be shared as a singleton as they were probably intended:
import {Injector} from 'angular2/angular2';
let appInjectorRef: Injector;
export const appInjector = (injector?: Injector):Injector => {
if (injector) {
appInjectorRef = injector;
}
return appInjectorRef;
};
bootstrap([ServiceYouNeed]).then((appRef) => {
// store a reference to the injector
appInjector(appRef.injector);
});
You have to inject it using injector. Just a quick copy paste from a project I'm doing:
@CanActivate((next: ComponentInstruction, prev: ComponentInstruction) => {
console.info('CanActivate hook! - can return boolean or Promise');
var injector = Injector.resolveAndCreate([HTTP_PROVIDERS, YourService]);
var yourService = injector.get(YourService);
// do something with yourService
return new Promise(function(resolve, reject) {
resolve(true);
});
})
HTTP_PROVIDERS you have to pass along if your service is for example injecting the HTTP service in the constructor.
I use it to put an observable on the params of the next object. And the next object is your next "Component/state":
@CanActivate((next: ComponentInstruction, prev: ComponentInstruction) => {
console.info('properties component CanActivate hook! - can return boolean or Promise');
var injector = Injector.resolveAndCreate([HTTP_PROVIDERS, PropertiesService]);
var propertiesService = injector.get(PropertiesService);
return new Promise(function(resolve, reject) {
next.params['properties'] = propertiesService.getProperties();
resolve(true);
});
})
The PropertiesService calls a backend and returns an Observable that represents the data with properties
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