0

As we known, the angular have performance if there are more than 2000 records in the page, because only some of the records need add some behavior , so I would prefer to dynamically add attribute to element according model value in link function, as there will be less watches.

So I use $compile to recompile the element like below:

mainApp.directive("popoverSetting", function ($compile) {
    return {
        restrict: "A",
        link: function (scope, element, attrs) {
            if (scope.item.isTrue) {
                element.attrs("ns-popover-trigger", "mouseenter");
                element.attrs("ns-popover-timeout", "0.01");

                $compile(element)(scope);
            }
        }
    }
})

Because there are about 1000 records, so the speed is very slow, is there some other way to add attribute and compile quickly? although there only 5 records need to add these attribute, it still increase about twofold time than before.

Ron Smith
  • 197
  • 3
  • 17
  • If it was me i would delegate the mouse events to the main parent of all of these elements and trigger the popover from that single event listener. Then there is nothing to compile at element level, just add a class instead on the ones that have popover – charlietfl May 06 '15 at 03:50
  • @charlietfl,I think like what you do, you need control popover operation yourself. but here there is an directive to support and I just need to add the attribute it can work immediately. – Ron Smith May 06 '15 at 03:58
  • Well that's all fine and good if that simple directive wasn't causing serious performance problems – charlietfl May 06 '15 at 04:02
  • what is controlling the popovers? Wouldn't it be better to include the logic internally in the directive rather than trying to update the entire DOM to add or remove logic? – tpie May 06 '15 at 04:03
  • @charlietfl, the simple directive is great. but there are more than 1000 records, if I add these attribute related to the directive to element, and determine whether this element should show the popover information, it need 1000 watches at least, so it will cause performance issue. I think use dynamic compile to add attribute as necessary. but it also make it more slow. – Ron Smith May 06 '15 at 05:13
  • @ tpie, yes you are right , the directive need contain logic itself. but it will according to watch one property whether need to apply to. so because the amount of data is large so cause low performance – Ron Smith May 06 '15 at 05:22
  • I really don't think you understand what event delegation means. It is one event listener on the parent element only .... the popovers show if the sub element has the class or not. One event handler for all 1000 of your elements vs 1000 listeners. No more directive on the 1000 elements that way – charlietfl May 06 '15 at 12:06

2 Answers2

0

The problem with trying to do this is that nested compiles are slow (an understatement), especially when you are talking thousands of them. It just isn't the way to go.

I could see where some may consider this ugly, and depending on your needs, this solution may NOT work for you, but it does get everything compiled pretty quickly, with the attributes applied. I ended up building a compiler directive that would generate markup based on the dataset, adding the necessary attributes if isTrue was true, and then compiling the whole thing once. The ugly part is that it doesn't generate bindings. So if you need to change something or update something, you'll have to recompile the whole thing.

It's running right now at about 200-300ms to compile the whole thing with 2000 items in the dataset.

Plunker

Directives:

app.directive('nsPopoverTrigger', function($timeout) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      element.on(attrs.nsPopoverTrigger, function() {
               console.log('popover triggered')
             })  
    }
  }
})
app.directive('compiler', function($compile) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var i = 0, template = '<ul>'
      scope.generateMarkup = function() {
        for (i; i < scope.data.length; i++) {
          if(scope.data[i].isTrue) {
            template = template + "<li ns-popover-trigger='mouseenter' ns-popover-timeout='0.01'>" + scope.data[i].key + "</li>"
          } else {
          template = template + '<li>' + scope.data[i].key + '</li>'
        }
        }
      var $template = angular.element(template)
      $compile($template)(scope)
      element.append($template)
      }
    }
  }
})
tpie
  • 6,021
  • 3
  • 22
  • 41
  • the reason it is necessary to recompile is that there is a directive to realize the requirement. basically the angular will compile these attribute to add behaviors to the element, so it can work right? but in link function ,if I add these attribute without the compile this popover directive can't work – Ron Smith May 06 '15 at 06:04
  • see my updated solution... I am curious to see if there is a way to get it compiled that quickly with the bindings intact. – tpie May 06 '15 at 07:43
  • yeah, I also curious, and I don't know the cost of the $compile function. if I add the popoverSetting directive to li element, of course li also have ng-repeate directive. when one item need add the popover attributes, after I called the $compile(element)(scope), what will happen? just compile current element? if the scope is root scope, what will happen? – Ron Smith May 06 '15 at 08:10
  • You could compile it again, but i think it would be costly, especially for just updating one element. – tpie May 06 '15 at 08:15
  • Lots person had met this issue. Actually if there are more than 1000 records, it also cost not much time. but it cause infinite loop after call the compile function. so need to remove the directive from the element or determine whether need to compile again. here is the url http://stackoverflow.com/questions/21315312/creating-a-new-directive-with-angularjs/, it's useful – Ron Smith May 07 '15 at 03:58
0

because I don't remove the directive so it makes the angular compile element over and over again. so i need to remove the directive or set a flat to determine if it is necessary to compile again.

function groupPopoverDirective($compile) {
    return {
        restrict: "A",
        scope:true,
        link: function ($scope, $element, $attrs) {

            $scope.groupPopoverData = $scope.$eval($attrs.groupPopoverData);

            if ($scope.groupPopoverData.isTrue) {
                if ($element.attr("ns-popover")) {
                    return;
                }

                $element.attr("ns-popover", "true");
                $element.attr("ns-popover-template", "popover-template.html");
                $element.attr("ns-popover-trigger", "mouseenter");
                $element.attr("ns-popover-placement", "right|top");
                $element.attr("ns-popover-mouse-relative", "x");
                $element.attr("ns-popover-theme", "ns-popover-tooltip-theme");
                $element.attr("ns-popover-timeout", "0.01");

                //element.removeAttr('popover-info');

                var linkFn = $compile($element);
                var pp = linkFn($scope);
            }
        }
    };
};
Ron Smith
  • 197
  • 3
  • 17