Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Detached DOMs in nested ng-Repeats #9573

Closed
sundarns opened this issue Oct 12, 2014 · 1 comment
Closed

Detached DOMs in nested ng-Repeats #9573

sundarns opened this issue Oct 12, 2014 · 1 comment

Comments

@sundarns
Copy link

I have a complex table to be generated that has pagination and sorting capabilities. I have created a customTable directive. I am still running on AngularJS 1.0.7 for compatibility purposes. This code is a part of a Cordova application. I am testing on KitKat 4.4.4 (that uses Chrome WebView). I am finding a lot of DOM Nodes that are leaking. I have seen other issues (#2624, #1313) that talk about this as a V8 issue. Can you kindly point me in the right direction?

Directive Code is as below:

appDirectives.directive('customTable', function() {
    return {
        restrict : 'A',
        replace : true,
        templateUrl : 'templates/tableDirective.html',
        scope : {
            ngshow : '=',
            caption : '@',
            oddRowClass : '@',
            evenRowClass : '@',
            headerNeed : '=',
            multipleHeader : '=',
            subHeaderNeed : '=',
            sortable : '=',
            pageSize : '=',
            paginator : '=',
            ngModel : '=',
            tableName : '@',
            tableClass : '@'
        },
        link : function(scope, elem, attrs) {
            scope.headerOrder = "order";
            scope.subHeaderOrder = "order";
            scope.subHeaderPredicate = '';
            scope.subHeaderReverse = false;
            scope.headerPredicate = '';
            scope.headerReverse = false;
            scope.subHeaderPaginatorPredicate = '';
            scope.subHeaderPaginatorReverse = false;
            scope.headerPaginatorPredicate = '';
            scope.headerPaginatorReverse = false;
            scope.currentPageNumber = 0;
            scope.paginatorColSpan
            scope.getValuesOrdered = function(key, clickedValue, e) {
                if (clickedValue == 'subHeader') {
                    if (scope.paginator) {
                        scope.subHeaderPaginatorPredicate = key;
                        scope.subHeaderPaginatorReverse = !(scope.subHeaderPaginatorReverse);
                    } else {
                        scope.subHeaderPredicate = key;
                        scope.subHeaderReverse = !(scope.subHeaderReverse);
                    }
                } else if (clickedValue == 'header') {
                    if (scope.paginator) {
                        scope.headerPaginatorPredicate = key;
                        scope.headerPaginatorReverse = !(scope.headerPaginatorReverse);
                    } else {
                        scope.headerPredicate = key;
                        scope.headerReverse = !(scope.headerReverse);
                    }
                }

                if ($(e.target).hasClass("sorter_arrow_dwn")) {
                    $(e.target).toggleClass("sorter_arrow_dwn sorter_arrow_up");
                } else {
                    $(e.target).toggleClass("sorter_arrow_up sorter_arrow_dwn");
                }
            }

            scope.getValue = function(row, key) {
                return row[key.map];
            }
            scope.checkDecimal = function(number) {
                var convertedNumber;
                if (number % 1 != 0) {
                    convertedNumber = Math.round(number * 100) / 100;
                }
                if (isNaN(convertedNumber))
                    convertedNumber = number;
                return convertedNumber;
            }
            scope.clickedPage = function(pageNumber) {
                scope.currentPageNumber = (pageNumber - 1);
            }

            scope.tableValueRows = "";
            eval("scope.tableValueRows = " + attrs.tableValues.split("'").join('"').split("ngModel").join("scope.ngModel"));

            if (attrs.headerColumns != "") {
                eval("scope.headerColumns = " + attrs.headerColumns.split("'").join('"'));
            }
            if (attrs.subHeaderColumns != "") {
                eval("scope.subHeaderColumns = " + attrs.subHeaderColumns.split("'").join('"'));
            }
            if (scope.subHeaderColumns != undefined) {
                scope.paginatorColSpan = scope.subHeaderColumns.length;
            } else if (scope.headerColumns != undefined) {
                scope.paginatorColSpan = scope.headerColumns.length;
            }
            scope.pageList = [];
            var noOfPages = scope.tableValueRows.length / scope.pageSize;
            for (var i = 0; i < noOfPages; i++) {
                scope.pageList[i] = (i + 1);
            }
        }
    }
});

The directive template is as below:

<div class="detailed_view_table_container" ng-show="ngshow">
<span class="illustrator_details_table_title" translate="{{caption}}"></span>
<div class="illustrator_table_outer_container">
<div class="illustrator_table_inner_container">
  <table id="{{tableName}}" class="illustrator_details_table {{tableClass}}">
      <thead ng-if="headerNeed && !multipleHeader">
          <tr ng-if="subHeaderNeed" class="illustrator_table_head">
              <th bind-once ng:repeat="headerColumn in headerColumns" colspan="{{headerColumn.colspan}}" translate="{{headerColumn.translateid}}"></th>
          </tr>
     </thead>
      <thead ng-if="headerNeed && multipleHeader">
          <tr ng-if="subHeaderNeed" ng:repeat="headerColumn in headerColumns" class="illustrator_table_head">
              <th bind-once ng:repeat="header in headerColumn.row" colspan="{{header.colspan}}" translate="{{header.translateid}}"></th>
          </tr>
     </thead>
    <tbody>
        <tr ng-if="subHeaderNeed && sortable" class="illustrator_table_row illustrator_table_first_row row_even">
            <td bind-once ng:repeat="subHeaderColumn in subHeaderColumns | orderBy:subHeaderOrder"><a href="" ng-click="getValuesOrdered(subHeaderColumn.map,'subHeader')" translate="{{subHeaderColumn.translateid}}"></a></td>
        </tr>
        <tr ng-if="subHeaderNeed && !(sortable)" class="illustrator_table_row illustrator_table_first_row row_even">
            <td bind-once ng:repeat="subHeaderColumn in subHeaderColumns | orderBy:subHeaderOrder">
                <a class="table_sort_arrow sorter sorter_arrow_dwn" ng-if="(subHeaderColumn.columnSortable)" href="" ng-click="getValuesOrdered(subHeaderColumn.map,'subHeader', $event)" translate="{{subHeaderColumn.translateid}}"></a>
                <span ng-if="!(subHeaderColumn.columnSortable)" translate="{{subHeaderColumn.translateid}}"></span>
            </td>
        </tr>

        <tr ng-if="!(paginator) && subHeaderNeed" ng:repeat="tableValueRow in tableValueRows | orderBy:subHeaderPredicate:subHeaderReverse" ng-class="{illustrator_content_row_odd : $index%2==0, illustrator_content_row_even : !($index%2==0)}">
            <td bind-once ng:repeat="subHeaderColumn in subHeaderColumns | orderBy:subHeaderOrder"  ng-bind="checkDecimal(tableValueRow[subHeaderColumn.map])"></td>
        </tr>

        <tr ng-if="paginator && subHeaderNeed" ng:repeat="tableValueRow in tableValueRows | orderBy:subHeaderPaginatorPredicate:subHeaderPaginatorReverse | paginate:currentPageNumber:pageSize" ng-class="{illustrator_table_row_odd : $index%2==0, illustrator_table_row_even : !($index%2==0)}">
             <td bind-once ng:repeat="subHeaderColumn in subHeaderColumns | orderBy:subHeaderOrder" ng-bind="checkDecimal(tableValueRow[subHeaderColumn.map])"></td>             
        </tr>

        <tr ng-if="!(paginator) && (headerNeed && !(subHeaderNeed))" class="illustrator_table_row_odd">
            <td bind-once align="center" valign="middle" class={{headerColumn.class}} colspan="{{headerColumn.colspan}}" ng:repeat="headerColumn in headerColumns" translate="{{headerColumn.translateid}}"></td>            
        </tr>

        <tr ng-if="!(paginator) && (headerNeed && !(subHeaderNeed))" ng:repeat="tableValueRow in tableValueRows | orderBy:headerPredicate:headerReverse" ng-class="{illustrator_table_row_odd : $index%2==0, illustrator_table_row_even : !($index%2==0)}">
            <td bind-once ng:repeat="tableValueColumn in tableValueRow" translate="[[checkDecimal(tableValueColumn)]]"></td>                 
        </tr>

        <tr ng-if="!(subHeaderNeed) && !(sortable)" ng:repeat="tableValueRow in tableValueRows" ng-class-odd="'illustrator_table_row_odd'" ng-class-even="'illustrator_table_row_even'">
            <td bind-once ng:repeat="tableValueColumn in tableValueRow" translate="{{tableValueColumn}}"></td>          
        </tr>

  </tbody>
</table>
</div>
</div>

Kindly excuse me for pasting big code chunks.

Please find below the Chrome Dev Tools Profile. I did 3 snapshots and compared them.

image

@btford
Copy link
Contributor

btford commented Oct 13, 2014

We are no longer fixing issues with the 1.0.x branch. If this is still an issue in the latest 1.2.x branch, please let me know and we can re-open this.

@btford btford closed this as completed Oct 13, 2014
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants