1

I have an encoded URL parameter that I need to decode when the app initializes. I need to do this in a provider because that object needs to be available to routing guards as well as components.

I have the following provider. It grabs the incoming query parameter and parses it into an interface.

import { Injectable } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { MyObject } from './data.d';

@Injectable()
export class DataProvider {

    constructor(private route: ActivatedRoute) { }

    public get() {
      debugger;
      this.route.queryParams.subscribe(params => {
        let payload = params['data'] !== undefined ? Base64.decode(params['data']) : null;
        return payload ? <MyObject>JSON.parse(payload) : null;
      });
    }
}

I'm having trouble with the syntax on how to add this to my provider array, and how to access it from other places in the app (specifically routing guards). What's the correct way to set this up?

EDIT: It seems that perhaps query parameters aren't available at the time the provider is initialized. Where can I parse the query param in a place other than the component? I need to be able to access it in the routing guard.

app.guard.ts:

import { Injectable } from '@angular/core';
import { Router, RouterStateSnapshot, ActivatedRouteSnapshot, CanActivate } from '@angular/router';
import { SessionService } from './services/session.service';
import { DataProvider } from './data.provider';

@Injectable()
export class SessionIsValidGuard implements CanActivate {

    constructor(
        private router: Router, private sessionService: SessionService, private dataProvider: DataProvider
    ) {}

    /*
    * Main guard to verify that session ID is valid before proceeding
    */
    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
      // When the below is called, dataProvider.get() returns null
      let params = this.dataProvider.get();
      if (this.sessionService.isValidSessionId(params.transactionId)) {
        return true;
      } else {
        this.router.navigate(['/error', { error: 'Invalid session' }]);
      }
    }
}

app.component.ts:

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { DataProvider } from './data.provider';

/*
* Main app component that houses all views.
*/
@Component({
    selector: 'app-comp',
    templateUrl: './app.component.html'
})

export class AppComponent implements OnInit {

    constructor(private router: Router, private route: ActivatedRoute, private srcService: SRCService, private dataProvider: DataProvider) {}

    ngOnInit() {
      // Here dataProvider.get() returns the expected value
      console.log(this.dataProvider.get());
    }
}
Paul Erdos
  • 1,355
  • 2
  • 23
  • 47

4 Answers4

1

You can simply go with

@Injectable({
    providedIn: 'root'
})

to have your provider available everywhere.

Kevin FONTAINE
  • 845
  • 8
  • 17
  • Isn't that only available in Angular 6? I'm using Angular 5 and can't upgrade at the moment. – Paul Erdos Mar 12 '19 at 16:37
  • It seems so. Would this help : https://stackoverflow.com/questions/50489412/angular-6-providedin-how-to-customize-the-injectable-provider-for-dependenc ? – Kevin FONTAINE Mar 12 '19 at 16:40
1

I guess many guys tell you about declare it to NgModule, but you will have another problem that you cant return a value from a subscribe.

So you can read about how to handle that here https://angularfirebase.com/lessons/sharing-data-between-angular-components-four-methods/

anthony willis muñoz
  • 2,346
  • 3
  • 16
  • 33
1

You can Register it in the AppModules under providers by adding it to the providers array.After that to access it through the route guard you can pass the reference to the constructor of the class that implements the guard

And in AppModules.ts it looks like this

@NgModule({
  imports: [
    CommonModule,

  ],
  exports: [],
  declarations: [],
  providers: [

    AdminAuthGaurd,

  ]

})

RoutGaurd.ts should look like this

export class AdminAuthGaurd implements CanActivate {
    constructor(private authService: AuthenticationService, private router: Router) { }
    canActivate(): boolean {
        if(this.authService.getUserpermissions()==="Admin"){
                return true;
        }
        else{
            this.router.navigate(["/login"]);
        }

    }
}
Sathiraumesh
  • 5,949
  • 1
  • 12
  • 11
0

Import DataProvider service into app.module.ts file

@NgModule({
   providers: [DataProvider]
})

Then you can access it from other components

constructor(private dataProvider: DataProvider)

this.dataProvider.get();

EDIT: Decode URL in routing guard only once then use it in component also.

Access Query Parameter into routing guard file:

export class RouteGuard implements CanActivate {
   public params: any;
   canActivate(
     route: ActivatedRouteSnapshot,
     state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | 
     boolean {
        this.params = route.queryParams;
        if (this.sessionService.isValidSessionId(params.transactionId)) {
           return true;
        } else {
           this.router.navigate(['/error', { error: 'Invalid session' }]);
        }
     }

     get() {
         let payload = this.params['data'] !== undefined ? 
         Base64.decode(this.params['data']) : null;
         return payload ? <MyObject>JSON.parse(payload) : null;
       });
     }
}

Use this decoding method into component

constructor(private routeGaurd: RouteGaurd)

this.routeGaurd.get();
Zarna Borda
  • 695
  • 1
  • 6
  • 22
  • Pretty straightforward accessing it from a component but what about the routing module? When I try to access dataProvider.get() from the routing guard it's null. – Paul Erdos Mar 12 '19 at 17:00
  • Updated. The main issue seems to be that query parameters aren't available when the DataProvider service is initialized. – Paul Erdos Mar 12 '19 at 17:25
  • have you tried to access the URL query parameter into a route guard? – Zarna Borda Mar 12 '19 at 17:43
  • I can access the query params in route guard, but then I would have to decode the parameter twice - once in the routing guard and again in the component. I'd rather parse it once and use the object wherever I need it. – Paul Erdos Mar 12 '19 at 18:05
  • Ohk. Then Decode the params in guard and you can use that in component – Zarna Borda Mar 12 '19 at 18:10
  • How do I pass the value from the guard to the component? – Paul Erdos Mar 12 '19 at 18:18