I use an AuthService and an AuthGuard to log in/log out users and guard routes. The AuthService is used in the AuthGuard as well as in a LoginComponent. The AuthGuard is used to guard routes via CanActivate. When I try to run the app I get the following error:
zone.js:522 Unhandled Promise rejection: No provider for AuthService! ; Zone: angular ; Task: Promise.then ; Value: NoProviderError {__zone_symbol__error: Error: DI Error
at NoProviderError.ZoneAwareError
I have checked that the LoginComponent and AuthGuard both import the AuthService and inject it into the components via the constructor. I have also checked that the AuthService is imported into the AppModule file and added to the providers array so it can be used as a singleton service.
Edited to add code samples:
My App module contains the following:
#NgModule({
imports: [...],
providers: [..., AuthService, AuthGuard, ...],
declarations: [..., LoginComponent, EntryComponent ...],
bootstrap: [EntryComponent]
})
export class AppModule {
}
AuthGuard:
import { Injectable } from '#angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { ApiConfig } from '../Api';
import { AuthService } from './authservice';
#Injectable()
export class AuthGuard implements CanActivate {
constructor(
private authService: AuthService,
private router: Router,
private config: ApiConfig
) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
console.log(this.isAuthenticated());
if (this.isAuthenticated()) {
if (this.config.defined) {
return true;
} else {
this.authService.setConfig();
return true;
}
} else {
this.router.navigate(['/Login']);
return false;
}
}
// Checks if user is logged in
isAuthenticated() {
return this.authService.userLoggedIn();
}
}
LoginComponent constructor:
constructor(
private router: Router,
private notifications: Notifications,
private authService: AuthService
) {}
AuthService:
import { Injectable } from '#angular/core';
import { Http, Headers, RequestOptions } from '#angular/http';
import { Router } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import { ApiConfig } from '../Api';
#Injectable()
export class AuthService {
constructor(
private http: Http,
private router: Router,
private config: ApiConfig
) {
this.apiRoot = localStorage.getItem('apiRoot');
}
...
}
In your app.component class #Component({}) decoration add a line that states:
providers: [AuthService]
This creates a singleton service for that service at that level. If you wanted to provide it at a more granular level you would provide (instantiate) it at that lower level.
Please see the first couple paragraphs in this official angular 2 documentation
Turns out I didn't capitalize my AuthService import correctly in AuthGuard.....
import { AuthService } from './authservice';
should have been
import { AuthService } from './AuthService';
Note to self: imports are case sensitive
Related
I have a lazy loaded module. I want to load some data before the module load. I tried app_initializer but it didn't work because i have already an app_initializer in AppModule. Is there any way to do this?
You can use an Resolver that will handle your data before the module will load and navigate to your specific route as that loaded completly
resolver
import { Injectable } from '#angular/core';
import { APIService } from './api.service';
import { Resolve } from '#angular/router';
import { ActivatedRouteSnapshot } from '#angular/router';
#Injectable()
export class APIResolver implements Resolve<any> {
constructor(private apiService: APIService) {}
resolve(route: ActivatedRouteSnapshot) {
return this.apiService.getItems(route.params.date);
}
}
in your route
{
path: 'items/:date',
component: ItemsComponent,
resolve: { items: APIResolver }
}
and to acces the resolved data in the desired component
import { ActivatedRoute } from '#angular/router';
constructor(private route: ActivatedRoute) { }
items: any[];
ngOnInit() {
this.items= this.route.snapshot.data.items;
}
using a component with a resolver in a given module will not init that module until all the data is resolved
you can check also https://angular.io/api/router/Resolve
I have a component SurveyComponent and a service SurveyService. The SurveyService loads some data form JSON.
I add the SurveyService into the constructor from SurveyComponent.
constructor(public surveyService: SurveyService) {}
In the constructor from SurveyService I load some data.
constructor(private storageService: StorageService) {
this.finishedSurveys = [];
this.loadCurrentSurvey();
this.loadFinishedSurveys();
}
I put also the SurveyService into the app.module:
.....providers: [SurveyService].....
But the component loads before service so I haven no data in my component. Why does it happen? Can I solve this issue?
you can use resolve for the angular router. E.g.
Make a class APIResolver like
import { Injectable } from '#angular/core';
import { APIService } from './api.service';
import { Resolve } from '#angular/router';
import { ActivatedRouteSnapshot } from '#angular/router';
#Injectable()
export class APIResolver implements Resolve<any> {
constructor(private apiService: APIService) {}
resolve(route: ActivatedRouteSnapshot) {
return this.apiService.getItems(route.params.date);
}
}
Then, resolve your corresponding router like
{
path: 'items',
component: ItemsComponent,
resolve: { items: APIResolver }
}
You can refer to this link to use OnInit
#Component({selector: 'my-cmp', template: `...`})
class MyComponent implements OnInit {
ngOnInit() {
you call the service that you want to execute
}
}
I am attempting to use Server-Side Rendering in Angular (v4) to allow for better SEO.
Things work as expected until I add resolve on my route. Adding resolve causes HTML title to retain it's initial value when viewing source.
My Module:
import {
Injectable,
ModuleWithProviders,
NgModule
} from '#angular/core';
import {
ActivatedRouteSnapshot,
Resolve,
Router,
RouterModule,
RouterStateSnapshot
} from '#angular/router';
import {
Observable
} from 'rxjs/Rx';
import {
ArticleComponent
} from './article.component';
import {
Article,
ArticlesService,
UserService,
SharedModule
} from '../shared';
#Injectable()
export class ArticleResolver implements Resolve < Article > {
constructor(
private articlesService: ArticlesService,
private router: Router,
private userService: UserService
) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): any {
return this.articlesService.get(route.params['slug'])
.catch((err) => this.router.navigateByUrl('/'));
}
}
const articleRouting: ModuleWithProviders = RouterModule.forChild([{
path: 'article/:slug',
component: ArticleComponent,
resolve: {
article: ArticleResolver
},
data: {
preload: true
}
}]);
#NgModule({
imports: [
articleRouting,
SharedModule
],
declarations: [
ArticleComponent
],
providers: [
ArticleResolver
]
}) export class ArticleModule {}
My Component:
import {
Component,
OnInit
} from '#angular/core';
import {
ActivatedRoute,
Router,
} from '#angular/router';
import {
Title,
Meta
} from '#angular/platform-browser';
import {
AppComponent
} from '../app.component';
import {
Article,
} from '../shared';
#Component({
selector: 'article-page',
templateUrl: './article.component.html'
})
export class ArticleComponent implements OnInit {
article: Article;
constructor(
private route: ActivatedRoute,
private meta: Meta,
private title: Title
) {}
ngOnInit() {
this.route.data.subscribe(
(data: {
article: Article
}) => {
this.article = data.article;
}
);
this.title.setTitle(this.article.title);
}
}
I am new to Angular SSR so any guidance is greatly appreciated.
Instead of subscribing to route data, retrieve your results from the snapshot like this:
this.route.snapshot.data['article']
You also might need to register ArticlesService in your providers for the module.
As a side note, this import:
import {
Observable
} from 'rxjs/Rx';
is an RxJS antipattern. Please use the following import instead:
import {Observable} from 'rxjs/Observable';
I found that my primary service was referencing a secondary service that was attempting to return an authentication token from window.localStorage.
Attempting to access the client storage caused Angular SSR to omit the generation of source code for my component.
Thanks #Adam_P for helping me walk through it!
I'm struggling with connecting the ActivationGuard with an UserAuthenticationService, which is returning from server if the given user is logged or not. The main problem here imo is the fact, it's coming from an asynchro request.
I've got some code, made with help from one kind user on Stack. It's made with observables.
But the problem here is that I'm getting following error, when entering the protected components:
zone.js:388 Unhandled Promise rejection: Cannot read property 'do' of undefined;
My files:
ActivationGuard.ts
import {Injectable} from '#angular/core';
import {Router, CanActivate, RouterStateSnapshot, ActivatedRouteSnapshot} from '#angular/router';
import {Observable} from 'rxjs/Observable';
import {UserAuthenticationService} from './UserAuthenticationService';
#Injectable()
export class ActivationGuard implements CanActivate {
constructor(private router: Router, private userService: UserAuthenticationService) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.userService.isUserAuthenticated
.do(success => {
if (!success) {
this.router.navigate(['/']);
}
});
}
}
UserAuthenticationService.ts
import {Injectable, OnInit} from '#angular/core';
import {Http} from '#angular/http';
import {Observable} from "rxjs/Observable";
#Injectable()
export class UserAuthenticationService implements OnInit {
public isUserAuthenticated: Observable<boolean>;
username: string = 'admin';
constructor(private http: Http) {}
ngOnInit(){
this.isUserAuthenticated = this.http.get(`http://localhost/api/auth/isLogged/${this.username}`)
.map(res => res.json())
.share();
}
}
Looking forward for any kind of help. If you have any questions let me know. Thank you.
api.service.ts
import { Injectable, Inject } from '#angular/core';
import { Http, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/timeout';
import 'rxjs/add/operator/retry';
import { CacheService } from './cache.service';
import { AuthService } from '../services/auth.service';
import { CookieService } from 'angular2-cookie/core';
#Injectable()
export class ApiService {
constructor(
public _http: Http,
private _auth: AuthService,
private _cookie: CookieService,
#Inject('isBrowser') public isBrowser: boolean
) {}
get(){
console.log(this._cookie.get('Token'));//undefined
}
}
controller.component.ts
import { Component, OnInit, ChangeDetectionStrategy } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { ApiService } from './api.service';
import { ReviewComponent } from '../shared/+review/review.component';
import { CookieService } from 'angular2-cookie/core';
// import { ModelService } from '../shared/model/model.service';
#Component({
selector: 'mall',
templateUrl: './mall.component.html',
styleUrls:['./mall.component.css'],
providers: [ ApiService, CookieService ]
})
export class MallComponent implements OnInit {
constructor(private _api: ApiService, private route: ActivatedRoute, private _cookie: CookieService) {}
ngOnInit(){
this._cookie.get('Token');// Token => value
this._api.get(); //Token => undefined
}
}
I don't understand this behavior. The cookie exist when i access in controller directly but is undefined when i access through service.
Is there any way to access cookie through services?
using https://github.com/salemdar/angular2-cookie with angular universal.
Maybe this?
ngOnInit(){
this._cookie.put('Token', WHATEVER_TOKEN_IS);// Token => value
console.log(this._api.get('Token')); //Token => undefined
}
and then
api-service
export class ApiService {
constructor(
readonly _http: Http,
private _auth: AuthService,
private _cookie: CookieService,
#Inject('isBrowser') public isBrowser: boolean
) {}
get() {
const token = this._cookie.get('Token');
console.log(token);
return token;
}
}
This might be late, but I went through the same problem.
I was not defining the base path as "/". So what was happening is that the cookie was being set for the default path where I was.
Eg. I was at site.com/auth/
Cookie would get saved at path "/auth"
If I save a cookie like
this.cookieService.set('token', token, null, "/");
then problem is solved.
Hope this helps further devs.
It was my mistake to add CookieService in component providers which initiate a new instance of service which was causing the issue.
#Component({
selector: 'mall',
templateUrl: './mall.component.html',
styleUrls:['./mall.component.css'],
providers: [ ApiService, CookieService ] //<--- here
})
CookieService should only be imported into AppComponent(root) to make a single instance available to other components.