重置 Angular 2 应用程序

问题描述 投票:0回答:4

我的 Angular 2 应用程序具有注销功能。如果可以的话,我们希望避免重新加载页面(即

document.location.href = '/';
),但注销过程需要重置应用程序,以便当另一个用户登录时,不会有上一个会话的残留数据。

这是我们的 main.ts 文件:

import 'es6-shim/es6-shim';
import './polyfills';    
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { ComponentRef, enableProdMode } from '@angular/core';
import { environment } from '@environment';
import { AppModule } from './app/app.module';

if (environment.production === true) {
    enableProdMode();
}

const init = () => {
  platformBrowserDynamic().bootstrapModule(AppModule)
  .then(() => (<any>window).appBootstrap && (<any>window).appBootstrap())
  .catch(err => console.error(err));
};

init();

platformBrowserDynamic().onDestroy(() => {
  init();
});

您可以看到,当应用程序被销毁时,我尝试调用 init() 方法。我们的 user-authentication.service 中的 logout 方法启动 destroy:

logout() {   
  this.destroyAuthToken();  
  this.setLoggedIn(false);
  this.navigateToLogin()
  .then(() => {
    platformBrowserDynamic().destroy();
  });
}

这会出现以下错误:

选择器“app-root”未匹配任何元素

任何帮助表示赞赏。

angular angular2-services
4个回答
34
投票

我最终弄清楚了这一点。这可以比我的实现更简单,但我想将引导保留在

main.ts
中,而不是将其粘贴在请求重新启动的服务中。

  1. 创建一个单例,为 Angular 和非 Angular (
    main.ts
    ) 提供一种通信方式:

boot-control.ts

import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
export class BootController {
  private static instance: BootController;
  private _reboot: Subject<boolean> = new Subject();
  private reboot$ = this._reboot.asObservable();

  static getbootControl() {
    if (!BootController.instance) {
      BootController.instance = new BootController();
    }
    return BootController.instance;
  }

  public watchReboot() {
    return this.reboot$;
  }

  public restart() {
    this._reboot.next(true);
  }
}
  1. 调整
    main.ts
    订阅重启请求:

main.ts

import { enableProdMode, NgModuleRef, NgModule } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { BootController } from './boot-control';

if (environment.production) {
  enableProdMode();
}

const init = () => {
  platformBrowserDynamic().bootstrapModule(AppModule)
  .then(() => (<any>window).appBootstrap && (<any>window).appBootstrap())
  .catch(err => console.error('NG Bootstrap Error =>', err));
}

// Init on first load
init();

// Init on reboot request
const boot = BootController.getbootControl().watchReboot().subscribe(() => init());
  1. 将NgZone添加到触发注销的服务中:

user-auth.service.ts

import { BootController } from '@app/../boot-control';
import { Injectable, NgZone } from '@angular/core';

@Injectable()
export class UserAuthenticationService {
    constructor (
        private ngZone: NgZone,
        private router: Router
    ) {...}

    logout() {
        // Removes auth token kept in local storage (not strictly relevant to this demo)
        this.removeAuthToken();

        // Triggers the reboot in main.ts        
        this.ngZone.runOutsideAngular(() => BootController.getbootControl().restart());

        // Navigate back to login
        this.router.navigate(['login']);
    }
}

NgZone 的要求是为了避免错误:

预计不会位于角区,但它确实位于!


13
投票

我遇到了同样的问题。更简单的方法是使用 location.reload()

当用户单击注销按钮时调用的 App.component 中的函数应如下所示。

logout() {
  //Auth Logout service call
  this.auth.logout();
  //Router Navigation to Login Page
  this.router.navigate(['login']);
  //Reload Angular to refresh components and prevent old data from loading up for a 
  //another user after login. This especially applies lazy loading cases. 
  location.reload(); 
}

0
投票

当我将项目从 Angular 7 更新到 Angular 14 时,我遇到了同样的问题。错误

选择器“app-root”与任何元素都不匹配 仅在重置时才会出现。

当调用

destroyPlatform()
platformBrowserDynamic().destroy()
时,它也会从文档主体中销毁
<app-root></app-root>
元素。在版本 7 中,在调用
destroyPlatform()
platformBrowserDynamic().destroy()
后,根标签仍然存在,但在版本 14 中则不然,我不知道从哪个版本开始出现此行为。

我的解决方案是检查主体中是否存在

<app-root></app-root>
元素,如果不存在则创建它。这是我的主要.ts:

import { destroyPlatform, enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { BootController } from './boot-controller';

if (environment.production) {
    enableProdMode();
}

const init = () => {
   const rootSelector = document.getElementsByTagName('app-root');
    // console.log(rootSelector);
    // console.log(document);
    if (!rootSelector || rootSelector.length === 0) {
        const root = document.createElement('app-root');
        document.body.appendChild(root);
    }
    platformBrowserDynamic()
        .bootstrapModule(AppModule)
        .catch((err) => console.error(err));
};

init();
const boot = BootController.getbootControl()
    .watchReboot()
    .subscribe(() => {
        destroyPlatform();
        init();
    });

BootController.ts:

import { Subject } from 'rxjs';

export class BootController {
    private static instance: BootController;
    private _reboot: Subject<boolean> = new Subject();
    private reboot$ = this._reboot.asObservable();

    static getbootControl() {
        if (!BootController.instance) {
            BootController.instance = new BootController();
        }
        return BootController.instance;
    }

    public watchReboot() {
        return this.reboot$;
    }

    public restart() {
        this._reboot.next(true);
    }
}

和注销处理程序:

logOut(event?: MouseEvent) {
        if (event) {
            event.stopPropagation();
        }
        this.resetToken();
        this.router.navigate(['/login']);
        this.ngZone.runOutsideAngular(() => BootController.getbootControl().restart());
    }

0
投票

@benh80,感谢提出的解决方案。

我想在用户密码过期后重置女士加载的模块,从而触发分配的

canLoad
守卫。这个解决方案使之成为可能。

顺便说一句,我修改了建议的解决方案,以使用最新的 Angular 17 版本。

  1. app-boot.service.ts
    目录中创建
    app

app-boot.service.ts

import { BehaviorSubject, Observable } from 'rxjs';

export class AppBootService {
  private static instance: AppBootService;
  private reboot: BehaviorSubject<boolean> = new BehaviorSubject(true);
  private reboot$ = this.reboot.asObservable();

  public static getBootControl(): AppBootService {
    if (!AppBootService.instance) {
      AppBootService.instance = new AppBootService();
    }
    return AppBootService.instance;
  }

  public watchReboot(): Observable<boolean> {
    return this.reboot$;
  }

  public restart() {
    this.reboot.next(true);
  }
}
  1. 修改
    main.ts
    订阅重启请求:

main.ts

// Bootstrap app initially and on subsequential reboot requests
AppBootService.getBootControl()
  .watchReboot()
  .subscribe(() => {
    platformBrowserDynamic()
      .bootstrapModule(AppModule)
      .then(() => (<any>window).appBootstrap && (<any>window).appBootstrap())
      .catch((err) => console.error('Angular Bootstrap Error =>', err));
  });
  1. 修改
    account.service.ts
    logout()
    触发重启操作的方法。

account.service.ts

  logout() {
    // Clears the local storage states
    this.tokenService.reset();
    this.sidebarService.reset();
    this.chartService.reset();
    this.accountStore.reset();

    // https://stackoverflow.com/a/45456825/1789788
    // Triggers the reboot in main.ts
    // This forces Angular application to reboot, forcing lazy loaded module to reload
    // This is necessary because of password expiration routing
    this.ngZone.runOutsideAngular(() => AppBootService.getBootControl().restart());

    // Navigate back to login
    this.router.navigate(['/login']);
  }
© www.soinside.com 2019 - 2024. All rights reserved.