Tengo una pequeña duda sobre ng-init, de hecho tengo algunos gráficos.

  1. Línea de tendencia en la parte superior
  2. Gráficos circulares a continuación.
  3. Tabla HTML a continuación.

En la carga de la página no estoy mostrando nada, pero una vez que hago clic en un botón en particular por primera vez, todos mis gráficos y tablas se muestran para los datos recibidos por la solicitud http. pero luego, si hago clic en el otro botón y ejecuto la misma solicitud http, entonces se actualiza la línea de tendencia y la tabla, pero no los gráficos circulares. Mis gráficos circulares se representan dentro del div de abajo con ng-init, y cuando estoy probando ese método ng-init = "myFunction (index)" no se actualiza en el siguiente clic.

<form>
  <md-input-container>
    <label for="myInput">Search Division</label>
    <md-icon md-svg-icon="~/Content/myIcons/searchicon/ic_search_black_36px.svg"></md-icon>
    <input type="text" id="myInput" ng-model="searchDiv" md-autofocus>
  </md-input-container>
  <md-content ng-repeat="prod in listofProd | filter: searchDiv">
    <md-button class="md-whiteframe-1dp card" ng-click="generateChart(prod)" flex-sm="100" flex-gt-sm="100" flex-gt-md="100">
    {{prod}}
    </md-button>
  </md-content>
</form>

<div layout="row" layout-wrap>
  <div flex="25" style="width:1000px;height:300px; border: 1px solid skyblue;" ng-repeat="year in years track by $index" id="piechart{{$index}}" ng-init="myFunction($index)">
  </div>
</div>

Y este es mi código angularjs para la función ng-init.

(function () {
  'use strict'
  angular.module("GAiiNSApp").controller("ProdPerDash", ['$http', '$scope', '$log', function ($http, $scope, $log) {
    $scope.isLoading = false;
    $scope.hideme = true;
    var currentdate = new Date();
    var startdd = "01";
    var startmonth = "00";
    var lastyear = currentdate.getFullYear() - 3;
    var enddd = "31";
    var endmonth = "11";
    var endyear = currentdate.getFullYear();
    $scope.StartDate = new Date(lastyear, startmonth, startdd);
    $scope.EndDate = new Date(endyear, endmonth, enddd);
    $scope.Region = "Local";
    $scope.Country = "";
    $scope.ProductLineSelected = false;
    $scope.ButtonText = "CUSTOMIZE";

    $scope.clicked = false;
    $scope.Yes = function () {
      if (!$scope.clicked == true) {
        $scope.ButtonText = "CANCEL";
        $scope.clicked = true;
      }
      else {
        $scope.ButtonText = "CUSTOMIZE";
        $scope.clicked = false;
      }
    }
    $scope.alert = function (message) {
      $window.alert(message);
    }

    $scope.Regions = ["Export", "Local"];
    $http.get('/General/API/GetProductDivision').then(function (res) {
      $scope.listofProd = res.data;
    });
    $scope.selectedProduct = "";
    $scope.generateChart = function (prodname) {
      $scope.selectedProduct = prodname;
      $scope.isLoading = true;
      $http.get('/Marketing/MarketingAPI/ProdPerformance', {
        params: {
          StartDate: $scope.StartDate,
          EndDate: $scope.EndDate,
          ProductDivision: prodname,
          Division: $scope.Region,
          Area: $scope.Country
        }
      }).then(function (res) {
        var resArray = res.data;
        var trendarray = [];
        $scope.TableRecords = resArray;
        $scope.hideme = false;
        var json = resArray;
        // console.log("Array of Objects: " + JSON.stringify($scope.TableRecords));
        var groupedData = {};
        var result = [];
        resArray.forEach(function (item) {
          var year = item.SecuredYear;
          var value = item.ValueInDhs;
          if (groupedData.hasOwnProperty(year)) {
            groupedData[year] += value;
          } else {
            groupedData[year] = value;
          }
        });
        //Pie chart data starts here
        var years = [];
        json.forEach(function (obj) {
          if (years.indexOf(obj.SecuredYear) == -1)
            years.push(obj.SecuredYear);
        });
        //$scope.years = years;
        $scope.years = angular.copy(years);
        $scope.pichartsview = true;
        $scope.myFunction = function (index) {

          var data = [['Product', 'ValueInDhs']];
          json.forEach(function (obj) {
            if (obj.SecuredYear == years[index]) {
              data.push([String(obj.GroupName).trim(), obj.ValueInDhs]);
            }
          });
          $scope.displayChart(data, index);
        }
        $scope.displayChart = function (dataForChart, index) {
          var chartData = dataForChart;
          google.charts.load('current', { 'packages': ['corechart'] });
          google.charts.setOnLoadCallback(drawChartPie);
          function drawChartPie() {
            var data = google.visualization.arrayToDataTable(chartData);
            var options = {
              chartArea: { left: 10, top: 20, width: "80%", height: "80%" },
              legend: 'bottom',
              is3D: true,
              pieSliceText: 'percentage',
              pieSliceTextStyle: {
                fontSize: 8
              },
              title: $scope.selectedProduct + " - " + $scope.years[index]
            };
            var chart = new google.visualization.PieChart(document.getElementById('piechart' + index));
            $scope.$apply(function () {
              chart.draw(data, options);
            });
          }
        }
        //Pie chart data ends here
        for (var year in groupedData) {
          var tmp = {};
          tmp[year] = groupedData[year];
          result.push(tmp);
        }
        result.forEach(function (obj, index) {
          var key = Object.keys(obj)[0];
          trendarray.push([parseInt(key, 10), obj[key]]);
        });
        trendarray.splice(0, 0, [{ label: 'SecuredYear', type: 'number' }, { label: 'ValueInDhs', type: 'number' }]);
        google.charts.load('current', { 'packages': ['corechart'] });
        google.charts.setOnLoadCallback(drawChart);
        $scope.isLoading = false;
          function drawChart() {
            var data = google.visualization.arrayToDataTable(
              trendarray
              );
            var options = {
              legend: 'none',
              title: 'Product Performance Trendline - ' + $scope.selectedProduct + " - " + $scope.Region,
              hAxis: { title: 'Secured Year', format: '0' },
              vAxis: { title: 'Secured Value in DHS' },
              trendlines: {
                0: {
                  lineWidth: 5,
                  type: 'polynomial',
                  visibleInLegend: true,
                  color: 'green',
                }
              }
            };
            var chart = new google.visualization.ScatterChart(document.getElementById('chart_div2'));
            chart.draw(data, options);
          }
        })
    $scope.ProductLineSelected = true;
    };
  }]); //this is ctrl closing
})(); //this is function closing
3
Saleem 8 dic. 2016 a las 14:32
¿Cómo se vinculan los datos de ng-repeat years, una vez o siempre? ¿Puedes publicar el código del evento onclick mencionado anteriormente?
 – 
Aruna
8 dic. 2016 a las 15:28
Cada vez que quiero vincular estos años, y los años se vinculan correctamente, es decir, cada vez que hago clic en el botón, se llama http y se actualizan los años, pero después de eso, myFunction no se está ejecutando. Acabo de actualizar el código anterior con la funcionalidad de año.
 – 
Saleem
8 dic. 2016 a las 15:33
Cuando asigne $scope.years = years;, simplemente cámbielo a $scope.years = angular.copy(years);. Solo intente esto y actualizaré la respuesta en breve con el motivo de esto.
 – 
Aruna
8 dic. 2016 a las 15:39
Está bien. Déjame cambiar y comprobar.
 – 
Saleem
8 dic. 2016 a las 15:48
Acabo de actualizar, pero no cambia los datos de los gráficos circulares.
 – 
Saleem
8 dic. 2016 a las 15:49

2 respuestas

Desde Angular 1.2, tiene una opción track by para evitar que el repetidor de volver a renderizar todos los elementos para mejorar el rendimiento.

Puede encontrar más información en: http://www.codelord.net/2014/04/15/improving-ng-repeat-performance-with-track-by/

Es por eso que ng-init no se activa de nuevo para el mismo years renderizado ya que ha usado el track by aquí ng-repeat="year in years track by $index".

Incluso si elimina esto, el ng-init nuevamente no se dispara por segunda vez ya que years aquí está la matriz donde Angular es lo suficientemente brillante como para rastrear por su valor aunque no hay un {track by.

Por lo tanto, tenemos que cambiar years de una matriz simple a object array como, [{year: obj.SecuredYear}] para permitir que Angular genere un $$hashKey que no es idéntico cuando el objeto cambia cada vez y Angular redibujará el repetidor disparando ng-init.

Entonces, los cambios relacionados con esto son,

HTML

<div flex="25" style="width:1000px;height:300px; border: 1px solid skyblue;" 
  ng-repeat="year in years" id="piechart{{$index}}" ng-init="myFunction($index)">
</div>

JS (rellenando years)

    var years = [];
    json.forEach(function (obj) {
      var exists = years.filter(function(y) {
        return y.year === obj.SecuredYear;
      });

      if (exists.length === 0) { // This will replace the `indexOf` check
        years.push({year: obj.SecuredYear});  // change here
      }
    });
    $scope.years = years;

JS (myFunction ())

      $scope.myFunction = function (index) {
      var data = [['Product', 'ValueInDhs']];
      json.forEach(function (obj) {
        if (obj.SecuredYear == years[index].year) {   // change here at years[index].year
          data.push([String(obj.GroupName).trim(), obj.ValueInDhs]);
        }
      });
      $scope.displayChart(data, index);
    };

Acabo de crear un fragmento de muestra como se muestra a continuación para verificar esto con un console.log(years[index].year); dentro del $scope.myFunction() que se registra correctamente con ng-init, cada vez que el botón Generate Chart se hace clic.

angular.module('myApp', []);

function MyCtrl($scope) {
    var json = [{SecuredYear: 1965},{SecuredYear: 1988}, {SecuredYear: 2016}, {SecuredYear: 2012}];
    $scope.generateChart = function(item) {
      
        var years = [];
        json.forEach(function (obj) {
          var exists = years.filter(function(y) {
            return y.year === obj.SecuredYear;
          });
          
          if (exists.length === 0) {
            years.push({year: obj.SecuredYear});
          }
        });
        $scope.years = years;
      
        
        $scope.myFunction = function (index) {
          var data = [['Product', 'ValueInDhs']];
          json.forEach(function (obj) {
            if (obj.SecuredYear == years[index].year) {
              //data.push([String(obj.GroupName).trim(), obj.ValueInDhs]);
              console.log(years[index].year);
            }
          });
          //$scope.displayChart(data, index);
        };
    };
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
    <div ng-controller="MyCtrl">
        <button ng-click="generateChart()">Generate Chart</button><br/>
        <div style="border: 1px solid skyblue;" ng-repeat="year in years" id="piechart{{$index}}" ng-init="myFunction($index)" >{{year.year}}
        </div>
    </div>
</div>
1
Aruna 10 dic. 2016 a las 18:02
Aruna , de repente empezó a dar un error en otra parte de la función del gráfico de dibujo. El error es Error: $ rootScope: inprog . Los gráficos se representan correctamente por primera vez, pero una vez que hago clic por segunda vez en el botón, aparece el error anterior.
 – 
Saleem
12 dic. 2016 a las 07:55
¿Puedes darme el error completo y habrá una URL angular con el error que también puedes darme? Echaré un vistazo.
 – 
Aruna
12 dic. 2016 a las 08:05
Aruna , este es el enlace del error. docs.angularjs.org/error/$rootScope/inprog? p0 = $ digest el error viene dentro de la función $ scope.displayChart = function (dataForChart, index) {. puede tener TeamViewer si lo desea.
 – 
Saleem
12 dic. 2016 a las 08:07
Bien, esto se debe a esta línea dentro de la función $scope.displayChart, $scope.$apply(function () { chart.draw(data, options); });. Puede eliminar el contenedor $scope.$apply(function () { }) y puede cambiar esta línea simplemente a chart.draw(data, options);.
 – 
Aruna
12 dic. 2016 a las 08:09
Así que cambie de $scope.$apply(function () { chart.draw(data, options); }); a chart.draw(data, options);.
 – 
Aruna
12 dic. 2016 a las 08:11
angular.module('app', [
    'googlechart'
  ])
  .controller('appCtrl', function() {
    let vm = this;
    vm.charts = [];

    vm.addChart = function() {
      vm.charts.push({
        type: "PieChart",
        data: {
          "cols": [{
            id: "t",
            label: "Topping",
            type: "string"
          }, {
            id: "s",
            label: "Slices",
            type: "number"
          }],
          "rows": [{
            c: [{
              v: "Mushrooms"
            }, {
              v: 3
            }, ]
          }, {
            c: [{
              v: vm.input.name
            }, {
              v: vm.input.quantity
            }]
          }, {
            c: [{
              v: "Olives"
            }, {
              v: 31
            }]
          }, {
            c: [{
              v: "Zucchini"
            }, {
              v: 1
            }, ]
          }, {
            c: [{
              v: "Pepperoni"
            }, {
              v: 2
            }, ]
          }]
        }
      });
    };
    return vm;
  });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.9/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-google-chart/0.1.0/ng-google-chart.min.js" type="text/javascript"></script>
<div ng-app="app" ng-controller="appCtrl as vm">
  <div ng-repeat="chart in vm.charts track by $index">
    <div google-chart chart="chart" style="height:150px; width:100%;"></div>
  </div>
  <br>
  <input type="text" required ng-model="vm.input.name" placeholder="Enter name">
  <input type="number" required ng-model="vm.input.quantity" placeholder="Enter quantity">
  <button ng-click="vm.addChart()">Add chart</button>
</div>

He notado que estás usando gráficos de Google. Su problema se deriva del uso de una biblioteca no compatible (los gráficos de Google desconocen los cambios en el modelo, angular en ese caso).

Podrías escribir tu propia directiva, pero ¿por qué hacerlo si alguien más ya hizo el trabajo pesado? En su lugar, le sugiero que considere utilizar la siguiente directiva.

Aquí hay un ejemplo de en vivo.

Editar: ng-init no funciona de la forma deseada. ng-init simplemente activa una función cuando se carga el controlador , no cuando se está renderizando el elemento.

0
Muli Yulzary 15 ene. 2017 a las 20:44
No tengo idea sobre cómo usar los gráficos de anguarjs, ¿puede mostrarme un ejemplo usando mi código :)
 – 
Saleem
15 ene. 2017 a las 19:14
Hay un sitio completo dedicado a ejemplos. utilice los enlaces que proporcioné.
 – 
Muli Yulzary
15 ene. 2017 a las 19:20
Muli , como puede ver, estoy tratando de mostrar gráficos circulares en ng-repeat, con gráficos angulares que no entienden cómo lograr lo mismo. agradecería si pudiera ayudarme aquí.
 – 
Saleem
15 ene. 2017 a las 19:36
Está bien. Entonces, ¿cómo renderizo varios gráficos circulares de forma dinámica?
 – 
Saleem
15 ene. 2017 a las 19:50
Publicaré un ejemplo mínimo. tenga en cuenta que esto será mínimo y tendrá que aprender a utilizar la directiva.
 – 
Muli Yulzary
15 ene. 2017 a las 19:51