测试 ng-transclude 不起作用
testing ng-transclude doesn't work
我正在编写两个包装 ui-bootstrap 的 tabset 和 tab 指令的指令。
为了将我的指令的内容传递给包装的指令,我在这两个指令中都使用了 t运行sclusion。
这 quite 很好用,唯一的问题是我没有编写测试来检查它。我的测试使用替换指令作为包装指令的模拟,我在每次测试之前使用 $compileProvider 替换它。
测试代码如下所示:
beforeEach(module('myModule', function($compileProvider) {
// Mock the internally used 'tab' which is a third party and should not be tested here
$compileProvider.directive('tab', function() {
// Provide a directive with a high priority and 'terminal' set to true, makes sure that
// the mock directive will get executed, and that the real directive will not
var mock = {
priority: 100,
terminal: true,
restrict: 'EAC',
replace: true,
transclude: true,
template: '<div class="mock" ng-transclude></div>'
};
return mock;
});
}));
beforeEach(function() {
inject(function(_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
});
});
beforeEach(function() {
$scope = $rootScope.$new();
});
afterEach(function() {
$scope.$destroy();
});
it('Places the enclosed html inside the tab body', function() {
element = $compile("<div><my-tab>test paragraph</my-tab></div>")($scope);
$scope.$digest();
console.log("element.html() = ", element.html());
expect(element.text().trim()).toEqual("test paragraph");
});
我的指令模板如下所示:
<div><tab><div ng-transclude></div></tab></div>
指令模块看起来像这样:
angular.module('myModule', ['ui.bootstrap'])
.directive('myTab', function() {
return {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: 'templates/my-tab.tpl.html',
scope: {
}
};
});
打印到控制台的结果是这样的:
LOG: 'element.html() = ', '<div class="ng-isolate-scope" id=""><div id="" heading="" class="mock"><ng-transclude></ng-transclude></div></div>'
关于为什么 t运行 排除没有发生的任何想法(同样,它在测试之外工作得很好)?
更新
我已经转向其他事情和指令,并且 运行 再次进入这个问题,但现在它更重要,原因是,我放在父指令中的指令 requires 父控制器在其 link 函数中。
我对此进行了更多研究,结果发现由于某种原因,编译 mock 指令不会创建 t运行scluded 内容的实例。
我知道的原因是,我在两个指令(模拟和 t运行scluded 指令)中的每个可能的钩子中都放置了一个打印输出,即编译、pre-link、post-link 和控制器构造函数,我看到唯一的打印输出来自 mock 指令。
现在,这是真正有趣的部分:我尝试在 mock 指令的 link 函数中使用 t运行sclude 函数 "force" 编译 t运行scluded 指令,有效! (另一个证明它没有隐式发生的证据)。
你问的问题在哪里?嗯,还是不行。这一次,由于 t运行scluded 指令的 link 函数失败,因为它没有找到 mock 指令的控制器。什么?!
代码如下:
代码
var mod = angular.module('MyModule', []);
mod.directive('parent', function() {
return {
restrict: 'E',
replace: true,
template: '<div class="parent">...</div>',
controller: function() {
this.foo = function() { ... };
}
};
});
mod.directive('child', function() {
return {
restrict: 'E',
require: '^parent',
link: function(scope, element, attrs, parentCtrl) {
parentCtrl.foo();
}
};
});
测试
describe('child directive', function() {
beforeEach(module('MyModule', function($compileProvider) {
$compileProvider.directive('parent', function() {
return {
priority: 100,
terminal: true,
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="mock"><ng-transclude></ng-transclude></div>',
controller: function() {
this.foo = jasmine.createSpy();
},
link: function(scope, element, attrs, ctrls, transcludeFn) {
transcludeFn();
}
};
});
}));
});
此测试失败并显示错误消息,例如:
Error: [$compile:ctreq] Controller 'parent', required by directive
'child', can't be found!
如有任何想法、想法和建议,我们将不胜感激。
好的,可能是 SO 历史上最短的赏金......
问题出在 mock 指令的 terminal: true
和 priority: 100
属性上。我的印象是(来自我在网上阅读的一篇关于如何模拟指令的文章),这些属性导致编译器停止编译具有相同名称的指令并优先考虑首先评估模拟指令。
我显然错了。查看this and this,很明显:
- 'terminal' 停止任何 其他尚未处理的指令
- 'priority' 用于确保模拟指令在它模拟的指令之前被处理
问题在于,这会导致所有其他处理停止,包括 ng-transclude
指令,它的默认优先级为 0
。
但是,删除这些属性会导致一切乱套,因为这两个指令都已注册,等等(我不会用所有血淋淋的细节来增加您的负担)。为了能够删除这些属性,这两个指令应该驻留在不同的模块中,并且它们之间应该没有依赖关系。简而言之,在测试 child
指令时,唯一被评估的名为 parent
的指令应该是 mock 指令。
为了支持实际使用,我在系统中引入了三个模块:
child
指令的模块(无依赖项)
parent
指令的模块(无依赖项)
- 一个没有内容但依赖于
child
和 parent
模块的模块,这是您唯一需要在代码中添加为依赖项的模块
差不多就这些了。我希望它能帮助遇到此类问题的其他人。
我正在编写两个包装 ui-bootstrap 的 tabset 和 tab 指令的指令。
为了将我的指令的内容传递给包装的指令,我在这两个指令中都使用了 t运行sclusion。
这 quite 很好用,唯一的问题是我没有编写测试来检查它。我的测试使用替换指令作为包装指令的模拟,我在每次测试之前使用 $compileProvider 替换它。
测试代码如下所示:
beforeEach(module('myModule', function($compileProvider) {
// Mock the internally used 'tab' which is a third party and should not be tested here
$compileProvider.directive('tab', function() {
// Provide a directive with a high priority and 'terminal' set to true, makes sure that
// the mock directive will get executed, and that the real directive will not
var mock = {
priority: 100,
terminal: true,
restrict: 'EAC',
replace: true,
transclude: true,
template: '<div class="mock" ng-transclude></div>'
};
return mock;
});
}));
beforeEach(function() {
inject(function(_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
});
});
beforeEach(function() {
$scope = $rootScope.$new();
});
afterEach(function() {
$scope.$destroy();
});
it('Places the enclosed html inside the tab body', function() {
element = $compile("<div><my-tab>test paragraph</my-tab></div>")($scope);
$scope.$digest();
console.log("element.html() = ", element.html());
expect(element.text().trim()).toEqual("test paragraph");
});
我的指令模板如下所示:
<div><tab><div ng-transclude></div></tab></div>
指令模块看起来像这样:
angular.module('myModule', ['ui.bootstrap'])
.directive('myTab', function() {
return {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: 'templates/my-tab.tpl.html',
scope: {
}
};
});
打印到控制台的结果是这样的:
LOG: 'element.html() = ', '<div class="ng-isolate-scope" id=""><div id="" heading="" class="mock"><ng-transclude></ng-transclude></div></div>'
关于为什么 t运行 排除没有发生的任何想法(同样,它在测试之外工作得很好)?
更新
我已经转向其他事情和指令,并且 运行 再次进入这个问题,但现在它更重要,原因是,我放在父指令中的指令 requires 父控制器在其 link 函数中。
我对此进行了更多研究,结果发现由于某种原因,编译 mock 指令不会创建 t运行scluded 内容的实例。
我知道的原因是,我在两个指令(模拟和 t运行scluded 指令)中的每个可能的钩子中都放置了一个打印输出,即编译、pre-link、post-link 和控制器构造函数,我看到唯一的打印输出来自 mock 指令。
现在,这是真正有趣的部分:我尝试在 mock 指令的 link 函数中使用 t运行sclude 函数 "force" 编译 t运行scluded 指令,有效! (另一个证明它没有隐式发生的证据)。
你问的问题在哪里?嗯,还是不行。这一次,由于 t运行scluded 指令的 link 函数失败,因为它没有找到 mock 指令的控制器。什么?!
代码如下:
代码
var mod = angular.module('MyModule', []);
mod.directive('parent', function() {
return {
restrict: 'E',
replace: true,
template: '<div class="parent">...</div>',
controller: function() {
this.foo = function() { ... };
}
};
});
mod.directive('child', function() {
return {
restrict: 'E',
require: '^parent',
link: function(scope, element, attrs, parentCtrl) {
parentCtrl.foo();
}
};
});
测试
describe('child directive', function() {
beforeEach(module('MyModule', function($compileProvider) {
$compileProvider.directive('parent', function() {
return {
priority: 100,
terminal: true,
restrict: 'E',
replace: true,
transclude: true,
template: '<div class="mock"><ng-transclude></ng-transclude></div>',
controller: function() {
this.foo = jasmine.createSpy();
},
link: function(scope, element, attrs, ctrls, transcludeFn) {
transcludeFn();
}
};
});
}));
});
此测试失败并显示错误消息,例如:
Error: [$compile:ctreq] Controller 'parent', required by directive 'child', can't be found!
如有任何想法、想法和建议,我们将不胜感激。
好的,可能是 SO 历史上最短的赏金......
问题出在 mock 指令的 terminal: true
和 priority: 100
属性上。我的印象是(来自我在网上阅读的一篇关于如何模拟指令的文章),这些属性导致编译器停止编译具有相同名称的指令并优先考虑首先评估模拟指令。
我显然错了。查看this and this,很明显:
- 'terminal' 停止任何 其他尚未处理的指令
- 'priority' 用于确保模拟指令在它模拟的指令之前被处理
问题在于,这会导致所有其他处理停止,包括 ng-transclude
指令,它的默认优先级为 0
。
但是,删除这些属性会导致一切乱套,因为这两个指令都已注册,等等(我不会用所有血淋淋的细节来增加您的负担)。为了能够删除这些属性,这两个指令应该驻留在不同的模块中,并且它们之间应该没有依赖关系。简而言之,在测试 child
指令时,唯一被评估的名为 parent
的指令应该是 mock 指令。
为了支持实际使用,我在系统中引入了三个模块:
child
指令的模块(无依赖项)parent
指令的模块(无依赖项)- 一个没有内容但依赖于
child
和parent
模块的模块,这是您唯一需要在代码中添加为依赖项的模块
差不多就这些了。我希望它能帮助遇到此类问题的其他人。