【问题标题】:How to handle tree like structure in angularjs?如何在angularjs中处理树状结构?
【发布时间】:2016-02-05 03:12:39
【问题描述】:

我正在尝试在复杂的 angular.js Web 应用程序中以树的形式实现层次结构。我正在使用嵌套的 ng-repeats 来渲染结构,但我在 IE 10 中遇到了与性能相关的重大问题,在 chrome 中遇到了较小的性能问题。最终级别将使用的数据将包含多达 5,000 个条目。 根据我的研究,我认为以下可能是其背后的原因:

  1. 大量的观察者元素。 为了解决这个问题,我已经实现了一次数据绑定,并且观察者的数量并不多。
  2. 浏览器重绘时间: ng-repeat 将元素一一添加到 DOM 中。这可能会导致浏览器引擎超载以多次渲染复杂的 HTML,从而导致大量延迟。 为了解决这个问题,我应用了延迟加载技术,仅在一个节点折叠时才渲染子节点。我仍然在渲染节点时遇到可观察到的延迟,其中要渲染的节点数量很大。
  3. CSS 类: 我尝试通过从节点元素中剥离所有类来实现树结构。这导致了显着的改进,但删除类并不是一个真正的选择。此外,如果我为元素提供内联样式,那么它也会带来更好的性能。
  4. Angular 材质的性能问题: Angular 材质是我的 Web 应用程序不可或缺的一部分。在查看了 Angular Material 用户提交的大量 ng-repeats 问题后,这些问题已经推出了修复。但升级到最新版本也无济于事。

refer 到这张图片为桌子设计。用于创建树的模板如下:

<li ng-repeat="item in ::item.childLevelDetails" >
 <div >
    <a ng-click="toggle(this)" class="icon icon-stream-add-2">
      <span></span>
    </a>
    <div class="unitTextDiv">{{::item.title}}</div>
 </div>
 <ol ng-include= "'tree_node'">
</li>

请求您提出任何可能解决此问题的方法。

【问题讨论】:

    标签: angularjs performance angularjs-ng-repeat internet-explorer-10


    【解决方案1】:

    angular-ui-grid 可以被认为是一种选择。它使用虚拟化并仅呈现可见的行。因此,它在大量行的情况下也表现良好。它带有一个很棒的文档和示例http://ui-grid.info/docs/#/tutorial

    请参阅分组示例以确保这有助于您的用例 http://ui-grid.info/docs/#/tutorial/209_grouping

    【讨论】:

    • 这看起来是一个很好的解决方案,它确实是一个非常方便的模块......谢谢
    【解决方案2】:

    是时候将stick 与此potion 一起使用并根据black magic 完成!

    【讨论】:

      【解决方案3】:

      你可以试试我为你做的这个递归示例。 使用 ng-if 显示/隐藏元素会减少手表的数量。

      在这里找到Fiddler

      var myApp = angular.module('myApp',[]);
      
       myApp.controller('MyCtrl', ['$scope','$timeout', 'getWatchCount' , function ($scope ,$timeout, getWatchCount ){
      
       $scope.tree = [
      	{title:'Element level 1',
           elements: [
           	{ title: 'Element level 1.1'},
              { title: 'Element level 1.2',
                elements: [
      			{ title: 'Element level 1.2.2'},
      			{ title: 'Element level 1.2.2'},
                ]}
          ]},
           {title:'Element level 2'}
       	]
       
       
       //NEXT CODE ONLY USED FOR COUNTING WATCHES//
       $scope.countWatches = function(){
       	$scope.numberOfWatches = getWatchCount();
       }
       
       $timeout(function(){$scope.countWatches()} , 0 );
       
           
       // I return the count of watchers on the current page.
      function getWatchCount() {
      
          // Keep track of the total number of watch bindings on the page.
          var total = 0;
      
          // There are cases in which two different ng-scope markers will actually be referencing
          // the same scope, such as with transclusion into an existing scope (ie, cloning a node
          // and then linking it with an existing scope, not a new one). As such, we need to make
          // sure that we don't double-count scopes.
          var scopeIds = {};
      
          // AngularJS denotes new scopes in the HTML markup by appending the classes "ng-scope"
          // and "ng-isolate-scope" to appropriate elements. As such, rather than attempting to
          // navigate the hierarchical Scope tree, we can simply query the DOM for the individual
          // scopes. Then, we can pluck the watcher-count from each scope.
          // --
          // NOTE: Ordinarily, it would be a HUGE SIN for an AngularJS service to access the DOM
          // (Document Object Model). But, in this case, we're not really building a true AngularJS
          // service, so we can break the rules a bit.
          angular.forEach(
              document.querySelectorAll( ".ng-scope , .ng-isolate-scope" ),
              countWatchersInNode
          );
      
          return( total );
      
      
          // ---
          // PRIVATE METHODS.
          // ---
      
      
          // I count the $watchers in to the scopes (regular and isolate) associated with the given
          // element node, and add the count to the running total.
          function countWatchersInNode( node ) {
      
              // Get the current, wrapped element.
              var element = angular.element( node );
      
              // It seems that in earlier versions of AngularJS, the separation between the regular
              // scope and the isolate scope where not as strong. The element was flagged as having
              // an isolate scope (using the ng-isolate-scope class); but, there was no .isolateScope()
              // method before AngularJS 1.2. As such, in earlier versions of AngularJS, we have to
              // fall back to using the .scope() method for both regular and isolate scopes.
              if ( element.hasClass( "ng-isolate-scope" ) && element.isolateScope ) {
      
                  countWatchersInScope( element.isolateScope() );
      
              }
      
              // This class denotes a non-isolate scope in later versions of AngularJS; but,
              // possibly an isolate-scope in earlier versions of AngularJS (1.0.8).
              if ( element.hasClass( "ng-scope" ) ) {
      
                  countWatchersInScope( element.scope() );
      
              }
      
          }
      
      
          // I count the $$watchers in the given scope and add the count to the running total.
          function countWatchersInScope( scope ) {
      
              // Make sure we're not double-counting this scope.
              if ( scopeIds.hasOwnProperty( scope.$id ) ) {
      
                  return;
      
              }
      
              scopeIds[ scope.$id ] = true;
      
              // The $$watchers value starts out as NULL until the first watcher is bound. As such,
              // the $$watchers collection may not exist yet on this scope.
              if ( scope.$$watchers ) {
      
                  total += scope.$$watchers.length;
      
              }
      
          }
      
      }
       
       }]);
      
      myApp.factory(
      			"getWatchCount",
      			function() {
      
      				// I return the count of watchers on the current page.
      				function getWatchCount() {
      
      					var total = 0;
      
      					// AngularJS denotes new scopes in the HTML markup by appending the
      					// class "ng-scope" to appropriate elements. As such, rather than 
      					// attempting to navigate the hierarchical Scope tree, we can simply
      					// query the DOM for the individual scopes. Then, we can pluck the 
      					// watcher-count from each scope.
      					// --
      					// NOTE: Ordinarily, it would be a HUGE SIN for an AngularJS service
      					// to access the DOM (Document Object Model). But, in this case,
      					// we're not really building a true AngularJS service, so we can 
      					// break the rules a bit.
      					angular.element( ".ng-scope" ).each(
      						function ngScopeIterator() {
      
      							// Get the scope associated with this element node.
      							var scope = $( this ).scope();
      
      							// The $$watchers value starts out as NULL. 
      							total += scope.$$watchers
      								? scope.$$watchers.length
      								: 0
      							;
      
      						}
      					);
      					
      					return( total );
      
      				}
      
      				// For convenience, let's serialize the above method and convert it to 
      				// a bookmarklet that can easily be run on ANY AngularJS page. 
      				getWatchCount.bookmarklet = ( 
      					"javascript:alert('Watchers:'+(" + 
      					getWatchCount.toString()
      						.replace( /\/\/.*/g, " " )
      						.replace( /\s+/g, " " ) +
      					")());void(0);" 
      				);
      
      				return( getWatchCount );
      
      			}
      		);
      ul{
          list-style-type: none;
      }
      
      li{
          font-size:13px;
      }
      .arrow{
           width: 0;
          height: 0;
          border-top: 7px solid transparent;
          border-bottom: 7px solid transparent;
          border-right: 7px solid transparent;
          cursor: pointer;
          margin-left: 5px;
          border-left: 7px solid #000;
          display: inline-block;
          transition:all 0.3s; 
      }
      
      .arrow.expand {
          transform: rotate(45deg);
          transform-origin: 20% 50%;
          margin-top: 0;
      }
      
      .arrow.none {
          border-left: 7px solid #ccc;
      }
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
      <div ng-app="myApp" ng-controller="MyCtrl" >
      
          <p>
      		<strong>Watch Count:</strong> {{ numberOfWatches }}
          </p>
          
          <script type="text/ng-template" id="elementTree">
              <li>
                  <div class="arrow"
                       
                       ng-class="{expand:element.isOpen,none:!element.elements}"
                       ng-click="$apply(element.isOpen = !element.isOpen) ; countWatches()">
                  </div>
                  {{element.title}}
              </li>
              <div ng-if="element.isOpen">
                  <ul
                      ng-repeat="element in element.elements"
                      ng-include="'elementTree'">
                   </ul
              </div>
              
          </script>
          
          <ul ng-repeat="element in tree"
               ng-include="'elementTree'">
          </ul>
          
          
      </div>

      【讨论】:

      • 通过使用 angular 的一次性绑定功能,手表的数量从来都不是问题。主要问题是元素渲染的严重延迟。这是我修改的 Fiddler 的分支,它展示了渲染问题
      猜你喜欢
      • 2015-06-10
      • 1970-01-01
      • 1970-01-01
      • 2015-03-30
      • 2023-03-14
      • 2018-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多