【问题标题】:Angular doesn't update view when array was changed更改数组时Angular不会更新视图
【发布时间】:2016-12-16 09:18:52
【问题描述】:

我从服务更新了我的数组,但视图没有更新。

当我打电话给$scope.$apply() 或$scope.$digest 时,它会记录下来

$digest 已经在进行中

这是我的 main.html 文件。 ctrl.flightsTo 是我提到的数组

<md-progress-linear class="md-accent" ng-hide="ctrl.isFlightDataLoaded" mode="indeterminate"></md-progress-linear>
<div class="md-padding">
  <md-autocomplete
      ng-show="ctrl.isFlightDataLoaded"
      md-selected-item="ctrl.selectedItem"
      md-selected-item-change="ctrl.selectedItemChange(item)"
      md-search-text="ctrl.searchText"
      md-items="item in ctrl.querySearch(searchText)"
      md-item-text="item.name"
      md-min-length="0"
      placeholder="Choose a destination">
    <md-item-template>
      <span md-highlight-text="ctrl.searchText">{{item.name}} - {{item.iata}}</span>
    </md-item-template>
  </md-autocomplete>
  <ul>
    <li ng-repeat="flight in ctrl.flightsTo">{{flight}}</li>
  </ul>
</div>

这是我的 MainCtrl。

当调用md-selected-item-change 事件时,我在selectedItemChange 函数中从flightDataService 获取我的航班数据。

但 Angular 不会更新视图。

.controller('MainCtrl', function(flightDataService) {
    var self = this;
    self.isFlightDataLoaded = false;
    self.destinations = [];
    self.querySearch = querySearch;
    self.flightsTo = [];
    self.selectedItemChange = selectedItemChange;
    self.init = init;

    function querySearch(query) {
      var results = query ? self.destinations.filter(createFilterFor(query)) : self.destinations;
      return results;
    };

    function createFilterFor(query) {
      var lowercaseQuery = angular.lowercase(query);

      return function filterFn(destination) {
        return angular.lowercase(destination.iata).indexOf(lowercaseQuery) === 0;
      }
    };

    function selectedItemChange(item) {
      if (typeof item !== 'undefined') {
        self.flightsTo = flightDataService.getFlightsTo(item.iata);
      }
    };

    function init() {
      flightDataService.loadAll().then(function() {
        self.destinations = flightDataService.getAirports();
        self.isFlightDataLoaded = true;
      }).catch(function(error) {
        console.log(error);
      });
    };

    self.init();
  });

最后,我附加了我的 flightDataService。

它只是返回保存的数据。

.factory('flightDataService', function($http, $q, papa) {
    var FLIGHT_DATA_URL = 'https://s3-ap-southeast-2.amazonaws.com/glow-dev-assets/flight-data-gz.csv';

    var self = this;
    self.flights = [];
    self.airports = [];
    self.routes = [];
    self.loadAll = loadAll;
    self.getAirports = getAirports;
    self.getFlightsTo = getFlightsTo;

    function loadAll() {
      var deferred = $q.defer();

      loadAirportData().then(function(airports) {
        loadFlightData().then(function(flights) {
          processFlightData(flights, airports);
          deferred.resolve();
        }).catch(function(error) {
          deferred.reject(error);
        });
      }).catch(function(error) {
        deferred.reject(error);
      });
      return deferred.promise;
    };

    function loadAirportData() {
      var deferred = $q.defer();
      $http({
        method: 'GET',
        url: '/assets/airports.json'
      }).then(function(response) {
        deferred.resolve(response.data);
      }, function(error) {
        deferred.reject(error);
      });
      return deferred.promise;
    };

    function loadFlightData() {
      var deferred = $q.defer();
      $http({
        method: 'GET',
        url: FLIGHT_DATA_URL
      }).then(function(response) {
        deferred.resolve(response.data);
      }, function(error) {
        deferred.reject(error);
      });
      return deferred.promise;
    };

    function processFlightData(data, airports) {
      self.flights = papa.parse(data, { header: true }).data;

      var dests = self.flights.map(function(flight) { return flight.destination });
      var destSet = new Set(dests);
      destSet.forEach(function(dest) {
        var airport = airports.find(function(airport) {
          return airport.iata === dest
        });

        if (typeof airport !== 'undefined') {
          self.airports.push(airport);
        }
      });

      self.flights.forEach(function(flight) {
        var origin = flight.origin;
        var dest = flight.destination;

        if (typeof self.routes[dest] === 'undefined') {
          self.routes[dest] = [];
        }
        if (typeof self.routes[dest][origin] === 'undefined') {
          self.routes[dest][origin] = {
            date: flight.date,
            delay: flight.delay,
            distance: flight.distance
          };
        }
      });

      return self.flights;
    };

    function getAirports() {
      return self.airports;
    };

    function getFlightsTo(destination) {
      return self.routes[destination];
    };

    return self;
  });

当我在控制台上登录ctrl.flightsTo。

它总是改变得很好。

但视图与空数组保持相同的状态。

【问题讨论】:

  • 使用$scope 而不是var self = this;
  • @azad 我已经使用$scope 而不是self。但它不起作用。
  • 你是在self.flightsTo = flightDataService.getFlightsTo(item.iata);这行之后直接调用$scope.apply()吗?
  • 我看到你正在使用 http 调用,这里的答案可能会帮助你 stackoverflow.com/a/15476728/5272567 可能需要在 http 调用之后直接执行 $scope.apply()
  • @Matthias 我确实在self.flightsTo = flightDataService.getFlightsTo(item.iata); 之后打电话给$scope.$apply() 但它会犯像$digest already in progress 这样的错误

标签: javascript angularjs arrays


【解决方案1】:

终于找到了!

因为我的 flightsTo 变量是关联数组,Angular 不会更新它。

所以我把它改成了普通数组。

另外,IAmDranged 的​​意见也是对的。

谢谢大家!

【讨论】:

    【解决方案2】:

    这与其说是一个答案,不如说是当我发现自己处于类似情况时有时会使用的一个技巧:注入一个 $timeout 并使用它在 $http 调用的上下文之外进行最终更新:

    $timeout(function () {
        processFlightData(flights, airports);
        deferred.resolve();
    }
    

    显然,这并不能解决问题或解释问题发生的原因,但如果您只是需要让您的应用正常运行,这可能是一种实用的解决方法。

    【讨论】:

      【解决方案3】:

      试试这个:当您使用简单的 javascript 函数时,角度不会将更改应用于此函数内的变量。

      function selectedItemChange(item) {
        if (typeof item !== 'undefined') {
          $scope.flightsTo = flightDataService.getFlightsTo(item.iata);
          $scope.$$phase || $scope.$apply();
        }
      };
      

      或者您也可以通过将函数放入 $scope 变量中来使用函数表达式,例如

       $scope.functionName = function () {}
      

      【讨论】:

        【解决方案4】:

        我最初删除了这条评论,因为我有一段时间没有做过 angular 并且对控制器的语法感到困惑,但实际上它在某些方面可能仍然有用。

        ngRepeat 使用 $watchCollection 检测数组中的更改 - 这将跟踪数组中单个值的更改。这就是 Angular 文档所说的。

        尝试进一步看,实际上听起来像 $watchCollection 可能在数组的副本上工作 - 实际上只是对数组的引用的副本 - 就像最初初始化的那样由应用程序。

        在您的情况下,您正在分配给您的数组 - 因此您更改它指向的引用 - 每次更新它时。但是 $watchCollection 仍然只监视它所引用的原始数组中的值变化。

        您可能需要设置一个额外的监视来查看对您的阵列的参考更改。

        这与其说是实际答案,不如说是评论,但这太长了,无法进入评论部分。希望这会有所帮助。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-05-05
          • 1970-01-01
          • 2019-01-26
          • 1970-01-01
          • 2021-05-21
          • 2020-03-03
          • 2021-06-29
          相关资源
          最近更新 更多