My goal is to get "clientId" asynchronously from the AppConfigService and use it as the "GoogleLoginProvider" function's "clientId" before the app starts.
I could put it in an environment variable but, in my specific case, it is not an option.
I'm using Angular 8.
import { APP_INITIALIZER } from '@angular/core';
export function getGoogleClientId(appConfigService: AppConfigService) {
return () => appConfigService.getGoogleClientId().toPromise().then((clientId) => {
// service reliably returns clientId here
});
}
const getGoogleId: Provider = {
provide: APP_INITIALIZER,
useFactory: getGoogleClientId,
deps: [AppConfigService],
multi: true
}
@NgModule({
providers: [
{
provide: 'SocialAuthServiceConfig',
useValue: {
autoLogin: false,
providers: [{
id: GoogleLoginProvider.PROVIDER_ID,
provider: new GoogleLoginProvider(clientId), //<-- How do I get the service's "clientId" here?
}],
} as SocialAuthServiceConfig
}
]
})
export class AppModule {}
Your problem is that you're using useValue to inject an object but you need to use useFactory to create a changeable, dependent value based on information unavailable before run time which allows dependencies (API service, config service, etc).
Then I suggest modifying the library you're using at this moment (angularx-social-login) to allow the behavior that you want.
However, I was reading the library code and I realized that they accept an object and a promise!
You can check It here
Hence, I create an example to handle promise and fetch our config from our server (API).
app.module.ts
export function AppConfigServiceFactory(
configService: AppConfigService
): () => void {
return async () => await configService.load();
}
@NgModule({
imports: [BrowserModule, FormsModule, SocialLoginModule, HttpClientModule],
declarations: [AppComponent, HelloComponent],
bootstrap: [AppComponent],
providers: [
{
provide: APP_INITIALIZER,
useFactory: AppConfigServiceFactory,
deps: [AppConfigService],
multi: true
},
{
provide: "SocialAuthServiceConfig",
useValue: new Promise(async resolve => {
// await until the app config service is loaded
const config = await AppConfigService.configFetched();
resolve({
autoLogin: false,
providers: [
{
id: GoogleLoginProvider.PROVIDER_ID,
provider: new GoogleLoginProvider(config.googleClientId)
}
]
} as SocialAuthServiceConfig);
})
}
]
})
export class AppModule {}
app.config.service
export class AppConfigService {
static config: AppConfig | null = null;
constructor(private api: ApiService) {}
static configFetched(): Promise<AppConfig> {
return new Promise(async resolve => {
// wait for the app config service is loaded (after 3000 ms)
const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));
const waitFor = async function waitFor(f) {
// check each 500 ms
while (!f()) await sleep(500);
return f();
};
await waitFor(() => AppConfigService?.config);
resolve(AppConfigService.config);
});
}
async load(): Promise<AppConfig> {
try {
// simulating HTTP request to obtain my config
const promise = new Promise<AppConfig>(resolve => {
// after 3000 ms our config will be available
setTimeout(async () => {
const config: AppConfig = await this.api.getConfig().toPromise();
AppConfigService.config = config;
resolve(config);
}, 3000);
}).then(config => config);
return promise;
} catch (error) {
throw error;
}
}
}
My complete solution is here on stackblitz.
i developed another solucion using @bjdose answer
as angularx-social-login can receive a Promise<SocialAuthServiceConfig>
However, I was reading the library code and I realized that they accept an object and a promise!
make a function that returns a Promise with the config, first get the necessary data from your backend in my case the client id string and then return the config object
/**
* return a Promise with *angularx-social-login* config object
*/
async function getGoogleLoginConfig(): Promise<SocialAuthServiceConfig> {
const clientId = await fetchGoogleClientId()
return Promise.resolve(
{
autoLogin: true,
providers: [
{
id: GoogleLoginProvider.PROVIDER_ID,
provider: new GoogleLoginProvider(clientId)
}
],
onError: (err) => {
console.error(err);
}
} as SocialAuthServiceConfig)
}
/**
* get google client id from backend
*/
async function fetchGoogleClientId(): Promise<string> {
let url = `${environment.auth.googleId}`
let response = await fetch(url,
{
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
})
let data = await response.json()
//change here to your backend response structure
return data.client_id
}
lastly add angularx-social-login to your providers
providers: [
{
provide: "SocialAuthServiceConfig",
useValue: getGoogleLoginConfig()
}
]
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