角模对话框不显示

时间:2018-08-16 10:29:59

标签: angular typescript modal-dialog behaviorsubject

我在Angular 4应用程序中使用了一个模态对话框,该对话框利用可观察到的BehaviorSubject,并且在许多其他领域都可以正常工作,但不适用于一个特定用例。

有关某些打字稿的示例,请参见下文。

dialog.service.ts:

import { Injectable, TemplateRef } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Dialog, DialogSizeEnum } from './dialog.model';

@Injectable()
export class DialogService {

    private d: Dialog = { template: null, size: DialogSizeEnum.XLarge };
    private dialogSubject = new BehaviorSubject<Dialog>({ template: null, size: DialogSizeEnum.XLarge });

    constructor() { }

    showDialog(template: TemplateRef<any>, size = DialogSizeEnum.XLarge, requiresAction = false) {
        Object.assign(this.d, { template: template, size: size, requiresAction: requiresAction });
        if (this.d !== null) {
            this.dialogSubject.next(this.d);
        }
    }

    getDialog(): BehaviorSubject<Dialog> {
        return this.dialogSubject;
    }

    clear() {
        this.dialogSubject.next(null);
    }
}

dialog.component.ts:

import { Component, OnInit, OnDestroy, ViewEncapsulation, ViewChild } from '@angular/core';
import { Dialog, DialogSizeEnum } from './dialog.model';
import { DialogService } from './dialog.service';
import { PlatformLocation } from '@angular/common';
import { Router, NavigationStart, RouterEvent, NavigationEnd } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import { state, trigger, transition, animate, style, AnimationEvent } from '@angular/animations';

@Component({
    selector: 'app-dialog',
    templateUrl: './dialog.component.html',
    styles: [],
    encapsulation: ViewEncapsulation.None,
    animations: [
        trigger('dialogState', [
            state('void', style(
                {
                    opacity: 0,
                    'margin-top': '-300px'
                }
            )),
            transition('* => *', [
                animate('0.5s ease')
            ])
        ]),
        trigger('dialogBgState', [
            state('void', style(
                {
                    opacity: 0,
                }
            )),
            transition('* => *', [
                animate('0.5s ease')
            ])
        ])
    ]
})
export class DialogComponent implements OnInit, OnDestroy {

    @ViewChild('engModal') engModal: any;
    routerSubscription: Subscription;
    subscription: Subscription;
    dialog: Dialog;
    showDialog: boolean;
    dialogSize = DialogSizeEnum;

    constructor(
    private _location: PlatformLocation,
    private _dialog: DialogService,
    private _router: Router) { }

    open() {
        this.showDialog = true;
        const body = document.body;
        body.classList.add('cell-modal-open');
    }

    close(validClose: boolean) {
        if (validClose === true) {
            this.dialog = undefined;
        }
    }

    dialogAnimDone(event: AnimationEvent) {
        if (event.toState === 'void') {
            const body = document.body;
            body.classList.remove('cell-modal-open');
            this.showDialog = false;
        }
    }

    getDialogSize(dialogSize: DialogSizeEnum): string {
        switch (dialogSize) {
            case DialogSizeEnum.Standard:
                return '';
            case DialogSizeEnum.Large:
                return 'modal-lg';
            case DialogSizeEnum.XLarge:
                return 'modal-xl';
            case DialogSizeEnum.Small:
                return 'modal-sm';
            default:
                return '';
        }
    }

    private handleDialog(d: Dialog) {
        if (!d) {
            this.close(true);
        } else if (d.template) {
            if (this.showDialog) {
                this.close(true);
            }
            this.dialog = d;
            this.open();
        }
    }

    private handleDialogError(err: any) {
        console.log('Dialog Component error: ' + err);
    }

    private initialiseRoutingEventListeners(): void {
        this._location.onPopState(() => {
            if (this.showDialog) {
                this.close(true);
            }
        });
        this.routerSubscription = this._router.events.subscribe((event: RouterEvent) => {
            if (event instanceof NavigationStart) {
                if (this.showDialog) {
                    this.close(true);
                }
            }
            if (event instanceof NavigationEnd) {
                if (this.showDialog) {
                    this.close(true);
                }
            }
        });
    }

    ngOnInit() {
        this.subscription = this
            ._dialog
            .getDialog()
            .subscribe({
                next: (d) => { this.handleDialog(d) },
                error: (err) => this.handleDialogError(err)
            });
        this.initialiseRoutingEventListeners();
    }

    ngOnDestroy() {
        if (this.subscription) {
            this.subscription.unsubscribe();
        }
        if (this.routerSubscription) {
            this.routerSubscription.unsubscribe();
        }
    }

}

requests.module.ts(模态正常工作的业务区域模块):

import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';

import {
    UserComponent, DetailComponent, DetailModalComponent, CreateComponent, CreateAmendmentComponent,
    ReviewAmendmentListComponent, ViewAmendmentListComponent, ListComponent,
    ReviewedAmendmentListComponent, AssociatedComponent
} from './';

import { RequestsRoutingModule } from './requests-routing.module';
import { RequestService, JiraService, JiraCreateIssueComponent, RequestFilterComponent } from './shared';
import { RaciMembersComponent } from './shared/components/raci-members/raci-members.component';
import { CommentsModule } from './comments/comments.module';
import { TitleCasePipe } from '../shared';

@NgModule({
    imports: [
        RequestsRoutingModule,
        SharedModule,
        NgbModule,
        CommentsModule
    ],
    declarations: [
        UserComponent,
        DetailComponent,
        CreateComponent,
        CreateAmendmentComponent,
        ReviewAmendmentListComponent,
        ViewAmendmentListComponent,
        ListComponent,
        ReviewedAmendmentListComponent,
        AssociatedComponent,
        JiraCreateIssueComponent,
        RaciMembersComponent,
        RequestFilterComponent,
        DetailModalComponent
    ],
    providers: [
        RequestService,
        JiraService,
        TitleCasePipe
    ],
    exports: [
        RequestFilterComponent,
        DetailModalComponent
    ]
})
export class RequestsModule { }

detail-modal.component.ts(用于实际详细信息视图):

import { Component, OnInit, TemplateRef, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { DialogService } from '../../../shared/components/dialog/dialog.service';
import { FormBuilder, FormGroup, Validators, FormArray, FormControl, AbstractControl, ValidatorFn } from '@angular/forms';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { Location } from '@angular/common';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import * as moment from 'moment';
import { Observable } from 'rxjs/Observable';
import { appsettings } from '../../../../settings/appsettings';
import {
    RequestService, RequestEdit,
    RequestAmendmentView, ViewAmendmentListComponent,
    RequestEditAdminSubmit, RequestEditDevSubmit,
    RequestEditCustomerMemberSubmit,
    RagstatusEnum
} from '../../shared';
import {
    RequestStatus, RequestType, AlertService, ActiveDirectory,
    ActiveDirectoryService, UserSearchComponent, ConfirmComponent, TitleCasePipe,
    ValueCompareValidator, ValueCompare
} from '../../../shared';
import { RequestQueue } from '../../../shared/models';
import { RequestQueueService } from 'app/request-queue/request-queue.service';
import { RequestUrgencyType, RequestUrgencyTypeEnum } from '../../../shared/models/request-urgency.model';
import { NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
import { DateCompareValidator } from 'app/shared/validation/value-compare/date-compare.function';
import { ActiveDirectoryUserValidator } from 'app/shared/validation/active-directory/active-directory.function';
import { RequestStatusEnum } from 'app/shared/models';
import { DateCompareValueValidator } from 'app/shared/validation/value-compare/date-compare-value.function';
import { RequiredOnDropDownValidator } from 'app/shared/validation/required-on-drop-down.function';
import Dialogmodel = require('../../../shared/components/dialog/dialog.model');
import DialogSizeEnum = Dialogmodel.DialogSizeEnum;

declare var $: any;

@Component({
    selector: 'app-detail-modal',
    templateUrl: './detail-modal.component.html',
    styleUrls: ['../detail.component.scss']
})

export class DetailModalComponent implements OnInit {

    @ViewChild('detailModal') detailModal: TemplateRef<any>;

    @ViewChild('tagEntryBox') tagInput: ElementRef;

    form: FormGroup;
    id: number;
    request: RequestEdit;
    isBusy = true;
    waitCursorMessage = 'Loading modal...';
    hideAmendments = true;            // is the amendment accordion hidden?
    showAddAmendment = false;         // is the create amendment visible?
    amendmentAccordionOpen = false;   // is the amendment accordian currently open?
    disableControls = true;
    showAmendButton = false;
    membersUpdated = false;
    shouldDisableResponsibleMembers = false;
    amendmentsUpdated = true;
    amendmentsFormGroup: FormGroup;
    createAmendmentFormGroup: FormGroup;
    showFooter = true;
    descriptionMax: number;
    showDateNeeded = false;
    tempDate = new Date(moment('2001-01-01', 'YYYY-MM-DD').format('MM/DD/YYYY')).toDateString();
    ragStatus = RagstatusEnum;
    currentRate = 2;
    hovered = 0;
    showCompletedDate = false;
    allQueues: RequestQueue[];
    initialQueue: RequestQueue;
    queueId: number;
    modalEl = null;

    constructor(
    private _dialog: DialogService,
    private _formBuilder: FormBuilder,
    private _requestService: RequestService,
    private _route: ActivatedRoute,
    private _alertService: AlertService,
    private _activeDirectoryService: ActiveDirectoryService,
    private _location: Location,
    private _sanitizer: DomSanitizer,
    private _titleCase: TitleCasePipe,
    private _queueService: RequestQueueService,
    private _rootNode: ElementRef) { }

    ngOnInit(): void { }

    populateForm(): void {
        this.getAllQueues();
        this.getRequest();    }

    getAllQueues(): void {
        this.isBusy = true;
        this._queueService.getAllQueues().subscribe({
            next: (result) => {
                this.allQueues = result;
            },
            error: (err) => {
                this.isBusy = false;
                this._alertService.sendAlert('warning', 'Unable to load queues!');
            },
            complete: () => {
                this.isBusy = false;
            }
        });
    }

    getRequest(): void {
        this._route.params
            .switchMap(x => this._requestService.getDetailRequest(this.id))
            .subscribe(request => this.receiveRequest(request), error => this.loadError());
    }

    loadError() {
        this.isBusy = false;
        this._alertService.sendAlert('warning', 'Failed to load request!');
    }

    showSave() {
        return !this.shouldDisableControlForAdmin()
            || !this.shouldDisableControlForDev()
            || !this.shouldDisableControlForRequester()
            || this.membersUpdated
    }

    initForm() {
    …
    … (other code not relevant to this question)
    …
    }


    setformValues() {
        this.form.patchValue({
           …
    … (other code not relevant to this question)
    …
        });
    }

    private mapToNgbDate(val: any): NgbDateStruct {
        if (!val) {
            return null;
        }
        const d: Date = new Date(val);
        return {
            day: d.getDate(),
            month: d.getMonth() + 1,
            year: d.getFullYear()
        };
    }

    private mapToDate(d: NgbDateStruct): Date {
        if (!d) {
            return null;
        }
        return new Date(d.year, d.month - 1, d.day);
    }

    onSubmit(): void {
        this.isBusy = true;
        this.waitCursorMessage = 'Saving...';
        if (this.checkCanChangeStatus()) {
            this.prepareSaveRequest();
        } else {
            this.isBusy = false;
            this._alertService.sendAlert('info', 'You cannot change status when there is an outstanding amendment!');
        }
    }

    submissionComplete(request) {
        this.request = request;
        this._alertService.sendAlert('success', 'Request updated!');
        this.form.markAsPristine();
        this.isBusy = false;
        this.membersUpdated = false;
        this.allowedToAddResponsibleMembers();
        this.setShowAmendButton();
    }

    submissionError() {
        this._alertService.sendAlert('warning', 'Failed to update request!');
        this.isBusy = false;
    }

    prepareSaveRequest(): void {        
    …
    … (other code not relevant to this question)
    …
            this.cleanSaveRequest();
        }

    …
    … (other code not relevant to this question)
    …
    }

    showDialog(requestId: number) {
        this.id = requestId;
        this.populateForm();
        this._dialog.showDialog(this.detailModal, DialogSizeEnum.XLarge);
    }

    close() {
        this._dialog.clear();
    }

}

associated.component.ts(成功使用模式的示例):

import { Component, OnInit, ViewChild } from '@angular/core';
import { AdminListRequestViewModel, RequestService, RequestFilter, RagstatusEnum } from '../shared';
import { AlertService, RequestStatusEnum } from '../../shared';
import { DetailModalComponent } from '../detail/detail-modal/detail-modal.component'

@Component({
    templateUrl: './associated.component.html',
    styleUrls: ['./associated.component.scss']
})
export class AssociatedComponent implements OnInit {
    @ViewChild('associatedDetailModalComponent') associatedDetailModalComponent: DetailModalComponent;

    requests: AdminListRequestViewModel[];
    isBusy = true;
    selectedStatuses = [
      RequestStatusEnum.New,
      RequestStatusEnum.AwaitingApproval,
      RequestStatusEnum.BeingTested,
      RequestStatusEnum.InDesign,
      RequestStatusEnum.InDevelopment,
      RequestStatusEnum.Approved
    ];
    selectedFilter: RequestFilter;
    ragStatus = RagstatusEnum;

    constructor(
    private _requestService: RequestService,
    private _alertService: AlertService) { }

    ngOnInit(): void {
        this.getRequests();
    }

    getRequests(): void {
        this._requestService.getAssociatedRequests().subscribe(requests => this.receiveRequests(requests), error => this.onError());
    }

    onError() {
        this._alertService.sendAlert('warning', 'Unable to load requests!');
        this.isBusy = false;
    }

    receiveRequests(returnedRequests): void {
        this.requests = returnedRequests;
        this.isBusy = false;
    }

    filterChanged(newFilterValue: RequestFilter) {
        this.selectedFilter = newFilterValue;
    }

    viewAssociatedRequest(requestId: number) {
        this.associatedDetailModalComponent.showDialog(requestId);
    }

}

manage-request-queues.component.ts(其中不显示模式):

import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { RequestQueueService } from 'app/request-queue/request-queue.service';
import {
    RequestQueue, RequestQueueRequest, RequestQueuePosition, RequestQueueChanges,
    RequestQueueWithRequests, RequestQueueMove
} from 'app/request-queue/models';
import { RequestService } from 'app/requests/shared';
import { AlertService } from 'app/shared';
import { DragulaService } from 'ng2-dragula';
import { YesNoCancelComponent, RequestStatusEnum } from 'app/shared';
import { DetailModalComponent } from '../../requests/detail/detail-modal/detail-modal.component';

@Component({
    selector: 'app-manage-request-queues',
    templateUrl: './manage-request-queues.component.html',
    styleUrls: ['./manage-request-queues.component.scss']
})
export class ManageRequestQueuesComponent implements OnInit, OnDestroy {
    @ViewChild('manageRequestDetailModalComponent') manageRequestDetailModalComponent: DetailModalComponent;

    selectedQueueId = '-1';
    allQueues: RequestQueueWithRequests[];
    dragulaDropSubscription: any;
    dragulaDragSubscription: any;
    dragulaDragEndSubscription: any;
    moves = new Array<RequestQueueMove>();
    saving = false;
    isBusy = true;
    waitCursorMessage = 'Loading...';

    get canDeactivate(): boolean {
        return this.moves.length === 0;
    }

    constructor(
        private _queueService: RequestQueueService,
        private _alertService: AlertService,
        private _dragulaService: DragulaService) {
    }


    saveQueues() {
        this.saving = true;
        this.isBusy = true;
        this.waitCursorMessage = 'Saving...';
        const saveRequestCollection = this.buildSaveRequestCollection();
        if (saveRequestCollection.length > 0) {
            this._queueService.saveRequestQueueChanges(saveRequestCollection).subscribe({
                next: (result) => {
                    this._alertService.sendAlert('success', 'Request queues have been updated.');
                },
                error: (err) => {
                    console.log(err);
                    const result = JSON.parse(err.error);
                    this._alertService.sendAlert('danger', result.messageList[0]);
                    this.saving = false;
                    this.isBusy = false;
                },
                complete: () => {
                    this.saving = false;
                    this.isBusy = false;
                    this.moves = new Array<RequestQueueMove>();
                }
            });
        }
    }

    resetQueues() {
        this.selectedQueueId = '-1';
        this.allQueues = new Array<RequestQueueWithRequests>();
        this.moves = new Array<RequestQueueMove>();
        this.getAllQueuesWithRequests();
    }

    showQueue(requestQueueID: number) {
        return +this.selectedQueueId === requestQueueID;
    }



    private buildSaveRequestCollection() {
        const saveRequestCollection = new Array<RequestQueuePosition>();

        this.allQueues.forEach(queue => queue.requests.forEach(request => {
            const queueIndex = queue.requests.indexOf(request);
            // set queue id to pick up user changes
            request.requestQueueID = queue.requestQueueID;
            if (this.checkHasUpdated(request, queue.requests.indexOf(request))) {
                // must update position as this is likely to have changed
                request.requestQueuePosition = queueIndex + 1;
                saveRequestCollection.push({
                    requestID: request.requestID,
                    requestQueueID: request.requestQueueID,
                    queuePosition: request.requestQueuePosition,
                    requestStatusID: request.requestStatus.requestStatusId
                });
            }
        }));

        return saveRequestCollection;
    }

    ngOnInit() {
        this.getAllQueuesWithRequests();
    }

    ngOnDestroy() {
        this.selectedQueueId = '-1';
        this.allQueues = new Array<RequestQueueWithRequests>();
        this.moves = new Array<RequestQueueMove>();
        if (this.dragulaDropSubscription) {
            this.dragulaDropSubscription.unsubscribe();
        }
        if (this.dragulaDragSubscription) {
            this.dragulaDragSubscription.unsubscribe();
        }
        if (this.dragulaDragEndSubscription) {
            this.dragulaDragEndSubscription.unsubscribe();
        }
    }

    viewManageRequest(requestId: number) {
        this.manageRequestDetailModalComponent.showDialog(requestId);
    }

}

request-queue.module.ts(由失败的组件使用):

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RequestQueueRoutingModule } from './request-queue-routing.module';
import { RequestQueueService } from './request-queue.service';
import { ManageRequestQueuesComponent } from './manage-request-queues/manage-request-queues.component';
import { ModifyRequestQueueComponent } from './modify-request-queue/modify-request-queue.component';
import { AddRequestQueueComponent } from './add-request-queue/add-request-queue.component';
import { SharedModule } from 'app/shared/shared.module';
import { DragulaModule } from 'ng2-dragula';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { RequestsModule } from '../requests/requests.module'

@NgModule({
    imports: [
        CommonModule,
        SharedModule,
        FormsModule,
        DragulaModule,
        NgbModule,
        ReactiveFormsModule,
        RequestQueueRoutingModule,
        RequestsModule
    ],
    declarations: [ManageRequestQueuesComponent, ModifyRequestQueueComponent, AddRequestQueueComponent],
    providers: [RequestQueueService]
})
export class RequestQueueModule { }

我认为问题在于,dialog.service.ts中的dialogSubject(BehaviorSubject)在模式不起作用的情况下没有任何管理请求队列的观察者。这在detail-modal.component.ts中也很明显,其中注入的_dialog DialogService dialogSubject也没有观察者(该对话框的所有其他成功实现在其中)。

与成功的管理请求队列不同的另一个主要区别是,它位于应用程序的另一个区域,因此具有自己的模块(请参见上文)。我已经导入了RequestsModule,但这没有什么区别。我还尝试将DialogService添加到app.module中的提供程序,但无济于事。

我怀疑这可能是一个简单的修复程序,与不成功的组件没有订阅嵌入式BehaviorSubject有关,但是我不确定在其他用例中如何成功实现。

我意识到上面有很多代码,但是感兴趣的主要项目与dialog.service有关(我已经删除了大部分不相关的代码)。

我是Angular的新手,所以如果答案很明显,我深表歉意。

1 个答案:

答案 0 :(得分:0)

经过多次试验和错误,导致问题的原因是DialogService由于在解决方案中的位置而未在manage-request-queues.component的范围内。 解决方法是将导入和提供程序声明从shared.module移到主app.module。