[PM-3919] Remove deprecated Angular functionality from ModalService (#6285)

Remove deprecated Angular functionality from ModalService so we can upgrade Angular.
This commit is contained in:
Oscar Hinton 2024-01-22 10:36:42 +01:00 committed by GitHub
parent 487d17daed
commit 7bb4ea842f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 29 additions and 107 deletions

View File

@ -1,7 +1,6 @@
import { import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
ComponentFactoryResolver,
EventEmitter, EventEmitter,
Input, Input,
Output, Output,
@ -45,7 +44,6 @@ export class PolicyEditComponent {
private policyApiService: PolicyApiServiceAbstraction, private policyApiService: PolicyApiServiceAbstraction,
private i18nService: I18nService, private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService, private platformUtilsService: PlatformUtilsService,
private componentFactoryResolver: ComponentFactoryResolver,
private cdr: ChangeDetectorRef, private cdr: ChangeDetectorRef,
private logService: LogService, private logService: LogService,
) {} ) {}
@ -54,8 +52,7 @@ export class PolicyEditComponent {
await this.load(); await this.load();
this.loading = false; this.loading = false;
const factory = this.componentFactoryResolver.resolveComponentFactory(this.policy.component); this.policyComponent = this.policyFormRef.createComponent(this.policy.component)
this.policyComponent = this.policyFormRef.createComponent(factory)
.instance as BasePolicyComponent; .instance as BasePolicyComponent;
this.policyComponent.policy = this.policy; this.policyComponent.policy = this.policy;
this.policyComponent.policyResponse = this.policyResponse; this.policyComponent.policyResponse = this.policyResponse;

View File

@ -2,19 +2,15 @@ import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
import { Component, Inject } from "@angular/core"; import { Component, Inject } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder } from "@angular/forms";
import { UserVerificationPromptComponent as BaseUserVerificationPrompt } from "@bitwarden/angular/auth/components/user-verification-prompt.component"; import {
import { ModalConfig } from "@bitwarden/angular/services/modal.service"; UserVerificationPromptComponent as BaseUserVerificationPrompt,
UserVerificationPromptParams,
} from "@bitwarden/angular/auth/components/user-verification-prompt.component";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService } from "@bitwarden/components"; import { DialogService } from "@bitwarden/components";
export interface UserVerificationPromptParams {
confirmDescription: string;
confirmButtonText: string;
modalTitle: string;
}
@Component({ @Component({
templateUrl: "user-verification-prompt.component.html", templateUrl: "user-verification-prompt.component.html",
}) })
@ -27,16 +23,7 @@ export class UserVerificationPromptComponent extends BaseUserVerificationPrompt
platformUtilsService: PlatformUtilsService, platformUtilsService: PlatformUtilsService,
i18nService: I18nService, i18nService: I18nService,
) { ) {
// TODO: Remove when BaseUserVerificationPrompt has support for CL super(null, data, userVerificationService, formBuilder, platformUtilsService, i18nService);
const modalConfig: ModalConfig = { data };
super(
null,
modalConfig,
userVerificationService,
formBuilder,
platformUtilsService,
i18nService,
);
} }
override close(success: boolean) { override close(success: boolean) {

View File

@ -1,4 +1,4 @@
import { ApplicationRef, ComponentFactoryResolver, Injectable, Injector } from "@angular/core"; import { Injectable, Injector } from "@angular/core";
import * as jq from "jquery"; import * as jq from "jquery";
import { first } from "rxjs/operators"; import { first } from "rxjs/operators";
@ -13,12 +13,10 @@ export class ModalService extends BaseModalService {
modalOpen = false; modalOpen = false;
constructor( constructor(
componentFactoryResolver: ComponentFactoryResolver,
applicationRef: ApplicationRef,
injector: Injector, injector: Injector,
private messagingService: MessagingService, private messagingService: MessagingService,
) { ) {
super(componentFactoryResolver, applicationRef, injector); super(injector);
} }
protected setupHandlers(modalRef: ModalRef) { protected setupHandlers(modalRef: ModalRef) {

View File

@ -7,16 +7,21 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ModalRef } from "../../components/modal/modal.ref"; import { ModalRef } from "../../components/modal/modal.ref";
import { ModalConfig } from "../../services/modal.service";
export interface UserVerificationPromptParams {
confirmDescription: string;
confirmButtonText: string;
modalTitle: string;
}
/** /**
* Used to verify the user's identity (using their master password or email-based OTP for Key Connector users). You can customize all of the text in the modal. * Used to verify the user's identity (using their master password or email-based OTP for Key Connector users). You can customize all of the text in the modal.
*/ */
@Directive() @Directive()
export class UserVerificationPromptComponent { export class UserVerificationPromptComponent {
confirmDescription = this.config.data.confirmDescription; confirmDescription = this.config.confirmDescription;
confirmButtonText = this.config.data.confirmButtonText; confirmButtonText = this.config.confirmButtonText;
modalTitle = this.config.data.modalTitle; modalTitle = this.config.modalTitle;
formGroup = this.formBuilder.group({ formGroup = this.formBuilder.group({
secret: this.formBuilder.control<Verification | null>(null), secret: this.formBuilder.control<Verification | null>(null),
@ -26,7 +31,7 @@ export class UserVerificationPromptComponent {
constructor( constructor(
private modalRef: ModalRef, private modalRef: ModalRef,
protected config: ModalConfig, protected config: UserVerificationPromptParams,
protected userVerificationService: UserVerificationService, protected userVerificationService: UserVerificationService,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private platformUtilsService: PlatformUtilsService, private platformUtilsService: PlatformUtilsService,

View File

@ -11,8 +11,6 @@ import {
ViewContainerRef, ViewContainerRef,
} from "@angular/core"; } from "@angular/core";
import { ModalService } from "../../services/modal.service";
import { ModalRef } from "./modal.ref"; import { ModalRef } from "./modal.ref";
@Component({ @Component({
@ -31,7 +29,6 @@ export class DynamicModalComponent implements AfterViewInit, OnDestroy {
private focusTrap: ConfigurableFocusTrap; private focusTrap: ConfigurableFocusTrap;
constructor( constructor(
private modalService: ModalService,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private el: ElementRef<HTMLElement>, private el: ElementRef<HTMLElement>,
private focusTrapFactory: ConfigurableFocusTrapFactory, private focusTrapFactory: ConfigurableFocusTrapFactory,
@ -55,10 +52,8 @@ export class DynamicModalComponent implements AfterViewInit, OnDestroy {
} }
loadChildComponent(componentType: Type<any>) { loadChildComponent(componentType: Type<any>) {
const componentFactory = this.modalService.resolveComponentFactory(componentType);
this.modalContentRef.clear(); this.modalContentRef.clear();
this.componentRef = this.modalContentRef.createComponent(componentFactory); this.componentRef = this.modalContentRef.createComponent(componentType);
} }
ngOnDestroy() { ngOnDestroy() {

View File

@ -1,26 +1,10 @@
import { import { ComponentRef, Injectable, Injector, Type, ViewContainerRef } from "@angular/core";
ApplicationRef,
ComponentFactory,
ComponentFactoryResolver,
ComponentRef,
EmbeddedViewRef,
Injectable,
Injector,
Type,
ViewContainerRef,
} from "@angular/core";
import { first } from "rxjs/operators"; import { first } from "rxjs/operators";
import { DynamicModalComponent } from "../components/modal/dynamic-modal.component"; import { DynamicModalComponent } from "../components/modal/dynamic-modal.component";
import { ModalInjector } from "../components/modal/modal-injector"; import { ModalInjector } from "../components/modal/modal-injector";
import { ModalRef } from "../components/modal/modal.ref"; import { ModalRef } from "../components/modal/modal.ref";
export class ModalConfig<D = any> {
data?: D;
allowMultipleModals?: boolean;
replaceTopModal?: boolean;
}
/** /**
* @deprecated Use the Component Library's `DialogService` instead. * @deprecated Use the Component Library's `DialogService` instead.
*/ */
@ -28,15 +12,7 @@ export class ModalConfig<D = any> {
export class ModalService { export class ModalService {
protected modalList: ComponentRef<DynamicModalComponent>[] = []; protected modalList: ComponentRef<DynamicModalComponent>[] = [];
// Lazy loaded modules are not available in componentFactoryResolver, constructor(private injector: Injector) {
// therefore modules needs to manually initialize their resolvers.
private factoryResolvers: Map<Type<any>, ComponentFactoryResolver> = new Map();
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private applicationRef: ApplicationRef,
private injector: Injector,
) {
document.addEventListener("keyup", (event) => { document.addEventListener("keyup", (event) => {
if (event.key === "Escape" && this.modalCount > 0) { if (event.key === "Escape" && this.modalCount > 0) {
this.topModal.instance.close(); this.topModal.instance.close();
@ -62,7 +38,7 @@ export class ModalService {
viewContainerRef: ViewContainerRef, viewContainerRef: ViewContainerRef,
setComponentParameters: (component: T) => void = null, setComponentParameters: (component: T) => void = null,
): Promise<[ModalRef, T]> { ): Promise<[ModalRef, T]> {
const [modalRef, modalComponentRef] = this.openInternal(componentType, null, false); const [modalRef, modalComponentRef] = this.openInternal(viewContainerRef, componentType);
modalComponentRef.instance.setComponentParameters = setComponentParameters; modalComponentRef.instance.setComponentParameters = setComponentParameters;
viewContainerRef.insert(modalComponentRef.hostView); viewContainerRef.insert(modalComponentRef.hostView);
@ -72,52 +48,18 @@ export class ModalService {
return [modalRef, modalComponentRef.instance.componentRef.instance]; return [modalRef, modalComponentRef.instance.componentRef.instance];
} }
open(componentType: Type<any>, config: ModalConfig = {}) {
const { replaceTopModal = false, allowMultipleModals = false } = config;
if (this.modalCount > 0 && replaceTopModal) {
this.topModal.instance.close();
}
if (this.modalCount > 0 && !allowMultipleModals) {
return;
}
const [modalRef] = this.openInternal(componentType, config, true);
return modalRef;
}
resolveComponentFactory<T>(componentType: Type<T>): ComponentFactory<T> {
if (this.factoryResolvers.has(componentType)) {
return this.factoryResolvers.get(componentType).resolveComponentFactory(componentType);
}
return this.componentFactoryResolver.resolveComponentFactory(componentType);
}
closeAll(): void { closeAll(): void {
this.modalList.forEach((modal) => modal.instance.close()); this.modalList.forEach((modal) => modal.instance.close());
} }
protected openInternal( protected openInternal(
viewContainerRef: ViewContainerRef,
componentType: Type<any>, componentType: Type<any>,
config?: ModalConfig, ): [ModalRef, ComponentRef<any>] {
attachToDom?: boolean, const [modalRef, componentRef] = this.createModalComponent(viewContainerRef);
): [ModalRef, ComponentRef<DynamicModalComponent>] {
const [modalRef, componentRef] = this.createModalComponent(config);
componentRef.instance.childComponentType = componentType; componentRef.instance.childComponentType = componentType;
if (attachToDom) {
this.applicationRef.attachView(componentRef.hostView);
const domElem = (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;
document.body.appendChild(domElem);
}
modalRef.onClosed.pipe(first()).subscribe(() => { modalRef.onClosed.pipe(first()).subscribe(() => {
if (attachToDom) {
this.applicationRef.detachView(componentRef.hostView);
}
componentRef.destroy(); componentRef.destroy();
this.modalList.pop(); this.modalList.pop();
@ -174,17 +116,15 @@ export class ModalService {
} }
protected createModalComponent( protected createModalComponent(
config: ModalConfig, viewContainerRef: ViewContainerRef,
): [ModalRef, ComponentRef<DynamicModalComponent>] { ): [ModalRef, ComponentRef<any>] {
const modalRef = new ModalRef(); const modalRef = new ModalRef();
const map = new WeakMap(); const map = new WeakMap();
map.set(ModalConfig, config);
map.set(ModalRef, modalRef); map.set(ModalRef, modalRef);
const componentFactory = const injector = new ModalInjector(this.injector, map);
this.componentFactoryResolver.resolveComponentFactory(DynamicModalComponent); const componentRef = viewContainerRef.createComponent(DynamicModalComponent, { injector });
const componentRef = componentFactory.create(new ModalInjector(this.injector, map));
return [modalRef, componentRef]; return [modalRef, componentRef];
} }