【问题标题】:$watch not updating scope variable$watch 不更新范围变量
【发布时间】:2014-07-31 20:05:25
【问题描述】:

首先我想说我是 AngularJS 的一个完整的初学者,只是试图理解基本概念。我有 Java 和 PHP 的背景。

我正在构建网站的一部分。现在 Angular 应用程序只包含打开和关闭 2 个下拉菜单registrationDropDown 和 loginDropDown。我希望他们工作,以便一次只能打开一个,即。如果我打开一个,而另一个已经打开,则旧的将被强制关闭。

我有一项服务来管理确定下拉菜单应该打开还是关闭的变量,还有 2 个控制器,一个用于登录,一个用于注册,都包含用于各自变量的 $watch。

问题 我希望该应用能够运行,以便一次只能打开一个下拉菜单。

JSFIDDLE:http://jsfiddle.net/F5p6m/3/

    angular.module("ftApp", [])
    .factory('dropDownService', function () {
    var loginDropDownStatus = false;
    var registrationDropDownStatus = false;

    return {
        getLoginDropDownStatus: function () {
            return loginDropDownStatus;
        },
        showLoginDropDown: function () {
            console.log("showing login drop down");
            registrationDropDownStatus = false;
            loginDropDownStatus = true;
            console.log("loginDropDownStatus" + loginDropDownStatus + "registrationDropDownStatus" + registrationDropDownStatus);
        },
        hideLoginDropDown: function () {
            console.log("hiding login drop down");
            loginDropDownStatus = false;
            console.log("loginDropDownStatus" + loginDropDownStatus);

        },
        getRegistrationDropDownStatus: function () {
            return registrationDropDownStatus;
        },
        showRegistrationDropDown: function () {
            console.log("showing registration drop down");
            registrationDropDownStatus = true;
            loginDropDownStatus = false;
            console.log("registrationDropDownStatus" + registrationDropDownStatus);
        },
        hideRegistrationDropDown: function () {
            console.log("hiding registration drop down");
            registrationDropDownStatus = false;
            console.log("registrationDropDownStatus" + registrationDropDownStatus);
        }
    };

}) .controller("LoginDropDownController", function ($scope, dropDownService) {
        $scope.loginDropDownStatus = dropDownService.getLoginDropDownStatus();

        $scope.$watchCollection('loginDropDownStatus', function(newValue, oldValue) {
            console.log("watcher is working");
            console.log("value is " + newValue + oldValue);

            console.log("LOGIN new value is " + newValue);
            $scope.loginDropDownStatus = newValue;
        });

        $scope.toggleDropDown = function () {

            if ( $scope.loginDropDownStatus == false ) {
                dropDownService.showLoginDropDown();
                dropDownService.hideRegistrationDropDown();
                $scope.loginDropDownStatus = true;
            } else if ( $scope.loginDropDownStatus == true ) {
                dropDownService.hideLoginDropDown();
                $scope.loginDropDownStatus = false;
            }
        };

    })

        .controller("RegistrationDropDownController", function ($scope, dropDownService) {

        $scope.registrationDropDownStatus = dropDownService.getRegistrationDropDownStatus();

        $scope.$watch('registrationDropDownStatus', function(newValue, oldValue) {
            console.log("watcher is working");
            console.log("value is " + newValue + oldValue);

            console.log("new value is " + newValue);
            $scope.registrationDropDownStatus = newValue;
        });

        $scope.toggleDropDown = function () {

            if ( $scope.registrationDropDownStatus == false ) {
                dropDownService.showRegistrationDropDown();
                dropDownService.hideLoginDropDown();
                $scope.registrationDropDownStatus = true;
            } else if ( $scope.registrationDropDownStatus == true ) {
                dropDownService.hideRegistrationDropDown();
                $scope.registrationDropDownStatus = false;
            }
        };

        })

【问题讨论】:

  • 隔离问题,做一个小提琴。
  • 如何隔离问题?
  • 除了控制台日志之外,我不能从那里删除任何东西,可以吗?否则它将不再起作用。但总而言之,我可以说问题很简单:我想这样做一次只打开一个下拉菜单。

标签: angularjs watch


【解决方案1】:

编辑:

这可能是最短的选择:

angular.module("ftApp", [])
    .controller("ctrl", function ($scope) {
    $scope.toggle = function(menu){
         $scope.active = $scope.active === menu ? null : menu;
    }
})

FIDDLE

一个控制器,没有服务。


上一个答案:

我认为你有相当多的代码来完成一些非常简单的事情。这是我的解决方案:

angular.module("ftApp", [])
    .service('dropDownService', function () {        
        this.active = null;
        this.toggle = function(menu){
            this.active = this.active === menu ? null : menu;
        }
})

    .controller("LoginDropDownController", function ($scope, dropDownService) {
    $scope.status = dropDownService;
    $scope.toggleDropDown = function () {
        dropDownService.toggle("login");
    };

})

    .controller("RegistrationDropDownController", function ($scope, dropDownService) {
    $scope.status = dropDownService;
    $scope.toggleDropDown = function () {
        dropDownService.toggle("reg");
    };

})

FIDDLE

您可以通过仅使用一个控制器来缩短它。那时你甚至不需要这项服务。

【讨论】:

  • 将此作为公认的答案,因为它似乎是最佳实践。我最终按照你的建议使用了一个控制器。
【解决方案2】:

你把事情复杂化了。您只需要您的服务持有一个属性,该属性指示应该激活哪个 dorpdown。

然后您可以从控制器更改该属性的值并检查视图中的值以确定是否应显示或隐藏下拉菜单。

类似这样的:

<!-- In the VIEW -->
<li ng-controller="XyzController">
    <a ng-click="toggleDropdown()">Xyz</a>
    <div ng-show="isActive()">Dropdown</div>
</li>

/* In the SERVICE */
.factory('DropdownService', function () {
    return {
        activeDropDown: null
    };
})

/* In the CONTROLLER */
controller("XyzDropdownController", function ($scope, DropdownService) {
    var dropdownName = 'xyz';
    var dds = DropdownService;
    $scope.isActive = function () {
        return dropdownName === dds.activeDropdown;
    };
    $scope.toggleDropdown = function () {
        dds.activeDropdown = (dds.activeDropdown === dropdownName) ?
                null :
                dropdownName;
    };
})

另请参阅此short demo


根据您的具体操作,可能还有其他可能/首选的方法:
例如。您可以仅在控制器上使用来控制所有下拉菜单 或者您可以使用同一控制器的两个实例来控制每个下拉菜单。

【讨论】:

    【解决方案3】:

    查看我更新的fiddle。我简化了代码并删除了服务。因为您只使用了两个变量来控制可见性,所以您不需要服务也不需要$watch。您需要将变量保留在$rootScope 中,否则由于隔离范围,控制器中的更改对另一个控制器不可见。

    angular.module("ftApp", [])
        .controller("LoginDropDownController", function ($scope, $rootScope) {
        $rootScope.loginDropDownStatus = false;
        $scope.toggleDropDown = function () {
            if ($rootScope.loginDropDownStatus == false) {
                $rootScope.registrationDropDownStatus = false;
                $rootScope.loginDropDownStatus = true;
            } else if ($rootScope.loginDropDownStatus == true) {
                $rootScope.loginDropDownStatus = false;
            }
        };
    }).controller("RegistrationDropDownController", function ($scope, $rootScope) {
        $rootScope.registrationDropDownStatus = false;
        $scope.toggleDropDown = function () {
    
            if ($rootScope.registrationDropDownStatus === false) {
                $rootScope.loginDropDownStatus = false;
                $rootScope.registrationDropDownStatus = true;
            } else if ($scope.registrationDropDownStatus === true) {
                $rootScope.registrationDropDownStatus = false;
            }
        };
    
    })
    

    此代码可以进一步简化。我会把它留给你。

    【讨论】:

    • -1 用于建议 $rootScope 通过服务保存数据。
    • 我什至不确定服务是否是正确的方法。似乎这里需要一个父控制器。但是使用$rootScope 绝对不可取。
    • 同意,我也会这样做。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-28
    相关资源
    最近更新 更多