Angular CLI: 6.0.3
Node: 8.9.4
OS: darwin x64
Angular: 6.0.2
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router
Package Version
-----------------------------------------------------------
@angular-devkit/architect 0.6.3
@angular-devkit/build-angular 0.6.3
@angular-devkit/build-optimizer 0.6.3
@angular-devkit/core 0.6.3
@angular-devkit/schematics 0.6.3
@angular/cli 6.0.3
@ngtools/webpack 6.0.3
@schematics/angular 0.6.3
@schematics/update 0.6.3
rxjs 6.1.0
typescript 2.7.2
I have a static method .forRoot to configure my authorization module with next code
@NgModule({
imports: [
AuthModule.forRoot<Auth0AdapterConfig>({
adapterConfig: {
...
redirectUri: `${location.protocol}//${location.host}/callback`,
scope: 'openid'
}
})
],
When I use --aot option (explicitly or implicitly via --prod) the redirectUri parameter is replaced by hardcoded value redirectUri: "null//null/callback"
AOT must preserve location variable
Doubt it's a bug. You have a string interpolation expression there, which will be executed by Angular-compiler and replaced with a static value at compile time, whereas in Jit mode the expression gets executed at run-time, which is a side effect you rely on.
@AlexGS74 why it's executed by the Angular compiler? string interpolation is TS thing (as part of ES6 spec)
@fetis it is because window does not exist during build time. You will have to use Dependency Injection and you can use "useFactory" to return window and access it that way
@fetis This is whole point of AOT, as it's stated here (a good read, btw):
The Angular Ahead-of-Time (AOT) compiler converts your Angular HTML and TypeScript code into efficient JavaScript code during the build phase before the browser downloads and runs that code.
@fetis I think what @AlexGS74 meant is similar to what I said. location is not defined at build time because it is not running in the browser
@deebloo That is correct. There are shims used by the Angular Compiler, but these are static with no real values.
BTW, here is one more resource that might help: AoT Do's and Don'ts
It's still unclear for me why AOT tries to optimize this string. Please note, it's not a template
Is any way to prevent this behavior?
@fetis I understand that it isn't a template. AOT isn't trying to optimize the string. It is running that code, and at the time that it runs the code, location is not defined. Think about it this way, AuthModule.forRoot is being run in node at build time. not in the browser at runtime. If you want to do something like that you can use an injection token and create a factory function that returns the value you want. that will be run in the browser and you will see the value you want.
Sorry I know it sounds like I am just talking in circles. The simple example below shows how you can access this with a factory OR it needs to run in code that is not instantiated at build time. Perhaps in a services constructor.
I hope this helps
const REDIRECT_URI = new InjectionToken('REDIRECT_URI');
export function redirectUriFactory {
return `${location.protocol}//${location.host}/callback`
}
@NgModule(...)
class MyModule {
forRoot() {
return {
ngModule: MyModule,
providers: [
{ provide: REDIRECT_URI, useFactory: redirectUriFactory }
]
}
}
}
@fetis One of the things AOT does it tries to detect the lazy-load routes. For that it needs to execute/optimize etc. the modules and the forRoot methods. As @deebloo pointed out, it will be run in node environment, hence all the restrictions on what you can or can't to for AOT to work.
The code @deebloo provided above is perfect example of how to "move" the execution to runtime only.
@deebloo Does AOT always execute ngModule declaration?
I tried to experiment a bit with it in a router configuration
{ path: `random${ Math.random() }`, component: MyComponent}
I expected to get a randomly generated router path, but I get an error in runtime Uncaught Error: Invalid configuration of route '': routes must have either a path or a matcher specified
I'm trying to get more understanding on this topic.
@fetis yes. I am not positive about the example you showed with the router but I am willing to bet that is the case there as well
@deebloo It's just a synthetic example. the simplest I could imagine.
Why in that case I don't have a route like random0.xxx, but the error listed above. Actually, I don't have any route which starts with "random" in my main.js
Try dropping this at the end of your polyfills.ts file and try then:
(window as any).global = window;
If its fixed - read here ( https://github.com/angular/angular-cli/issues/9827#issuecomment-386154063 )
@hassanasad that is a different issue window will still not exist at build time
@fetis im not sure why you are seeing that
@deebloo Your code works fine, if it's just one config. What if, you're sending the bunch of configuration and one of the value needs location.origin like below -
cacheLocation: 'localStorage',
redirectUri:${window.location.origin}/login``
same way. just return the object from the factory function
I made it working this way -
export function getRedirectUrl() {
return `${window.location.origin}/login`;
}
adalConfig: {
cacheLocation: 'localStorage',
redirectUri: getRedirectUrl
},
Hope it helps!
Closing as there seem to be a lot of good responses and alternatives here, and also because this isn't really a CLI issue. Thanks all for the help!
@filipesilva could u please re-open the issue. I still have a problem with it. My configuration looks next now
@NgModule({
...
})
export class AuthModule {
static forRoot<T>(config: AuthModuleConfig<T>): ModuleWithProviders {
return {
ngModule: AuthModule,
providers: [
...
{ provide: BROWSER_LOCATION, useFactory: locationFactory }
]
};
}
}
and location factory exported from another file
export function locationFactory() {
return window.location;
}
this all is packed as a library with ng-packagr.
Config works fine for ng serve --aot, but when I run this in production mode it still fails
and produces next warning
WARNING in ./src/app/app.module.ngfactory.js
21:1871-1889 "export 'locationFactory' (imported as 'i7') was not found in '@XXXX/auth-module'
I think it was some misconfiguration of libraries on my machine, after some struggle it works fine
Hi, Still I am getting null !!!
My configuration like...
export const oauthConfig: AuthConfig = {
clientId: environment.oauth.clientId,
issuer: environment.oauth.url,
loginUrl: ${environment.oauth.url}/xyz,
logoutUrl: ${environment.oauth.url}/xyz,
redirectUri: ${environment.host}
};
enviroment.host = window.location.origin something like this.
while prod mode I am getting window null.
Isn't better to use @Inject(DOCUMENT) private doc: Document?
so my code looks like:
import { DOCUMENT } from '@angular/common';
....
constructor (@Inject(DOCUMENT) private doc: Document) {
}
....
getOrigin(): string {
return this.doc.location.origin;
}
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
_This action has been performed automatically by a bot._
Most helpful comment
@fetis I understand that it isn't a template. AOT isn't trying to optimize the string. It is running that code, and at the time that it runs the code, location is not defined. Think about it this way, AuthModule.forRoot is being run in node at build time. not in the browser at runtime. If you want to do something like that you can use an injection token and create a factory function that returns the value you want. that will be run in the browser and you will see the value you want.
Sorry I know it sounds like I am just talking in circles. The simple example below shows how you can access this with a factory OR it needs to run in code that is not instantiated at build time. Perhaps in a services constructor.
I hope this helps