【问题标题】:ng-if not updating when $scope var changes in AngularJSng-if 在 AngularJS 中的 $scope var 更改时不更新
【发布时间】:2017-06-15 12:34:20
【问题描述】:

在我看来,我有这个:

<div class="tab-loading-container" ng-if="mapStatus.loading =='true'">
    <div class="tab-loading">Loading map</div>
</div>

在我的控制器中,我有:

$scope.mapStatus = {};

然后是在满足某些条件(例如离线等)时更新ng-if 使用的范围变量的各种函数(例如:

function enableMap () {
    $scope.mapStatus.loading = false;
}

我的问题是,尽管范围 var 已正确更改(通过良好的 'ol console.log 和 angular chrome 扩展确认),但视图中的 ng-if 永远不会更新/添加/删除以显示/删除 div。

我尝试过使用 $apply(虽然我对它的理解不是很好),例如:

function enableMap () {
    $scope.$apply(function() {
        $scope.mapStatus.loading = false;
    });
}

但这会引发诸如Error: [$rootScope:inprog] $apply already in progress之类的错误

感觉好像我错过了一些明显的东西:(


根据要求提供更多代码:

angular.module('app.case.controller', [])
.controller('CaseController', function($rootScope, $scope, $state, $stateParams, $filter, casesFactory, $ionicActionSheet, $ionicModal, $ionicTabsDelegate, $ionicLoading, ConnectivityMonitor) {

/// Other non related code

// location map - refactor into a factory
    $scope.mapStatus = {};

    function initMap () {
        var pos = { 'lat':  52.6136149, 'lng': -1.1936672 };

        var latLng = new google.maps.LatLng(pos.lat, pos.lng);

        var mapOptions = {
            center: latLng,
            zoom: 15,
            mapTypeId: google.maps.MapTypeId.ROADMAP,
            fullscreenControl: false,
            mapTypeControl: false
        };

        $scope.map = new google.maps.Map(document.getElementById('map'), mapOptions);

        google.maps.event.trigger(map, 'resize');

        //Wait until the map is loaded
        google.maps.event.addListenerOnce($scope.map, 'idle', function () {

            enableMap();

            console.log('map loaded');

            var marker = new google.maps.Marker({
                map: $scope.map,
                animation: google.maps.Animation.DROP,
                position: latLng
            });

            google.maps.event.trigger(map, 'resize'); 

            $scope.map.setCenter(latLng);

        });
    }

    function loadGoogleMaps () {    
        $scope.mapStatus.loading = true;

        // This function will be called once the SDK has been loaded
        window.mapInit = function(){
            initMap();
        };

        // Create a script element to insert into the page
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.id = 'googleMaps';
        script.src = 'https://maps.googleapis.com/maps/api/js?key=XXX&callback=mapInit';

        document.body.appendChild(script);
    }

    function enableMap () {
        $scope.mapStatus.loading = false;
        $scope.mapStatus.offline = false;
    }

    function disableMap () {
        $scope.mapStatus.offline = true;
    }

    function checkLoaded () {
        if (typeof google == 'undefined"' || typeof google.maps == 'undefined') {
            loadGoogleMaps();
        } else {
            enableMap();
        }       
    }

    function addConnectivityListeners () {

        if (ionic.Platform.isWebView()) {

            // Check if the map is already loaded when the user comes online, if not, load it
            $rootScope.$on('$cordovaNetwork:online', function(event, networkState) {
                checkLoaded();
            });

            // Disable the map when the user goes offline
            $rootScope.$on('$cordovaNetwork:offline', function(event, networkState) {
                disableMap();
            });

        } else {

            //Same as above but for when we are not running on a device
            window.addEventListener("online", function(e) {
                checkLoaded();
            }, false);    

            window.addEventListener("offline", function(e) {
                disableMap();
            }, false);  
        }
    }

    function showMap () {

        console.log('showMap() called');

        if (typeof google == 'undefined' || typeof google.maps == 'undefined') {

            console.warn("Google Maps SDK needs to be loaded");

            disableMap();

            if (ConnectivityMonitor.isOnline()){
                loadGoogleMaps();
            }
        }
        else {
            if (ConnectivityMonitor.isOnline()){
                initMap();
                enableMap();
            } else {
                disableMap();
            }
        }

        addConnectivityListeners();
    }

    $scope.initMap = function () {
        showMap();
    };

为了确认范围变量正在被更改,这里是来自 AngularJS chrome 扩展的屏幕截图:

【问题讨论】:

  • 你为什么要把 boolean 值和 string 'true' 比较,直接写成ng-if="mapStatus.loading"
  • 哎呀,好地方。但是,在更改为mapStatus.loading 后,即使控制器中的 var 设置为 false(当满足某些条件时),div 也会始终显示。
  • 我认为你应该使用ng-show 而不是ng-if
  • 使用ng-if="$parent.mapStatus.loading"或ng-show
  • 值在哪里变为false?你能发布这个代码吗?

标签: javascript angularjs


【解决方案1】:

我已对其进行了更改,因此它只是一个布尔值,而不是将其与字符串进行比较。 您是否曾经将$scope.mapStatus.loading 设置为true?否则,它永远不会显示,因为它总是错误的。

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

function MyCtrl($scope) {
  $scope.mapStatus = {};

  $scope.hideMe = function() {
    $scope.mapStatus.loading = false;
  }

  $scope.showMe = function() {
    $scope.mapStatus.loading = true;
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
  <div ng-controller="MyCtrl">
    <div class="tab-loading-container" ng-if="mapStatus.loading">
      <div class="tab-loading">Loading map</div>
    </div>
    <button ng-click="hideMe()">Hide Me</button>
    <button ng-click="showMe()">Show Me</button>
  </div>
</div>

【讨论】:

  • 感谢您的回复,但问题仍然存在。我添加了更多代码来显示我将其设置为 true 的位置
  • 将此标记为正确答案,因为这是一个问题,另一个问题是逻辑问题
【解决方案2】:

我不确定这是否能解决您的问题,但在您深入探讨之前,我建议您删除对 $scope 的引用并使用 controllerAs。我见过很多实例,其中对直接添加到 $scope 的属性/方法的引用会因为没有特殊原因而失败。具体来说,我建议您:

  • 删除注入 $scope 的位置,
  • 将var ctrl=this 添加到控制器中的构造函数代码中,
  • 将控制器中对 $scope 的所有引用更改为 ctrl,
  • 将 controllerAs vm 添加到加载控制器的位置,
  • 将视图中对控制器属性的所有引用更改为以 vm 为前缀。

您可能会很幸运,这可能会解决您的问题,但无论哪种方式,这仍然是使用控制器的更好方法。 (如果您使用的是 Angular 1.5+,我真的建议您在走得太远之前将其重构为一个组件。)

【讨论】:

    【解决方案3】:

    (我发布此答案以防有人使用 angular v2+ 遇到此问题)

    如果ngIf 指令所指的表达式被来自外部角度区域的(异步)调用更新,则可能会发生此问题。这不会导致角度变化检测,因此在下一次变化检测发生之前,您的视图不会更新。您可以通过以下链接了解有关区域和更改检测的更多信息:zoneschange detection

    我在将 google 登录功能集成到我的组件时遇到了这个问题。 signInChangeListener 在用户登录或退出时由第三方谷歌库调用:

    export class GoogleLoginService {
       signedIn: boolean = false;
       ...
       signInChangeListener(isSignedIn) {
           this.signedIn = isSignedIn;
       }
    }
    

    在我的 HTML 中:

    <div *ngIf="signedIn"> ... </div>
    

    我必须更改我的代码,以便 angular 检测到更改:

    import { Injectable, NgZone } from '@angular/core';
    
    export class GoogleLoginService {
       signedIn: boolean = false;
    
       constructor(private ngZone: NgZone) { }
       ...
       signInChangeListener(isSignedIn) {
          // Run the value change inside angular 
          this.ngZone.run(() => {
             this.signedIn = isSignedIn;
          });
       }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多