从路由器加载URL以在NgModule中使用

时间:2019-09-01 17:27:10

标签: angular angular-ui-router nebular ngx-admin

我正在设置蓝色/绿色部署,并尝试根据用户正在查看的当前URL(redirectUri)更改下面的redirectUri: this.router.url + '/callback',。我收到的Uncaught TypeError: Cannot read property 'router' of undefined具有以下配置。

import { APP_BASE_HREF } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { CoreModule } from './@core/core.module';
import { AuthGuard } from './auth-guard.service';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { ThemeModule } from './@theme/theme.module';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { NbOAuth2AuthStrategy,
        NbAuthModule,
        NbOAuth2ResponseType,
        NbOAuth2GrantType,
        NbAuthOAuth2Token,
       } from '@nebular/auth';
import { OAuth2LoginComponent } from './auth/oauth2-login.component';
import { OAuth2CallbackComponent } from './auth/oauth2-callback.component';
import { environment } from '../environments/environment';
import { Router } from '@angular/router';

@NgModule({
  declarations: [AppComponent, OAuth2LoginComponent, OAuth2CallbackComponent ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    HttpClientModule,
    NgbModule.forRoot(),
    ThemeModule.forRoot(),
    CoreModule.forRoot(),
    NbAuthModule.forRoot({
      forms: {},
      strategies: [
        NbOAuth2AuthStrategy.setup({
          baseEndpoint: environment.authUrl,
          name: 'cognito',
          clientId: environment.clientId,
          authorize: {
            endpoint: '/oauth2/authorize',
            responseType: NbOAuth2ResponseType.CODE,
            scope: 'aws.cognito.signin.user.admin',
            redirectUri: this.router.url + '/callback',
          },
          redirect: {
            success: '/pages/dashboard',
          },
          token: {
            endpoint: '/oauth2/token',
            grantType: NbOAuth2GrantType.AUTHORIZATION_CODE,
            class: NbAuthOAuth2Token,
            redirectUri: this.router.url + '/callback',
          },
          refresh: {
            endpoint: 'refresh-token',
            grantType: NbOAuth2GrantType.REFRESH_TOKEN,
          },
        }),
       ],
    }),
    AppRoutingModule,
  ],
  bootstrap: [AppComponent],
  providers: [
    AuthGuard,
    { provide: APP_BASE_HREF, useValue: '/' },
  ],
})
export class AppModule {
  constructor(private router: Router) {}
}

我还尝试过使用redirectUri: window.location.origin + '/callback',它在本地工作,但是在为生产而构建时为null。

4 个答案:

答案 0 :(得分:2)

请注意,在创建类的任何实例之前,将class level decorators应用于构造函数。因此,路由器属性不适用于装饰器。在示例this.router.url + '/callback'引用全局this的情况下,很奇怪没有编译错误。

关于window.location,在aot编译模式下(默认为prod构建),装饰器中的表达式由Angular编译器在编译时执行,因此window.location不是在那里可用。看看这个GitHub问题:AOT replaces window.location object to null

作为解决方法,您可以动态初始化NbOAuth2AuthStrategy,例如:

@NgModule({
  imports: [
    ...
    NbAuthModule.forRoot({
      strategies: [
        NbOAuth2AuthStrategy.setup({
          name: 'cognito'
        })
      ],
      ...
    })
  ],
  ...
})
export class AppModule {
  constructor(
    authService: NbAuthService, // force construction of the auth service
    oauthStrategy: NbOAuth2AuthStrategy
  ) {
    // window.location should be available here
    this.oauthStrategy.setOpitions({
      name: 'cognito',
      ...
    });
  }
}

我发现,将NbAuthServiceNbOAuth2AuthStrategy都添加到构造函数参数很重要。看起来该服务在构造过程中初始化了策略,因此应该在策略初始化之前对其进行构造。

还请注意,setOptions()方法完全覆盖了模块装饰器中的选项,因此,整个策略初始化应从装饰器移至构造器。

我还发现了this的GitHub问题,这有助于我找到正确的解决方案。

答案 1 :(得分:0)

如果要执行类似的操作,则可以使用注入令牌并创建一个工厂函数来返回所需的值。它将在浏览器中运行,您将看到所需的值。

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 }
          ]
        }
      }
    }

我没有对此进行测试,但是带有Factory的InjectionToken是进入AOT的必经之路

更多信息https://github.com/angular/angular-cli/issues/10957

答案 2 :(得分:0)

在您提供的代码示例中,您似乎在实例化类“ this.router”之前尝试访问@NgModule批注中的路由器对象AppModule

实例变量在注释中不可用。 Angular使用[Dependency Injection] [1]通过构造函数提供对象实例。

根据您的情况,虽然我不太了解NbOAuth2AuthStrategy模块,但是在定义了import部分的模块后,您可以在构造函数中寻找配置auth策略的选项@NgModule注解。考虑一下此代码段,它可能对您有所帮助。在下面的代码中用<< >>

查找占位符

    NbAuthModule.forRoot({
        forms: {},
      strategies: [
        NbOAuth2AuthStrategy.setup({
          baseEndpoint: environment.authUrl,
          name: 'cognito',
          clientId: environment.clientId,
          authorize: {
            endpoint: '/oauth2/authorize',
            responseType: NbOAuth2ResponseType.CODE,
            scope: 'aws.cognito.signin.user.admin',
            redirectUri: '<<SET SOME DEFAULT URL>>',
          },
          redirect: {
            success: '/pages/dashboard',
          },
          token: {
            endpoint: '/oauth2/token',
            grantType: NbOAuth2GrantType.AUTHORIZATION_CODE,
            class: NbAuthOAuth2Token,
            redirectUri: '<<SET SOME DEFAULT URL>>',
          },
          refresh: {
            endpoint: 'refresh-token',
            grantType: NbOAuth2GrantType.REFRESH_TOKEN,
          },
        }),
       ...


    export class AppModule {
      constructor(private router: Router) {
        <<Reconfigure your NbOAuth2AuthStrategy>>
        NbOAuth2AuthStrategy.setup....
      }
    }


Hope the solution works for you.



  [1]: https://angular.io/guide/dependency-injection

答案 3 :(得分:0)

您的解决方案将不起作用,因为正如Valeriy Katkov提到的那样,在创建类的任何实例之前,将类级别的装饰器应用于构造函数。因此,您将无法将路由器注入到装饰器中。

为了能够注入路由器,您需要在类内部移动实现。可以通过setOptions实例的NbAuthStrategy方法来实现,但是存在一些问题,例如需要解决的问题herehere。 为了使它起作用,您应该将策略配置移至扩展NbAuthComponent的组件(这很重要),例如:

export class AppComponent extends NbAuthComponent {

  constructor(auth: NbAuthService,
              location: Location,
              private router: Router,
              authStrategy: NbPasswordAuthStrategy) {
    super(auth, location);

    authStrategy.setOptions({
      name: 'username',
      login: {
        alwaysFail: false,
        endpoint: 'test',
        method: 'post',
        requireValidToken: false,
        redirect: {
          success: this.router.url + '/success-callback',
          failure: this.location.path() + '/callback'
        },
        defaultErrors: ['Damn'],
        defaultMessages: ['Great'],
      },
    });
  }
}

我也建议您使用this.location.path()而不是this.router.url,因为this.router.url不会为您提供您所在的URL,而是为您提供Compoment级别的URL。 this.location.path()将为您提供所在页面的完整路径。

这是一个StackBlitz example,具有有效的解决方案。

请让我知道您是否仍然不清楚或需要进一步详细说明。