0

I have an Angularjs application with ruby-on-rails on the background. When I make a http get request and I get it back is fast and I have no trouble with it. The problem comes after I already got the data I wanted and ng-repeat takes a long time to render (or maybe it's not ng-repeat, I don't really know).

Some code and pictures down below:

My table with the ng-repeat (I have more td's, but even with only the id it is slow):

<md-table-container>
    <div ng-if="showSpinner" layout="row" layout-align="center start">
        <p></p>
        <md-progress-circular ng-if="showSpinner" md-diameter="70"></md-progress-circular>
    </div>

   <table md-table>
       <thead md-head>
          <tr md-row>
             <th md-column>Situação</th>
             <th md-column>Inclusão</th>
             <th md-column>Tipo de CTe</th>
             <th md-column>Número/Série</th>
             <th md-column class="min-width200">Emissor</th>
             <th md-column class="text-align-right">Valor</th>
             <th md-column>Emissão</th>
             <th md-column>
                 <md-icon md-font-set="md">done_all</md-icon>
                 <md-tooltip md-direction="top">Recebimento</md-tooltip>
             </th>
             <th md-column class="max-width5 text-align-center">
                <md-icon md-font-set="md">computer</md-icon>
                <md-tooltip md-direction="top">SEFAZ</md-tooltip>
             </th>
          </tr>
       </thead>
       <tbody>
          <tr ng-mouseleave="dataover = 0" ng-mouseover="dataover = document.id" ng-class="{'selected-row': idSelectedDocument === document.id, 'not-selected-row': idSelectedDocument != document.id, 'document_row_active': dataover === document.id, 'document_row': dataover != document.id}" md-row ng-repeat="document in documents track by document.id">
            <td>{{document.id}}</td>
            <!--Even with only the id it is slow-->

         </tr>
      </tbody>
   </table>
 </md-table-container>

 <md-table-pagination md-limit="itemsPerPage" md-page="currentPage" md-total="{{totalItems}}" md-label="{page: 'Página:', rowsPerPage: 'Filas por página:', of: 'de'}" md-on-paginate="selectPage(currentPage)" md-page-select></md-table-pagination>

My Http request:

$scope.promise = $http.get("/documents.json",
          { "params": {"company_id": company_id} }
    ).success(
        function(data,status,headers,config) {
          $scope.documents = data.data;
          $scope.totalItems = data.paging;
          console.log("HERE");
    }).error(
        function(data,status,headers,config) {
          toastr.error("Error.");
  });

As seen above in the JS I have a console.log(), even after the console.log is executed the ng-repeat is not ready yet and take another 2,3 seconds. It even freezes the application sometimes.

I installed Angularjs Batarang to debug some more, below is my screenshot, I don't think its affecting too much (getColumn is called from a component for my table angular md-data-table):

angularjs-batarang print

Edit 1: Turns out my tds inside the ng-repeat are making it slow, tried to remove one by one to see which one was it but I think it`s a combination of all of them, can you guys help me try to reduce the time it is taking?

<td md-cell>
      <div ng-mouseover="show_error_states($event, document)">
        <span ng-class="{'label-green': isApproved(document.current_state_id),
                          'label-red': isPending(document.current_state_id),
                          'label-yellow': isIncluded(document.current_state_id),
                          'label-orange': isDownloaded(document.current_state_id),
                          'label-dark-green': isLaunched(document.current_state_id),
                          'label-gray': isCancelled(document.current_state_id)}">{{document.current_state_title}}{{document.launched_by_erp ? '**' : ''}}</span>
        <md-tooltip md-direction="top">
          <span>{{document.current_state_view.description}}{{document.launched_by_erp ? ' **Lançado pelo ERP' : ''}}</span>
        </md-tooltip>
      </div>
    </td>

    <td md-cell ng-bind="document.received_date | date:'dd/MM/yyyy HH:mm'"></td>
    <td md-cell>{{document.dfe_number == null ? '----' : document.dfe_number + '/' + document.dfe_serie}}</td>
    <td md-cell>
      {{ document.emitter | limitTo: 35 }}{{document.emitter.length > 35 ? '...' : ''}}
      <md-tooltip md-direction="top">{{document.emitter + ' - ' + document.emitter_cnpj}}</md-tooltip>
    </td>
    <td md-cell class="text-align-right" ng-bind="document.dfe_value | currency: 'R$'"></td>
    <td md-cell ng-bind="document.dfe_dhemi | date: 'dd/MM/yyyy HH:mm'"></td>

    <td md-cell>
      <md-icon md-font-set="md">{{document.reception_state == "2" ? 'done' : 'clear'}}</md-icon>
      <md-tooltip md-direction="top">{{document.reception_state == "2" ? 'Recebido' : 'Pendente Recebimento'}}</md-tooltip>
    </td>

    <td md-cell class="text-align-center" ng-mouseover="find_dfe_situation(document)">
      <md-icon md-font-set="md">{{hasSuccessSefaz(document.dfe_situation) ? 'done' : ''}}{{hasErrorSefaz(document.dfe_situation) ? 'clear' : ''}}</md-icon>
      <md-tooltip ng-if="document.dfe_situation != null" md-direction="top">
        <div>
          <ul class="without-padding without-bullets">
            <li>
              <span>{{document.dfe_situation_description}}</span>
            </li>
            <li>
              <span ng-if="document.dfe_situation_date != null">{{' - ' + document.dfe_situation_date | date: 'dd/MM/yyyy HH:mm'}}</span>
            </li>
          </ul>
        </div>
      </md-tooltip>
    </td>


    <td md-cell class="text-align-center" ng-mouseover="find_manifestation(document)">
      <md-icon md-font-set="md">{{document.manifestation_state ? 'done' : 'clear'}}</md-icon>
      <md-tooltip ng-if="document.manifestation_state == true" md-direction="top">
        <div>
          <ul class="without-padding without-bullets">
            <li>
              <span>Descrição da Manifestação:</span>
              <span>{{document.manifestation_description}}{{document.manifestation_description == null ? 'Não Possui.' : ''}}</span>
            </li>
            <li>
              <span>Data da Manifestação:</span>
              <span>{{document.manifestation_date | date: 'dd/MM/yyyy HH:mm'}}{{document.manifestation_date == null ? 'Não Possui.' : ''}}</span>
            </li>
          </ul>
        </div>
      </md-tooltip>
    </td>

    <td md-cell>
      <md-button ng-click="openDanfe(document); setSelectedDocument(document.id)" ng-disabled="document.current_state_id == 17" class="md-raised min-width5">
        <md-icon md-font-set="md">find_in_page</md-icon>
        <md-tooltip md-direction="top">Danfe</md-tooltip>
      </md-button>

      <md-menu md-position-mode="target-right target" md-offset="45 35">
        <md-button class="min-width5" aria-label="Abrir menu" ng-click="$mdOpenMenu($event); open_documents_menu(document)">
          <div md-menu-origin>
            <md-icon class="color-blue" md-menu-origin md-font-set="md">settings</md-icon>
          </div>
        </md-button>
        <md-menu-content>
          <md-menu-item>
            <md-button ng-click="open_nfe_process(document); setSelectedDocument(document.id)" ng-disabled="document.current_state_id == 17 || document.current_state_id == 5 || document.launched_by_erp">
              Processo
            </md-button>
          </md-menu-item>
          <md-menu-item>
            <md-button ng-click="launch_now(document, $event); setSelectedDocument(document.id)" ng-disabled="document.current_state_id != 20">
              Lançar no ERP
            </md-button>
          </md-menu-item>
          <md-menu-item permission only="'admin'">
            <md-button ng-click="delete_now_erp(document, $event); setSelectedDocument(document.id)" ng-disabled="document.current_state_id != 21 || document.launched_by_erp">
              Excluir no ERP
            </md-button>
          </md-menu-item>
          <md-menu-item>
            <md-button ng-click="files(document, $event); setSelectedDocument(document.id)">
              Gerenciar Anexos
            </md-button>
          </md-menu-item>
          <md-menu-item>
            <md-button ng-click="manifest(document, $event); setSelectedDocument(document.id)">
              Manifestar
            </md-button>
          </md-menu-item>
          <md-menu-item>
            <md-button ng-click="events(document, $event);  setSelectedDocument(document.id)">
              Ocorrências
            </md-button>
          </md-menu-item>
          <md-menu-item>
            <md-button ng-click="nfe_events(document, $event); setSelectedDocument(document.id)">
              Eventos do Documento
            </md-button>
          </md-menu-item>
        </md-menu-content>
      </md-menu>
    </td>
14
  • How many items are you displaying in your repeater? Which browser are you using? Commented Sep 26, 2016 at 20:45
  • Hi @Soviut, 30 items, Chrome latest version... Commented Sep 26, 2016 at 20:46
  • @Soviut, what is the file size of the Icons you use in the table? Commented Sep 26, 2016 at 20:50
  • 30 items, sounds strange. What about creating 30 items in an array and iterate through that. Comment out all unesseary stuff to focus on the problem. The console.log should be executed first and after that the ng-repeat should start, or am I wrong. Commented Sep 26, 2016 at 20:51
  • Have you tried using "track by $index" instead of the id? Sometimes this can result in a better performance. Do you really need all the ng-class declarations? Commented Sep 26, 2016 at 20:52

2 Answers 2

1

One of the best performance improvements you can make for ng-repeats is to fill the array you're ng-repeating in chunks so that the table rows are rendered in batches instead of all at once. This allows the browser to do other things while rendering the table instead of focusing all of its energy on rendering the table.

In practice, you can do something like this in your success function (using $q to chain promises and Lodash to make the chunking much easier) and as documents are added to the documents array on the $scope they will be rendered to the DOM:

$scope.promise = $http.get("/documents.json",
      { "params": {"company_id": company_id} }
).success(
    function(data,status,headers,config) {
      let collection = data.data;
      // split the collection into smaller chunks; pick the size of each batch here
      let chunkedCollection = _.chunk(collection, size);
      let nextToRender;

      let promise = $q.resolve();

      function renderChunk(chunk) {
          // returning a $timeout ensures that each chunk is queued to be rendered seperately
          Array.prototype.push.apply($scope.documents, chunk);
          return $timeout(function(){}, 0);
      }

      _.forEach(chunkedCollection, function(chunk, index) {
          // we need to bind each chunk to the render function because of the $timeout
          nextToRender = renderChunk.bind(null, chunk);
          // $timeout returns a promise, so it's necessary to chain them like this
          promise = promise.then(nextToRender);
      });

      $scope.totalItems = data.paging;
      console.log("HERE");
}).error(
    function(data,status,headers,config) {
      toastr.error("Error.");
});
Sign up to request clarification or add additional context in comments.

Comments

0

A Common performance enhancement you can make here, is essentially move your code into a service, and then inject your service into your md-row to access your data. A Big reason why the performance is so slow here is because of the scope binding in your md-row directive. So..

  1. Get your data in a service, and then assign the value to the service.

  2. Create a md-table-content type directive, with no scope bindings, that has the service injected into it and then assigns the value of the service variable to the scope.

  3. Move the ng-repeat into the template of the md-table-content directive and create a $scope variable or function on md-table-content that would evaluate that giant ng-class logic quicker. Or better yet try to fold up some of these classes into the simple logic of if id = document.id -> apply active else apply non-active or something along those lines

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.