Angular UI 模式中的包含不起作用
Transclusion in Angular UI Modal not working
this plunk 的 objective 是将控制器中的元素嵌入到 Angular UI Modal 中,其中 Modal 由指令包装。解决方案应遵循以下前提:
- 该指令声明了字段的嵌入。这些字段包含在控制器 HTML 标记的指令声明中。
- 控制器中声明的这些字段应该显示在模态中。
- 这些字段的范围应该可以在控制器中访问(请参阅我在控制器中声明了一个
input1
变量,该变量应该在模态中设置一个值)。
- 我定义了一个
content
元素来嵌入字段。该元素位于模式的模板中。我不确定何时可以使用此模板来嵌入它。
总而言之,objective 是要在控制器 HTML 标记中声明一组字段,并在模态中可用,其中模态包含在指令中,并且范围受到管理在控制器中。任何想法将不胜感激。
HTML
<div the-modal control="modalCtl">
<p>some text</p>
<input type="text" ng-model="input1" />
</div>
<button type="button" ng-click="open()">Open me!</button>
Javascript
var app = angular.module("app", ['ui.bootstrap']);
app.controller("ctl", function($scope,$timeout) {
$scope.modalCtl = {};
$scope.input1 = "abc";
$scope.open = function(){
$scope.modalCtl.openModal();
};
});
app.directive("theModal", function($uibModal) {
return {
restrict: "AE",
scope: {
control: "="
},
transclude: true,
link: function (scope, element, attrs, ctrl, transclude) {
scope.control = scope.control || {}
scope.control.openModal = function () {
scope.instance = $uibModal.open({
animation: false,
scope: scope,
template: '<div>in the template</div><div class="content"></div>'
});
element.find('.content').append(transclude());
};
}
}
});
transclude: true,
不是这样工作的,它会插入在指令范围内定义的任何标记,并将该标记放入指令模板中(您将在其中放置 ngTransclude
)。那不是你(看似)试图做的。
你正在寻找的是定义一个模板,给它一个 url 并使用 templateUrl
属性.
将它提供给模式
HTML
<script type="text/ng-template" id="/some-tpl.html">
<p>some text</p>
<input type="text" value="1234" />
</script>
JS
$uibModal.open({
animation: false,
scope: scope,
templateUrl: "/some-tpl.html" // link to template url
})
然后您可以将 directive/your 逻辑放在您提供模态的控制器中。
您已经非常接近通过嵌入实现 objective,但是,您需要考虑以下几点:
首先,根据UI Bootstrap docs,options
中有一个appendTo
属性为$uibModal.open()
方法默认为 body
.
If appendTo
is not specified, the modal will be appended to the body
of your page and becomes a direct child of the body
. Therefore querying .content
in your directive via element.find('.content')
won't work because it doesn't exist there.
其次,AngularJS自带jQLite,是jQuery的轻量级版本。这意味着对 jQuery 的大部分功能的支持有限。一种这样的情况是 .find()
方法只适用于标签名称。
为了让它像 jQuery 一样工作(虽然你真的不必这样做,因为你仍然可以在链中使用 .children()
来查询嵌套的 DOM 元素),你必须在 Angular 之前加载 jQuery(我想你已经)。
参考 AngularJS docs on angular.element
了解更多信息。
呈现 DOM 需要一点时间 Angular 因为它需要进行与范围和视图相关的正确绑定,以完成摘要循环,并且很快。
因此,您最终可能会立即查询一个实际上可能尚未呈现的 DOM 元素。
The trick to wait for DOM rendering and completion of a digest cycle is to wrap your DOM related code into $timeout
wrapper.
考虑到以上几点,自定义指令 theModal
的 link 函数中的 openModal
方法应该如下所示:
scope.control.openModal = function () {
scope.instance = $uibModal.open({
animation: false,
scope: scope,
template: '<div>in the template</div><div class="content"></div>',
/**
* Make sure the modal is appended to your directive and NOT `body`
*/
appendTo: element
});
/**
* Give Angular some time to render your DOM
*/
$timeout(function (){
/**
* In case jQuery is not available
*/
// var content = element.children('.modal').children('.modal-dialog').children('.modal-content').children('.content');
/**
* Since you have jQuery loaded already
*/
var content = element.find('.content');
/**
* Finally, append the transcluded element to the correct position,
* while also making sure that the cloned DOM is bound to the parent scope (i.e. ctl)
*/
transclude(scope.$parent, function(clonedContent){
content.append(clonedContent);
});
});
};
请注意 transclude
函数如何让您控制如何将一些嵌入的 DOM 绑定到 自定义范围 而不是默认指令的范围.普通的 transclude()
调用将考虑当前可用的范围对象——即指令的范围——以绑定嵌入的 DOM.
Demo
正如前面的答案所建议的,您可以使用 appendTo
属性 将指令的元素提供为模态的父元素。
您可以 "wait for the modal's template to be rendered" 通过使用 UibModalIstance 中的 rendered
promise。 (Documentation)。
scope.control.openModal = function () {
scope.instance = $uibModal.open({
animation: false,
scope: scope,
template: '<div>in the template</div><div class="content"></div>',
appendTo: element
});
// We use the redered promise to make sure
// the modal's template has been loaded.
scope.instance.rendered.then(function (){
// You'll most likely want to pass the `$parent` scope as the first
// parameter for proper scope binding with your controller.
element.find('.content').append(transclude(scope.$parent));
});
};
this plunk 的 objective 是将控制器中的元素嵌入到 Angular UI Modal 中,其中 Modal 由指令包装。解决方案应遵循以下前提:
- 该指令声明了字段的嵌入。这些字段包含在控制器 HTML 标记的指令声明中。
- 控制器中声明的这些字段应该显示在模态中。
- 这些字段的范围应该可以在控制器中访问(请参阅我在控制器中声明了一个
input1
变量,该变量应该在模态中设置一个值)。 - 我定义了一个
content
元素来嵌入字段。该元素位于模式的模板中。我不确定何时可以使用此模板来嵌入它。
总而言之,objective 是要在控制器 HTML 标记中声明一组字段,并在模态中可用,其中模态包含在指令中,并且范围受到管理在控制器中。任何想法将不胜感激。
HTML
<div the-modal control="modalCtl">
<p>some text</p>
<input type="text" ng-model="input1" />
</div>
<button type="button" ng-click="open()">Open me!</button>
Javascript
var app = angular.module("app", ['ui.bootstrap']);
app.controller("ctl", function($scope,$timeout) {
$scope.modalCtl = {};
$scope.input1 = "abc";
$scope.open = function(){
$scope.modalCtl.openModal();
};
});
app.directive("theModal", function($uibModal) {
return {
restrict: "AE",
scope: {
control: "="
},
transclude: true,
link: function (scope, element, attrs, ctrl, transclude) {
scope.control = scope.control || {}
scope.control.openModal = function () {
scope.instance = $uibModal.open({
animation: false,
scope: scope,
template: '<div>in the template</div><div class="content"></div>'
});
element.find('.content').append(transclude());
};
}
}
});
transclude: true,
不是这样工作的,它会插入在指令范围内定义的任何标记,并将该标记放入指令模板中(您将在其中放置 ngTransclude
)。那不是你(看似)试图做的。
你正在寻找的是定义一个模板,给它一个 url 并使用 templateUrl
属性.
HTML
<script type="text/ng-template" id="/some-tpl.html">
<p>some text</p>
<input type="text" value="1234" />
</script>
JS
$uibModal.open({
animation: false,
scope: scope,
templateUrl: "/some-tpl.html" // link to template url
})
然后您可以将 directive/your 逻辑放在您提供模态的控制器中。
您已经非常接近通过嵌入实现 objective,但是,您需要考虑以下几点:
首先,根据UI Bootstrap docs,
options
中有一个appendTo
属性为$uibModal.open()
方法默认为body
.If
appendTo
is not specified, the modal will be appended to thebody
of your page and becomes a direct child of thebody
. Therefore querying.content
in your directive viaelement.find('.content')
won't work because it doesn't exist there.其次,AngularJS自带jQLite,是jQuery的轻量级版本。这意味着对 jQuery 的大部分功能的支持有限。一种这样的情况是
.find()
方法只适用于标签名称。为了让它像 jQuery 一样工作(虽然你真的不必这样做,因为你仍然可以在链中使用
.children()
来查询嵌套的 DOM 元素),你必须在 Angular 之前加载 jQuery(我想你已经)。参考 AngularJS docs on
angular.element
了解更多信息。呈现 DOM 需要一点时间 Angular 因为它需要进行与范围和视图相关的正确绑定,以完成摘要循环,并且很快。 因此,您最终可能会立即查询一个实际上可能尚未呈现的 DOM 元素。
The trick to wait for DOM rendering and completion of a digest cycle is to wrap your DOM related code into
$timeout
wrapper.
考虑到以上几点,自定义指令 theModal
的 link 函数中的 openModal
方法应该如下所示:
scope.control.openModal = function () {
scope.instance = $uibModal.open({
animation: false,
scope: scope,
template: '<div>in the template</div><div class="content"></div>',
/**
* Make sure the modal is appended to your directive and NOT `body`
*/
appendTo: element
});
/**
* Give Angular some time to render your DOM
*/
$timeout(function (){
/**
* In case jQuery is not available
*/
// var content = element.children('.modal').children('.modal-dialog').children('.modal-content').children('.content');
/**
* Since you have jQuery loaded already
*/
var content = element.find('.content');
/**
* Finally, append the transcluded element to the correct position,
* while also making sure that the cloned DOM is bound to the parent scope (i.e. ctl)
*/
transclude(scope.$parent, function(clonedContent){
content.append(clonedContent);
});
});
};
请注意 transclude
函数如何让您控制如何将一些嵌入的 DOM 绑定到 自定义范围 而不是默认指令的范围.普通的 transclude()
调用将考虑当前可用的范围对象——即指令的范围——以绑定嵌入的 DOM.
Demo
正如前面的答案所建议的,您可以使用 appendTo
属性 将指令的元素提供为模态的父元素。
您可以 "wait for the modal's template to be rendered" 通过使用 UibModalIstance 中的 rendered
promise。 (Documentation)。
scope.control.openModal = function () {
scope.instance = $uibModal.open({
animation: false,
scope: scope,
template: '<div>in the template</div><div class="content"></div>',
appendTo: element
});
// We use the redered promise to make sure
// the modal's template has been loaded.
scope.instance.rendered.then(function (){
// You'll most likely want to pass the `$parent` scope as the first
// parameter for proper scope binding with your controller.
element.find('.content').append(transclude(scope.$parent));
});
};