Emit a boolean from AuthGuard - Angular 4 - javascript

I use JWT in a server node to check if the user is connected.
AuthGuard in an observable, it will check if my user is connected :
canActivate(): Observable<any> {
if ( !localStorage.getItem('currentUser')){
return Observable.of(false);
}
// Authorization with JWT Token
const options = {
headers: new HttpHeaders().set('Authorization', 'BEARER ' + localStorage.getItem('currentUser') )
};
//Check if the JWT Token is always available and if the signature is right
return this.http.get(environment.api + 'checkToken', options )
.map(() => true).catch(() => {
return Observable.of(false);
});
}
Now I would like to emit the boolean from canActivate to an other component. My other component is the nav-bar of my website. I want to hide some link in the navbar when the user is disconnected. However, I would like to displays some elements when the user is connected.
But even when I use an observable in the constructor of my navbar component, it will check just one time if the user is connected.
Do you have any ideas to emit the boolean from my AuthGuard to my navbar component ?
Thank you

You can use resolve on the route and do almost the same things in resolver class. Then you can access boolean value (or user object itself if you resolve it) from the component by injecting ActivatedRouteSnapshot.
Or if you don't want round trips then you can subscribe to the observable before you return it from canActivate and emit the value to some service that contains Subject for example so you would be able to inject this service into your component and use that subject.

Related

tap is not waiting for response from backend service call in angular

how to subscribe to response when tap operator is used in the service.
Have anyone know how to resolve this?
edit(status) {
dataObj.val = status;
// call post service with status..
this.service
.update(dataObj)
.pipe(takeUntil(this._ngUnsubscribe$))
.subscribe(() => {
//i would like to wait until response come from backend and then navigate to the page so i get data over there.
if (res.status === 'Success') {
this.router
.navigate(['../../success'], {
relativeTo: this.route,
})
.then(() => {});
} else {
this.location.back();
}
});
}
//akita store service
update(
obj: any,
): Observable < any > {
return this.service.update(obj).pipe(
delay(800),
map((data: RestfulResponse < any > ) => data.data),
tap((data: anny) => {
this.store.update((state) => {
state.updateValue = data; // value is not updating and it is navigating to route
});
}),
);
}
//post service
update(obj){
//post call
}
Is there any way I can use tap and in service side and subscribe on component side?
I know I can use finalize but it is not helping for writing conditions inside.
The tap operator, by design, handles side effects which don't happen within the context of your observable pipeline. This means that your pipeline will never wait for results from the tap itself. I don't recommend using it in this manner. Under most circumstances, I only use tap for debugging.
If you are waiting for a particular state change, you should create a separate observable, selecting from your store, to watch the state for the expected change.
If you want to trigger an additional action when something happens, I recommend using ngrx Effects to achieve this.
Have a look at this post, where I talked about how to implement a similar use case:
https://stackoverflow.com/a/64491398/166850
You should also strive to set up reducers that apply your state changes, rather than updating the store directly.
Consider each of the following as a separate concern that you can implement independently of the others:
When user does an edit, trigger an edit action.
The reducer should update the state based on the edit action (for example, to show that a save is in progress)
When the edit action is triggered, trigger an effect. The app should make an HTTP call to save the change, then trigger a save finished action.
When the save is finished, the router navigation should be triggered.
This separates your code into multiple units which are easy to test and verify independently.
If #1 produces an action which is consumed by your reducer (#2), you can also create an ngrx Effect for #3 which listens for the same action, handles the HTTP call using switchMap, then triggers another action to signal that it's done.
Edit
Here's a simple example. The first time an action called APP_LOADED is triggered (from the AppComponent), this Effect makes an HTTP call to get data from the server, then triggers an action using the response data as the action payload.
The actual HTTP call is delegated to another service, the HttpMyConfigDataService, which simply calls HttpClient and returns an Observable.
#Injectable({
providedIn: 'root'
})
export class LoadMyConfigEffect {
constructor(
private httpMyConfigDataService: HttpMyConfigDataService,
private action$: Actions
) {
}
loadMyConfigData$ = createEffect(() => {
return this.action$.pipe(
filter((action) => action.type === 'APP_LOADED'),
take(1),
switchMap(() => this.httpMyConfigDataService.get().pipe(
map(data => {
return {type: 'MY_CONFIG_DATA_LOADED', payload: data};
}),
catchError(err => {
console.error('Error loading config data.', err);
return of({type: 'CONFIG_LOAD_ERROR', payload: err.message, isError: true);
})
))
);
});
}

Angular correct login flow and structure

I am trying to implement a frontend with angular, but I am not sure if my login and overall flow is correct.
I have a login component for the login page which sends the user information to an auth service for authentication, then I save the auth token in the localhost and send it with every get request for the user data, but also I need to check for expired jwt token so I logout the user and clear the localstorage from the information. But I am not sure where this logout should happen.
Also my home page displays 2 different views if the user is logged or not, so I have a boolean in the service that I check. This is my flow:
The login component: Here the error is bind to the html lets say if credentials are invalid
export class LoginComponent implements OnInit {
error : string;
constructor(private authService : AuthService) { }
ngOnInit() {
this.authService.login("Robocop1", "password").subscribe(
data =>{
localStorage.setItem('Authorization', data['token'])
this.auth.isLoggedIn = true
},
err => this.error = err['error']
);
}
}
Service component:
export class AuthService {
isLoggedIn : boolean = false
constructor(private httpClient : HttpClient) { }
login(username, password) {
return this.httpClient.post('http://localhost:8090/login', {
username,
password
})
}
}
This is my home component that checks first if the user is logged:
export class HomeComponent implements OnInit {
isLoggedIn : boolean
constructor(private auth : AuthService) { }
ngOnInit() {
this.isLoggedIn = this.auth.isLoggedIn
}
}
And displays different part of the html:
<div *ngIf="!isLoggedIn">
<p>
hey user
</p>
</div>
<div *ngIf="isLoggedIn">
<p>
not logged
</p>
</div>
So my question is is injetcing a dependencies in the home component ok just to check for single boolean and is there a better way to do it.
Also I can have another data component where I get user data from the database. In the post request I am sending the authentication token so I will have something like:
this.dataService.getItems().subscribe(
data =>{
this.userData = data
},
err => {
if(err['error' === 'Jwt token has expired'){
this.authService.logout()
}else{
this.error = err['error']
}
}
);
So is again injecting dependency just to call a logout method ok? should this logout method be in the authService or elsewhere?
So my question is is injetcing a dependencies in the home component ok
just to check for single boolean and is there a better way to do it.
If your application will be about a couple of simple pages and will not expand, your approach might be enough but for it the best practise is using Angular Route Guards
A route guard is a CanActivate implementation in which you implement your authentication/authorization logic to guard your routes (pages)
So is again injecting dependency just to call a logout method ok?
should this logout method be in the authService or elsewhere?
This should be done implementing an HttpInterceptor. So that you don't need to handle each http call for handling faulty responses or adding authorization tokens. Catching the error response inside your http interceptor and logging out is the way to go. That way you do not have to inject your corresponding service into each place this is required.
HttpInterceptor is also not a big deal. You can follow this step by step guide and implement your own

rxjs observable: Using some kind of await?

I've added an interceptor for my HTTP requests where I have to use the access token of my user instance. In my app component I initialise my user:
app.component.ts
private async restoreUser(): Promise<UserModel | any> {
// ... some view stuff
return this.userService.restore()
// login instance could be found
.then(async () => {
// ... some view stuff
})
// local storage is empty -> login is necessary
.catch(async () => {
// ... some view stuff
this.subscription = this.networkSrv.getNetworkStatus()
.subscribe((status: ConnectionStatus) => {
if (status === ConnectionStatus.Online) {
// ... some view stuff
} else {
// ... some view stuff
}
});
});
}
http.interceptor.ts
return this.userSrv.user.pipe(
map((user: UserModel) => request.clone(
{setParams: {'access-token': user.accessToken}}
)),
mergeMap(request => next.handle(request))
);
Now I would like to do a request by initialising my app. The problem is, that the user instance is empty and the application throws an error. Is there a way to do something like await -> so that the user instance is set?
Example:
this.transmissionSrv.restoreQueue().then((projects: ProjectModel[]) => {
this.transmissionSrv.transmitProjects(projects, true).subscribe(console.log);
});
Currently, I use the setTimeout-method, but that isn't the way I should do it, right? In addition, sorry for not being consistent by using Observer; Ionic often uses Promises(?)
You should try adding a filter before your map. Using the filter, your map wont get call until the user is set.
return this.userSrv.user.pipe(
filter(Boolean),
map((user: UserModel) => request.clone(
{setParams: {'access-token': user.accessToken}}
)),
mergeMap(request => next.handle(request))
);
There are a couple of ways you could solve this.
Synchronously: Use an Angular APP_INITIALIZER (see here) to make the backend call and ensure the user object is present when the app bootstraps.
Asynchronously: Modify your existing application to store the user instance in an RxJs BehaviorSubject in a service somewhere and have components that depend on it subscribe to that BehaviorSubject wherever the user instance is needed. When the service constructs, have it make the backend call and stick the completed user instance inside the BehaviorSubject (userSubject.next(user)) when it's complete.

How do I catch changes in state from Firebase Auth?

I am using Angular 2 with angularfire library. I am having trouble catching changes in the user's state.
First) Logging in works fine. I see the user come through to my subscribe function and all is dandy.
However, When I disable that user on the Firebase console...The user state has changed. Why am I not seeing that change in state come through in my app component?
I would like to captures errors of different types, and handle them according to the error code. To do this, I created a service here:
#Injectable()
export class CZAuthService {
constructor(private af: AngularFire){ }
getAuthState() : Observable<FirebaseAuthState>{
return this.af.auth.catch((error)=>{
console.log(error);
return this.af.auth;
})
}
}
I would like to catch exceptions here in this catch operator, and then return the stream to maintain connection to the stream.
Then in my root app component, I am subscribing to this stream and handling when the user is signed in:
this.auth.getAuthState().subscribe((user)=>{
console.log(user);
if(user != null){
this._store.dispatch(new UserLogInSuccessAction(user))
}
});

Angular 2 transfer ajax call response to another component

I just started playing with angular 2 and i've ran into a small problem, that i ve searched for in various forms and also angulars documentation.
I've managed to make a service that makes a call and then i want in a component when i press a button to load another component with dynamicload component and have access to the ajax result.
The problem is that I can t figure out how to do that..
The question is how can I make the result accesible in other components using Observables or Promises method.
If I understood correctly your question, you are looking a way to insert a data from request to another nested component.
I hope this image will clarify for you the data flow for this case.
Your Root component is calling a service method which returns for you promise object.
Then you map needed data from response to the component model inside Root Component constructor.
And your Child component should be subscribed for the model which you was preparing in previous step.
ngOnInit() {
this.dataService.getSomeData()
.subscribe((data: IData) => {
this.data = data;
});
}
Just a short example above how to set model in the root component from the promise object to the local model.
New research:
There is another way to fill your components by data from api's. You can use EventEmitter to emit event from service, and then, you can subscribe for this event inside you created components, so they will get a data, each time there will be called the service. Here is nice example of this strategy in the first answer. Service Events
Hope it will help you, let me know if you will need additional info!
Just create a service, then inject the service where you want.
Here it's an example how to share a service ajax data across many components without making the request twice :
https://stackoverflow.com/a/36413003/2681823
the Service:
#Injectable()
export class DataService {
constructor(private http: Http) { }
private _dataObs = new ReplaySubject<request>(1);
getData(forceRefresh?: boolean) {
// On Error the Subject will be Stoped and Unsubscribed, if so, create another one
this._dataObs = this._dataObs.isUnsubscribed ? new ReplaySubject(1) : this._dataObs;
// If the Subject was NOT subscribed before OR if forceRefresh is requested
if (!this._dataObs.observers.length || forceRefresh) {
this.http.get('http://jsonplaceholder.typicode.com/posts/2')
.subscribe(
requestData => {
this._dataObs.next(requestData);
},
error => this._dataObs.error(error));
}
return this._dataObs;
}
}
the Component:
#Component({
selector: 'child',
template : `<button (click)="makeRequest()" class="btn">Click me!</button>`
})
export class Child {
constructor(private _dataService: DataService) { }
makeRequest() {
this._dataService.getData().subscribe(
requestData => {
console.log('ChildComponent', requestData);
}
}
}
A full working example/plunker can be found here : http://plnkr.co/edit/TR7cAqNATuygDAfj4wno?p=preview

Categories

Resources