我的 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”未匹配任何元素
任何帮助表示赞赏。
我最终弄清楚了这一点。这可以比我的实现更简单,但我想将引导保留在
main.ts
中,而不是将其粘贴在请求重新启动的服务中。
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);
}
}
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());
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 的要求是为了避免错误:
预计不会位于角区,但它确实位于!
我遇到了同样的问题。更简单的方法是使用 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();
}
当我将项目从 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());
}
@benh80,感谢提出的解决方案。
我想在用户密码过期后重置女士加载的模块,从而触发分配的
canLoad
守卫。这个解决方案使之成为可能。
顺便说一句,我修改了建议的解决方案,以使用最新的 Angular 17 版本。
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);
}
}
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));
});
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']);
}