AngularJS日期选择器ng-change作用域状态保存问题排查
嘿,我来帮你搞定这个头疼的问题!首先纠正一下你的猜测:ng-change并不会创建局部作用域,你的问题其实是AngularJS里经典的「作用域变量遮蔽」问题——哪怕你没察觉到子作用域的存在,某些第三方指令(比如你用的uib-datepicker-popup)内部可能会创建子作用域,直接绑定到$scope上的变量很容易被这类子作用域遮蔽,导致你在控制器里看到的始终是父作用域的初始值。
问题根源
AngularJS的作用域继承基于原型链:如果子作用域里创建了和父作用域同名的变量,就会覆盖父作用域的变量,后续对这个变量的修改只会作用于子作用域,父作用域的变量完全不受影响。你调试时看到$scope.simStartDate有变化,可能只是子作用域里的同名变量被修改了,而控制器的父作用域变量根本没被更新。
解决方案:使用「点符号」绑定模型
这是AngularJS开发中避免作用域遮蔽的标准方案——把需要绑定的变量放到一个对象里,通过对象属性的方式访问,而不是直接挂载到$scope上。这样即使有子作用域,修改的也是对象的属性(会沿着原型链找到父作用域的对象),不会创建新的变量。
修改后的代码示例:
HTML代码
<div class="row" ng-controller="DashboardParamsFormCtrl"> <div class="col-sm-6"> <div class="form-group"> <label for="inputFirstName">Simulation start date</label> <p class="input-group"> <input type="text" class="form-control" uib-datepicker-popup="{{format}}" datepicker-options="options" ng-model="date" ng-change="setDate(date)" is-open="opened" ng-required="true" close-text="Close" alt-input-formats="altInputFormats" show-button-bar="true" /> <span class="input-group-btn"> <button type="button" class="btn btn-default" ng-click="open()"> <i class="glyphicon glyphicon-calendar"></i> </button> </span> </p> </div> </div> </div>
JS代码(修复语法错误+使用点符号)
angular.module('BlurAdmin.pages.dashboard') .controller('DashboardParamsFormCtrl', DashboardParamsFormCtrl); /** @ngInject */ function DashboardParamsFormCtrl(baConfig, layoutPaths, baUtil, $scope) { // 初始化表单数据对象,用点符号访问属性 $scope.formData = {}; $scope.formData.simStartDate = new Date(); $scope.date = new Date(); $scope.ParamsFormBtnClicked = function() { console.log("Date: " + $scope.formData.simStartDate); } $scope.open = open; $scope.opened = false; $scope.formats = ['dd-MMMM-yyyy', 'yyyy/MM/dd', 'dd.MM.yyyy', 'shortDate']; $scope.format = $scope.formats[0]; $scope.options = { showWeeks: false }; function open() { $scope.opened = true; } $scope.setDate = function(startDate) { // 修改对象属性而非直接赋值$scope变量 $scope.formData.simStartDate = startDate; } }
额外注意点
你原来的JS代码末尾多了一个多余的})();,这会导致语法错误,我已经在修改后的代码里去掉了,记得要修复这个问题,否则控制器可能无法正常加载。
为什么这样有效?
当你使用对象属性($scope.formData.simStartDate)时,子作用域会继承父作用域的formData对象引用。修改这个属性时,会直接操作父作用域对象的属性,不会在子作用域创建新的变量,自然也就不会出现遮蔽问题,提交表单时就能拿到正确的日期值了。
内容的提问来源于stack exchange,提问作者madu

