【问题标题】:Dynamic Content in Dynamic Tab (Angular, UI Bootstrap)动态选项卡中的动态内容(Angular,UI Bootstrap)
【发布时间】:2015-11-13 00:40:11
【问题描述】:

我想在使用 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 中,单击“添加选项卡”按钮。它调用 $scope 中的一个函数,该函数将一个新选项卡推送到集合中,但传入一些动态生成的内容,其中包括一个 ng-include 指令。预期的输出是 ng-include 将显示在选项卡内容区域内。

谢谢

【问题讨论】:

  • 我认为您的 plunk 工作不正常?
  • 我无法让它工作。笨拙只是我的尝试 - 希望这里有人可以帮助我让它工作。
  • ...哦,现在可以了。当我之前看它时,它被打破了。会再看的。
  • 现在我想起来了,您在按下选项卡之前正在执行 $compile。考虑在你的 $scope.tabs 上放置一个手表,它会在你推送它之后运行 $compile。

标签: angularjs angular-ui-bootstrap angular-ui-bootstrap-tab


【解决方案1】:

在您的 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>

演示: http://plnkr.co/edit/TRYAaxeEPMTAay6rqEXp?p=preview

【讨论】:

    【解决方案2】:

    我的情况可能没有那么复杂,所以这个简单的解决方案有效:

    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 的一个索引

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-26
      • 1970-01-01
      • 2016-10-25
      • 2020-06-11
      • 2014-07-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多