【发布时间】:2019-03-26 21:13:05
【问题描述】:
对于我正在从事的教育副项目,我想避免使用 AngularJS Material Design、UI Bootstrap 或任何提供模态功能的自定义库。
但是,我遇到了一个障碍。我创建了一个应该管理和动态创建模式的服务。它提供了一个open 函数,该函数接受一个规范对象,然后在 DOM 中复制该对象。
这段代码的实际作用:
1. modal 正确地附加到 DOM。
2.模态控制器的$onInit函数触发。
这段代码没有做什么:
1. 将标记中的$ctrl.message 属性绑定到我们知道启动的控制器实例。
通常,我会在提供代码后问我的问题,但是重现此问题需要大量代码(如下所示,没有一些 AngularJS 样板。)不过,既然如此,这是我的 问题:
我可以通过什么方式让该服务分离出来的模式正确地将它们的内容绑定到给定的控制器?
我的尝试:
正如您在ModalService.bindModalDiv 中看到的那样,我尝试了几种思路,主要是使用$compile。然而,$compile 和生成的链接函数实际上似乎并未将新的 DOM 元素绑定到 Angular。
我尝试使用$controller 将正在生成的新范围显式绑定到正在实例化的someModalCtrl,但这似乎根本没有帮助。
因为我可以在someModalCtrl 上设置断点,并查看我用作完整性检查的console.log 消息,我想我误解了我应该如何将新的DOM 元素绑定到Angular。我确定我错过了一些我已经设法忘记或忽视的基本内容。
还有一点说明:
我确信让模态正确绑定到 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);
【问题讨论】:
标签: angularjs typescript modal-dialog model-binding