diff --git a/README.md b/README.md index dc2e4398..fdaa33c5 100644 --- a/README.md +++ b/README.md @@ -239,4 +239,35 @@ export class AppComponent implements OnInit { } ``` -6. Enjoy! +### 6. Using signals +In order to use frontegg signals you will have to call it from the frontegg services and assign them to the component state + +``` +import { Component, OnInit, Signal } from '@angular/core'; +import { FronteggAppService, FronteggAuthService, AuthState } from '@frontegg/angular'; +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.scss'], +}) +export class AppComponent implements OnInit { + user: Signal + authenticated: Signal + constructor(private fronteggAppService: FronteggAppService, + private fronteggAuthService: FronteggAuthService, + private router: Router) { + this.user = this.fronteggAuthService.signals.user + this.authenticated = this.fronteggAppService.signals.isAuthenticated + } +} +``` + +Then access it from the html component file +``` +
+

Authenticated: {{authenticated()}}

+

Authenticated as: {{user()?.name}}

+
+``` + +7. Enjoy! \ No newline at end of file diff --git a/projects/frontegg-app/README.md b/projects/frontegg-app/README.md index 8c3c15f0..3683b86f 100644 --- a/projects/frontegg-app/README.md +++ b/projects/frontegg-app/README.md @@ -198,4 +198,35 @@ export class AppComponent implements OnInit { } ``` -6. Enjoy! +### 6. Using signals +In order to use frontegg signals you will have to call it from the frontegg services and assign them to the component state + +``` +import { Component, OnInit, Signal } from '@angular/core'; +import { FronteggAppService, FronteggAuthService, AuthState } from '@frontegg/angular'; +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.scss'], +}) +export class AppComponent implements OnInit { + user: Signal + authenticated: Signal + constructor(private fronteggAppService: FronteggAppService, + private fronteggAuthService: FronteggAuthService, + private router: Router) { + this.user = this.fronteggAuthService.signals.user + this.authenticated = this.fronteggAppService.signals.isAuthenticated + } +} +``` + +Then access it from the html component file +``` +
+

Authenticated: {{authenticated()}}

+

Authenticated as: {{user()?.name}}

+
+``` + +7. Enjoy! \ No newline at end of file diff --git a/projects/frontegg-app/src/lib/frontegg-app.service.ts b/projects/frontegg-app/src/lib/frontegg-app.service.ts index c998c41d..078499e9 100644 --- a/projects/frontegg-app/src/lib/frontegg-app.service.ts +++ b/projects/frontegg-app/src/lib/frontegg-app.service.ts @@ -1,13 +1,14 @@ import { Injectable, NgZone, Inject } from '@angular/core'; import { Route, Router, ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router'; import { FronteggApp, initialize } from '@frontegg/js'; -import { AuthPageRoutes,FronteggState, isAuthRoute } from '@frontegg/redux-store'; +import { AuthPageRoutes, FronteggState, isAuthRoute } from '@frontegg/redux-store'; import { FronteggAppOptions, FronteggCheckoutDialogOptions } from '@frontegg/types'; import { BehaviorSubject, Observable } from 'rxjs'; import { ContextHolder, RedirectOptions, FronteggFrameworks } from '@frontegg/rest-api'; import { FronteggComponent } from './frontegg.component'; import sdkVersion from '../sdkVersion'; import angularCoreVersion from '@angular/core/package.json'; +import { mapObservablesToSignals } from './v16'; export class FronteggAppOptionsClass implements FronteggAppOptions { contextOptions: FronteggAppOptions['contextOptions'] = { @@ -66,6 +67,10 @@ export class FronteggAppService { return this.isAuthenticatedSubject.asObservable(); }; + get signals() { + return mapObservablesToSignals(this); + } + constructor(@Inject(FronteggAppOptionsClass) private config: FronteggAppOptions, public router: Router, private ngZone: NgZone) { if (!this.config) { throw Error('Need to pass config: FronteggConfigOptions in FronteggAppModule.forRoot(config)'); @@ -117,8 +122,8 @@ export class FronteggAppService { ...this.mapAuthComponents, { path: '', - canActivate: [ FronteggLoadGuard ], - children: [ ...this.router.config ], + canActivate: [FronteggLoadGuard], + children: [...this.router.config], }, ]); const initialFronteggState = this.fronteggApp.store.getState() as FronteggState; diff --git a/projects/frontegg-app/src/lib/frontegg-auth.service.ts b/projects/frontegg-app/src/lib/frontegg-auth.service.ts index 7688ae83..d572893e 100644 --- a/projects/frontegg-app/src/lib/frontegg-auth.service.ts +++ b/projects/frontegg-app/src/lib/frontegg-auth.service.ts @@ -86,6 +86,7 @@ import { import type { FronteggState, ActivateAccountState, SocialLoginState } from '@frontegg/redux-store'; import { Observable } from 'rxjs'; import { Router } from '@angular/router'; +import { mapObservablesToSignals } from './v16'; interface AuthSubStates { field: Partial; @@ -225,6 +226,10 @@ export class FronteggAuthService { return this.ssoACSSubject.asObservable(); } + get signals() { + return mapObservablesToSignals(this); + } + constructor(private fronteggAppService: FronteggAppService, private router: Router) { const authSubStates: AuthSubStates[] = [ { field: 'acceptInvitationState', subject: this.acceptInvitationStateSubject }, diff --git a/projects/frontegg-app/src/lib/v16/consts.ts b/projects/frontegg-app/src/lib/v16/consts.ts new file mode 100644 index 00000000..b70c31d0 --- /dev/null +++ b/projects/frontegg-app/src/lib/v16/consts.ts @@ -0,0 +1 @@ +export const SIGNALS_ERROR_MESSAGE = 'Signals only supported from angular version 16, in order to use frontegg signals please upgrade your angular version to 16 or higher'; \ No newline at end of file diff --git a/projects/frontegg-app/src/lib/v16/index.ts b/projects/frontegg-app/src/lib/v16/index.ts new file mode 100644 index 00000000..2cafd1b4 --- /dev/null +++ b/projects/frontegg-app/src/lib/v16/index.ts @@ -0,0 +1 @@ +export * from './mapObservablesToSignals'; \ No newline at end of file diff --git a/projects/frontegg-app/src/lib/v16/isAngular16.ts b/projects/frontegg-app/src/lib/v16/isAngular16.ts new file mode 100644 index 00000000..ede2c111 --- /dev/null +++ b/projects/frontegg-app/src/lib/v16/isAngular16.ts @@ -0,0 +1,4 @@ +import angularCoreVersion from '@angular/core/package.json'; + +const major = Number(angularCoreVersion?.version?.split('.')?.[0] ?? 0); +export const isAngular16 = major >= 16; diff --git a/projects/frontegg-app/src/lib/v16/mapObservablesToSignals.ts b/projects/frontegg-app/src/lib/v16/mapObservablesToSignals.ts new file mode 100644 index 00000000..3923ea1a --- /dev/null +++ b/projects/frontegg-app/src/lib/v16/mapObservablesToSignals.ts @@ -0,0 +1,19 @@ +import { SIGNALS_ERROR_MESSAGE } from './consts'; +import { isAngular16 } from './isAngular16'; +import { Signal, toSignal } from './signals'; + +export const mapObservablesToSignals = (obj: T) => { + if (!isAngular16) { + throw new Error(SIGNALS_ERROR_MESSAGE) + } + const observables = Object.getOwnPropertyNames(Object.getPrototypeOf(obj)) + .filter(prop => prop.endsWith("$")); + + const fields: Record = {}; + observables.forEach(observable => { + const fieldName = observable.slice(0, -1); // Remove the trailing '$' + + fields[fieldName] = toSignal(obj[observable as keyof typeof obj]); + }) + return fields; +} diff --git a/projects/frontegg-app/src/lib/v16/signals.ts b/projects/frontegg-app/src/lib/v16/signals.ts new file mode 100644 index 00000000..f372bad7 --- /dev/null +++ b/projects/frontegg-app/src/lib/v16/signals.ts @@ -0,0 +1,21 @@ +/** @ts-ignore Signal is not exported from angular/core before version 16 **/ +import type { Signal } from '@angular/core'; +import { SIGNALS_ERROR_MESSAGE } from './consts'; +import { isAngular16 } from './isAngular16'; + +let toSignal = (observable: any) => { + if (!isAngular16) { + throw new Error(SIGNALS_ERROR_MESSAGE) + } +} + +if (isAngular16) { + const toSignalImportPath = '@angular/core/rxjs-interop' + import(toSignalImportPath) + .then(({ toSignal: angularToSignal }) => { + toSignal = angularToSignal; + }).catch(() => { }); +} + + +export { toSignal, Signal }