3

I have a service in my Angular 5 project that holds some configuration state:

@Injectable
export class FooService {

    isIncognito: boolean = null;

    constructor() {

        // I want Angular to wait for this to resolve (i.e. until `isIncognito != null`):
        FooService.isIncognitoWindow()
            .then( isIncognito => {

                this.isIncognito= isIncognito;
            } );
    }


    private static isIncognitoWindow(): Promise<boolean> {
    // https://stackoverflow.com/questions/2909367/can-you-determine-if-chrome-is-in-incognito-mode-via-a-script
    // https://developer.mozilla.org/en-US/docs/Web/API/LocalFileSystem

        return new Promise<boolean>( ( resolve, reject ) => {

            let rfs = window['requestFileSystem'] || window['webkitRequestFileSystem'];
            if( !rfs ) {
                console.warn( "window.RequestFileSystem not found." );
                resolve( false );
            }

            const typeTemporary = 0;
            const typePersistent = 1;

            // requestFileSystem's callbacks are made asynchronously, so we need to use a promise.
            rfs(
                /*type: */ typeTemporary,
                /* bytesRequested: */ 100,
                /* successCallback: */ function( fso: WebKitFileSystem ) {

                    resolve( false );
                },
                /* errorCallback: */ function( err: any /* FileError */ ) {

                    resolve( true );
                }
            );
        } );
    }
}

This service is used by many Components in my project and is passed as a constructor argument. For example:

@Component( {
    moduleId: module.id,
    templateUrl: 'auth.component.html'
} )
export class AuthComponent implements OnInit {

    constructor( private fooService: FooService ) {
    }

    ngOnInit(): void {

        // do stuff that depends on `this.fooService.isIncognito != null`
    }
}

Ideally I'd like Angular to wait for the FooService::isIncognitoWindow() promise to resolve (the resolution happens instantly, but not synchronously) first before injectinting the FooService into the other components.

An alternative solution is changing FooComponent.isIncognito's property to a Promise<boolean> and resolving it again and calling the rest of ngOnInit via a callback, but that means every component will cause the promise's main function to be invoked again - so that means possibly changing it to a buffered Observable or Subject instead, and that's getting needlessly complicated - all for a single boolean value. It also means refactoring lots of code I'd rather not have to do.

Dai
  • 141,631
  • 28
  • 261
  • 374
  • 1
    You could use APP_INITIALIZER token to resolve that isIncognito value before the application starts, and then in next that value to components/services – David May 11 '18 at 19:47
  • @David That sounds promising - can you repost that in an Answer with a brief example? That way I can accept it. – Dai May 11 '18 at 20:03

2 Answers2

6

What you can do is use APP_INITIALIZER token to make sure that your FooService is initialised before the application starts. With the code below, angular will only start the application when the promise returned by the load method has resolved.

FooService.ts

@Injectable()
export class FooService {

    isIncognito: boolean = null;

    public load()// <=== the method to be called and waited on during initialiation
  {
      return FooService.isIncognitoWindow().then(isIncognito =>
      {
        this.isIncognito = isIncognito;
      });

  }

    private static isIncognitoWindow(): Promise<boolean> {
    { //your method here

app.module.ts

export function loadFooService(fooService: FooService): Function 
{
  return () => { return fooService.load() }; 
}


@NgModule({
  imports:      [ BrowserModule, FormsModule ],
  declarations: [ AppComponent, HelloComponent ],
  bootstrap:    [ AppComponent ],

  providers: [ FooService,
  { provide: APP_INITIALIZER, useFactory:loadFooService , deps: [FooService], multi: true },]
})

See stackblitz example

David
  • 33,444
  • 11
  • 80
  • 118
0

If isIncognitoWindow returns Promise try this:

      await awaitFooService.isIncognitoWindow()
            .then( isIncognito => {
                this.isIncognito= isIncognito;
            } );
Antoniossss
  • 31,590
  • 6
  • 57
  • 99