В образовательном проекте, над которым я работаю, я хочу избегать использования AngularJS Material Design, UI Bootstrap или любых пользовательских библиотек, которые предоставляют модальные функциональные возможности.
Однако я столкнулся с проблемой.Я создал сервис, который должен управлять и динамически создавать модалы.Он предоставляет функцию open
, которая принимает объект спецификации, который затем воспроизводится в DOM.
Что этот код на самом деле делает:
1. Модал правильно добавлен в DOM.
2. Срабатывает функция $onInit
контроллера модального режима.
Чего не делает этот код:
1. Привязать свойство $ctrl.message
в разметке к экземпляру контроллера, который, как мы знаем, запускается.
Обычно я хотел бызадайте мой вопрос после предоставления кода, однако для воспроизведения этой проблемы требуется немало кода (ниже он приведен без примера AngularJS). Однако в данном случае мой вопрос:
Каким образом я могу получить модалы, отключаемые этим сервисом, чтобы правильно связать их содержимое с данным контроллером?
Что я пробовал:
Как вы можете видеть в ModalService.bindModalDiv
, я пробовал несколько путей мысли, в основном используя $compile
.Тем не менее, $compile
и получающаяся функция связи на самом деле, кажется, не связывают новые элементы DOM с Angular.
Я пытался использовать $controller
, чтобы явно привязать генерируемую новую область к создаваемому экземпляру someModalCtrl
, но это, похоже, совсем не помогает.
Поскольку я могу нажимать точки останова на someModalCtrl
и видеть сообщение console.log
, которое я использовал для проверки работоспособности, я думаю, что неправильно понимаю, как именно я должен связывать новые элементы DOM сУгловой.Я уверен, что упускаю что-то простое, о чем мне удалось как-то забыть или пренебречь.
Еще одно замечание:
Уверен, мои проблемы с получениеммодальный способ привязки к AngularJS - не единственные проблемы.Пожалуйста, помните, я делаю это частично как учебный пример;если вы все сможете помочь мне разобраться в моей модальной проблеме, я буду продолжать проявлять должную осмотрительность и выискивать недостатки, которые я, несомненно, заложил в этот подход.Поэтому, , если вы видите что-то, что не является модальной проблемой, все в порядке, чтобы привлечь мое внимание к этому, но я не буду переписывать вопрос, чтобы исправить то, что вы найдете - если только это не будет абсолютно необходимо, чтобы я сделал. Какпример - я знаю, что у ModalService.open
есть некоторые проблемы в реализации настройки обещаний.$rootScope.$watch
, вероятно, более разумно.
modalSvc.ts:
export interface IModalSpecObject {
parent?: string | Element | JQuery;
templateURL: string
controller: string;
controllerAs?: string;
data: object;
}
export class ModalInstance {
public isOpen: boolean = true;
public returnData: object = null;
public element: JQLite = null;
public $parent: JQuery = null;
public constructor(
public specObject: IModalSpecObject
) {
}
public close(returnData: object): void {
if (this.element)
this.element.remove();
this.isOpen = false;
this.returnData = returnData;
}
}
export class ModalService {
public pollRate: number = 250;
public instance: ModalInstance = null;
public static $inject: string[] = [
'$q', '$rootScope', '$compile', '$controller'
];
public constructor(
public $q: ng.IQService,
public $rootScope: ng.IRootScopeService,
public $compile: ng.ICompileService,
public $controller: ng.IControllerService
) {
}
public open(specObject: IModalSpecObject): ng.IPromise<{}> {
if (this.instance && this.instance.isOpen)
this.instance.close(null);
this.instance = new ModalInstance(specObject);
const $parent: JQuery = this.setParent(specObject);
const modalDiv: JQLite = this.buildModal(specObject);
this.bindModalDiv(modalDiv, $parent);
const result: ng.IPromise<{}> = this.$q((resolve) => {
setInterval(() => {
if (!this.instance.isOpen) {
resolve(this.instance.returnData);
}
}, this.pollRate);
});
return result;
}
private buildModal(specObject: IModalSpecObject): JQLite {
const modalDiv: JQLite = angular.element('<div/>');
modalDiv.addClass('modal');
const $modalPanel: JQuery = $('<div/>');
$modalPanel.addClass('modal-panel');
// Inject HTML template...
$modalPanel.load(specObject.templateUrl);
// Set up the angular controller...
const controllerAs: string = specObject.controllerAs
? specObject.controllerAs
: '$ctrl';
$modalPanel.attr('ng-controller', `${specObject.controller} as ${controllerAs}`);
modalDiv.append($modalPanel);
this.instance.element = modalDiv;
return modalDiv;
}
private setParent(specObject: IModalSpecObject): JQuery {
let $parent: JQuery;
if(!specObject.parent)
$parent = $(document);
else if (typeof specObject.parent === "string"
|| specObject.parent instanceof Element)
$parent = $(specObject.parent);
else if (specObject.parent instanceof jQuery)
$parent = specObject.parent;
else
$parent = $(document);
this.instance.$parent = $parent;
return $parent;
}
// !!!! !!!! I suspect this is where my problems lie. !!!! !!!!
private bindModalDiv(modalDiv: JQLite, $parent: JQuery): void {
const newScope: ng.IScope = this.$rootScope.$new(true);
// Try #1: Bind generated element to parent...
//$parent.append(this.$compile(modalDiv)(newScope));
// Try #1a: Generate bindings, then append to parent...
//const element: JQLite = this.$compile(modalDiv)(newScope);
//$parent.append(element);
// Try #2: Bind element to parent, then generate ng bindings...
//$parent.append(modalDiv);
//this.$compile(modalDiv)(newScope);
// Try #3: Well, what if we bind a controller to the scope?
const specObject: IModalSpecObject = this.instance.specObject;
const controllerAs: string = specObject.controllerAs
? specObject.controllerAs
: '$ctrl';
this.$controller(`${specObject.controller} as ${controllerAs}`, {
'$scope': newScope
});
const element = this.$compile(modalDiv)(newScope);
$parent.append(element);
}
}
angular
.module('app')
.service('modalSvc', ModalService);
SomeController.ts: SomeController.ts
в значительной степени простоуправляет кнопкой для запуска внешнего вида модала;По этой причине я не включил разметку.
export class SomeController {
public static $inject: string[] = [ 'modalSvc' ];
public constructor(
public modalSvc: ModalService
) {
}
public $onInit(): void {
}
public openModal(): void {
const newModal: IModalSpecObject = {
parent: 'body',
templateUrl: '/someModal.html',
controller: 'someModalCtrl',
data: {
'message': 'You should see this.'
}
};
this.modalSvc.open(newModal)
.then(() => {
console.log('You did it!');
});
}
}
angular.module('app').controller('someCtrl', SomeController);
someModal.html:
<div class="modal-header">
Important Message
</div>
<!-- This should read, "You should see this." -->
<div class="modal-body">
{{ $ctrl.message }}
</div>
<!-- You should click this, and hit a breakpoint and/or close the modal. -->
<div class="modal-footer">
<button ng-click="$ctrl.close()">Close</button>
</div>
someModal.ts:
export class SomeModalController {
public message: string = '';
public static $inject: string[] = [ 'modalSvc' ];
public constructor(
public modalSvc: ModalService
) {
}
public $onInit(): void {
console.log('$onInit was triggered!');
this.message = this.modalSvc.instance.specObject.data['message'];
}
public close(): void {
this.modalSvc.instance.close(null);
}
}
angular
.module('app')
.controller('someModalCtrl', SomeModalController);