动态选项卡中的动态内容(Angular、UI Bootstrap)
Dynamic Content in Dynamic Tab (Angular, UI Bootstrap)
我想在使用 AngularJs 和 UI Bootstrap.
动态生成的选项卡的内容中使用 ng-include
我这里有一个 Plunker:
http://plnkr.co/edit/2mpbovsu2eDrUdu8t7SM?p=preview
<div id="mainCntr" style="padding: 20px;">
<uib-tabset>
<uib-tab ng-repeat="tab in tabs" active="tab.active" disable="tab.disabled">
<uib-tab-heading>
{{tab.title}} <i class="glyphicon glyphicon-remove-sign" ng-click="removeTab($index)"></i>
</uib-tab-heading>
{{tab.content}}
</uib-tab>
</uib-tabset>
</div>
JS代码:
$scope.addTab = function() {
var len = $scope.tabs.length + 1;
var numLbl = '' + ((len > 9) ? '' : '0') + String(len);
var mrkUp = '<div>' +
'<h1>New Tab ' + numLbl + ' {{foo}}</h1>' +
'<div ng-include="tab.tabUrl" class="ng-scope"></div>' +
'</div>';
$scope.tabs.push({title: 'Tab ' + numLbl, content: $compile(angular.element(mrkUp))($scope)});
}
在 Plunker 中,单击 "Add Tab" 按钮。它调用 $scope 中的一个函数,该函数将一个新选项卡推送到集合中,但传入一些动态生成的内容,其中包括一个 ng-include 指令。预期的输出是 ng-include 将显示在选项卡内容区域中。
谢谢
在您的 Plunker 中,您使用的 ng-bind-html
不会为您编译 HTML。您可以创建一个新指令来为您执行此操作。
ng-bind-html
的源代码:
var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
return {
restrict: 'A',
compile: function ngBindHtmlCompile(tElement, tAttrs) {
var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
return (value || '').toString();
});
$compile.$$addBindingClass(tElement);
return function ngBindHtmlLink(scope, element, attr) {
$compile.$$addBindingInfo(element, attr.ngBindHtml);
scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
// we re-evaluate the expr because we want a TrustedValueHolderType
// for $sce, not a string
element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
});
};
}
};
}];
为新指令选择一个名称,例如 compile-html
。
将 tAttrs.ngBindHtml
替换为 tAttrs.compileHtml
(或您选择的任何名称)。
您需要将 $sce.getTrustedHtml
替换为 $sce.trustAsHtml
,否则您将得到 Error: [$sce:unsafe] Attempting to use an unsafe value in a safe context.
然后你需要调用$compile
:
$compile(element.contents())(scope);
完整指令:
app.directive('compileHtml', ['$sce', '$parse', '$compile',
function($sce, $parse, $compile) {
return {
restrict: 'A',
compile: function ngBindHtmlCompile(tElement, tAttrs) {
var ngBindHtmlGetter = $parse(tAttrs.compileHtml);
var ngBindHtmlWatch = $parse(tAttrs.compileHtml, function getStringValue(value) {
return (value || '').toString();
});
$compile.$$addBindingClass(tElement);
return function ngBindHtmlLink(scope, element, attr) {
$compile.$$addBindingInfo(element, attr.compileHtml);
scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
element.html($sce.trustAsHtml(ngBindHtmlGetter(scope)) || '');
$compile(element.contents())(scope);
});
};
}
};
}
]);
用法:
<div compile-html="tab.content"></div>
我的情况可能没有那么复杂,所以这个简单的解决方案有效:
sdo.tabs:{
data:[],
active:0,
reset: function(){
var tabs = this.data;
while( tabs.length > 0 ) {
this.removeTab( tabs[tabs.length-1].child.name);
}
this.active = 0;
},
childExists: function( childName ) {
var fromTheTop = this.data.length,
parentName = ( this.active > 0 ? this.data[ this.active - 1 ].child.name : 'zero' );
while( fromTheTop > this.active ) {
var child = this.data[ fromTheTop-1 ].child;
if( child && child.parent === parentName && child.name === childName ) return fromTheTop;
fromTheTop--;
}
return false;
},
removeTab: function( name ) { // will remove any descendents of this tab as well, see recursive call near end
var fromTheTop = this.data.length;
while( fromTheTop > 0 ) {
var tab = this.data[fromTheTop - 1];
if( tab.child.name === name ) {
angular.element( '#'+name ).empty();
this.data.splice( fromTheTop - 1);
return;
}
if( tab.child.parent === name) this.removeTab( tab.child.name );
fromTheTop--;
};
},
/*
* tab is string identifies tab but doesn't show in the UI
* tempmlate is HTML template
* scope is used to compile template
* title is string or function for UI tab title, appears in the tab row
*/
create: function( tab, template, scope, title ) {
var childName = tab;
var tabs = this.data;
tab = this.childExists( childName );
if( tab === false ) {
tab = tabs.length + 1;
} else { // recycling a tab, kill it & its descendents
this.removeTab( childName );
}
tabs[tab-1] = {
title:function(){
if( angular.isFunction(title) ) return title();
return title;
},
child: {
parent:( this.active > 0 ? this.data[ this.active - 1 ].child.name : 'zero' ),
name:childName
}
};
var ct = $timeout( function() {
angular.element( '#'+tabs[tab-1].child.name ).html( $compile( template )( scope ) );
sdo.tabs.active = tab;
return; // return nothing to avoid memory leak
});
scope.$on('$destroy', function() {
$timeout.cancel( ct );
});
return ct; // ct is a promise
}
}
HTML 是
<uib-tabset active="tabs.active">
<uib-tab index='0' heading="{{title}}">
<ng-view></ng-view>
</uib-tab>
<uib-tab ng-repeat="tab in tabs.data track by tab.child.name" heading="{{tab.title()}}" index='$index+1' >
<div id="{{tab.child.name}}"></div>
</uib-tab>
</uib-tabset>
在我的例子中,第一个选项卡由 Angular 路由器填充,这就是为什么选项卡数组是 tabs.active
中的一个索引的原因
我想在使用 AngularJs 和 UI Bootstrap.
动态生成的选项卡的内容中使用 ng-include我这里有一个 Plunker: http://plnkr.co/edit/2mpbovsu2eDrUdu8t7SM?p=preview
<div id="mainCntr" style="padding: 20px;">
<uib-tabset>
<uib-tab ng-repeat="tab in tabs" active="tab.active" disable="tab.disabled">
<uib-tab-heading>
{{tab.title}} <i class="glyphicon glyphicon-remove-sign" ng-click="removeTab($index)"></i>
</uib-tab-heading>
{{tab.content}}
</uib-tab>
</uib-tabset>
</div>
JS代码:
$scope.addTab = function() {
var len = $scope.tabs.length + 1;
var numLbl = '' + ((len > 9) ? '' : '0') + String(len);
var mrkUp = '<div>' +
'<h1>New Tab ' + numLbl + ' {{foo}}</h1>' +
'<div ng-include="tab.tabUrl" class="ng-scope"></div>' +
'</div>';
$scope.tabs.push({title: 'Tab ' + numLbl, content: $compile(angular.element(mrkUp))($scope)});
}
在 Plunker 中,单击 "Add Tab" 按钮。它调用 $scope 中的一个函数,该函数将一个新选项卡推送到集合中,但传入一些动态生成的内容,其中包括一个 ng-include 指令。预期的输出是 ng-include 将显示在选项卡内容区域中。
谢谢
在您的 Plunker 中,您使用的 ng-bind-html
不会为您编译 HTML。您可以创建一个新指令来为您执行此操作。
ng-bind-html
的源代码:
var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
return {
restrict: 'A',
compile: function ngBindHtmlCompile(tElement, tAttrs) {
var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
return (value || '').toString();
});
$compile.$$addBindingClass(tElement);
return function ngBindHtmlLink(scope, element, attr) {
$compile.$$addBindingInfo(element, attr.ngBindHtml);
scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
// we re-evaluate the expr because we want a TrustedValueHolderType
// for $sce, not a string
element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
});
};
}
};
}];
为新指令选择一个名称,例如 compile-html
。
将 tAttrs.ngBindHtml
替换为 tAttrs.compileHtml
(或您选择的任何名称)。
您需要将 $sce.getTrustedHtml
替换为 $sce.trustAsHtml
,否则您将得到 Error: [$sce:unsafe] Attempting to use an unsafe value in a safe context.
然后你需要调用$compile
:
$compile(element.contents())(scope);
完整指令:
app.directive('compileHtml', ['$sce', '$parse', '$compile',
function($sce, $parse, $compile) {
return {
restrict: 'A',
compile: function ngBindHtmlCompile(tElement, tAttrs) {
var ngBindHtmlGetter = $parse(tAttrs.compileHtml);
var ngBindHtmlWatch = $parse(tAttrs.compileHtml, function getStringValue(value) {
return (value || '').toString();
});
$compile.$$addBindingClass(tElement);
return function ngBindHtmlLink(scope, element, attr) {
$compile.$$addBindingInfo(element, attr.compileHtml);
scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
element.html($sce.trustAsHtml(ngBindHtmlGetter(scope)) || '');
$compile(element.contents())(scope);
});
};
}
};
}
]);
用法:
<div compile-html="tab.content"></div>
我的情况可能没有那么复杂,所以这个简单的解决方案有效:
sdo.tabs:{
data:[],
active:0,
reset: function(){
var tabs = this.data;
while( tabs.length > 0 ) {
this.removeTab( tabs[tabs.length-1].child.name);
}
this.active = 0;
},
childExists: function( childName ) {
var fromTheTop = this.data.length,
parentName = ( this.active > 0 ? this.data[ this.active - 1 ].child.name : 'zero' );
while( fromTheTop > this.active ) {
var child = this.data[ fromTheTop-1 ].child;
if( child && child.parent === parentName && child.name === childName ) return fromTheTop;
fromTheTop--;
}
return false;
},
removeTab: function( name ) { // will remove any descendents of this tab as well, see recursive call near end
var fromTheTop = this.data.length;
while( fromTheTop > 0 ) {
var tab = this.data[fromTheTop - 1];
if( tab.child.name === name ) {
angular.element( '#'+name ).empty();
this.data.splice( fromTheTop - 1);
return;
}
if( tab.child.parent === name) this.removeTab( tab.child.name );
fromTheTop--;
};
},
/*
* tab is string identifies tab but doesn't show in the UI
* tempmlate is HTML template
* scope is used to compile template
* title is string or function for UI tab title, appears in the tab row
*/
create: function( tab, template, scope, title ) {
var childName = tab;
var tabs = this.data;
tab = this.childExists( childName );
if( tab === false ) {
tab = tabs.length + 1;
} else { // recycling a tab, kill it & its descendents
this.removeTab( childName );
}
tabs[tab-1] = {
title:function(){
if( angular.isFunction(title) ) return title();
return title;
},
child: {
parent:( this.active > 0 ? this.data[ this.active - 1 ].child.name : 'zero' ),
name:childName
}
};
var ct = $timeout( function() {
angular.element( '#'+tabs[tab-1].child.name ).html( $compile( template )( scope ) );
sdo.tabs.active = tab;
return; // return nothing to avoid memory leak
});
scope.$on('$destroy', function() {
$timeout.cancel( ct );
});
return ct; // ct is a promise
}
}
HTML 是
<uib-tabset active="tabs.active">
<uib-tab index='0' heading="{{title}}">
<ng-view></ng-view>
</uib-tab>
<uib-tab ng-repeat="tab in tabs.data track by tab.child.name" heading="{{tab.title()}}" index='$index+1' >
<div id="{{tab.child.name}}"></div>
</uib-tab>
</uib-tabset>
在我的例子中,第一个选项卡由 Angular 路由器填充,这就是为什么选项卡数组是 tabs.active