AngularJS指令范围变量未定义

AngularJS Directive Scope variables undefined

本文关键字:未定义 变量 范围 指令 AngularJS      更新时间:2023-09-26

这是相关的JSFiddle

https://jsfiddle.net/9Ltyru6a/3/

在小提琴中,我已经设置了一个控制器和一个指令,我想用它来调用一个回调,每当一个值被改变。我知道Angular有一个ng-change指令,但我想要一些更类似于标准onchange事件的指令(当字段被模糊时触发一次)。

控制器:

var Controllers;
    (function (Controllers) {
    var MyCtrl = (function () {
        function MyCtrl($scope) {
            $scope.vm = this;
        }
        MyCtrl.prototype.callback = function (newValue) {
            alert(newValue);
        };
        return MyCtrl;
    })();
    Controllers.MyCtrl = MyCtrl;
})(Controllers || (Controllers = {}));

指令:

var Directives;
(function (Directives) {
    function OnChange() {
        var directive = {};
        directive.restrict = "A";
        directive.scope = {
            onchange: '&'
        };
        directive.link = function (scope, elm) {
            scope.$watch('onChange', function (nVal) {
                elm.val(nVal);
            });
            elm.bind('blur', function () {
                var currentValue = elm.val();
                scope.$apply(function () {
                    scope.onchange({ newValue: currentValue });
                });
            });
        };
        return directive;
    }
    Directives.OnChange = OnChange;
})(Directives || (Directives = {}));
HTML:

<body ng-app="app" style="overflow: hidden;">
    <div ng-controller="MyCtrl">
        <button ng-click="vm.callback('Works')">Test</button>
        <input onchange="vm.callback(newValue)"></input>
    </div>
</body>

按钮工作,所以我可以放心地说(我认为),控制器是好的。但是,每当我更改输入字段的值并取消焦点时,就会得到"vm未定义"错误。

谢谢你的帮助!

首先,使用正确的控制器符号,而不是$scope.vm = this;:

ng-controller="MyCtrl as vm"

然后不要混合自定义指令与本地onchange事件处理程序-这就是为什么你得到undefined错误的原因。将您的指令命名为onChange,并使用on-change属性代替。

正确的代码应该是这样的:
var app = angular.module("app", []);
var Directives;
(function (Directives) {
    function OnChange() {
        var directive = {};
        directive.restrict = "A";
        directive.scope = {
            onChange: '&'
        };
        directive.link = function (scope, elm) {
            elm.bind('blur', function () {
                var currentValue = elm.val();
                scope.$apply(function () {
                    scope.onChange({
                        newValue: currentValue
                    });
                });
            });
        };
        return directive;
    }
    Directives.onChange = OnChange;
})(Directives || (Directives = {}));
app.directive("onChange", Directives.onChange);

var Controllers;
(function (Controllers) {
    var MyCtrl = (function () {
        function MyCtrl($scope) {
        }
        MyCtrl.prototype.callback = function (newValue) {
            alert(newValue);
        };
        return MyCtrl;
    })();
    Controllers.MyCtrl = MyCtrl;
})(Controllers || (Controllers = {}));
app.controller("MyCtrl", ["$scope", function ($scope) {
    return new Controllers.MyCtrl($scope);
}]);
演示:

https://jsfiddle.net/9Ltyru6a/5/

如果你的代码的意图是只在模糊时更新你的控制器值,而不是在每次按键时更新它,angular有ngModelOptions用于此用途。例如:

<input type="text" ng-model="user.name" ng-model-options="{ updateOn: 'blur' }" />

您甚至可以提供一个debounce,或者一个按钮来清除值....

<form name="userForm">
  <input type="text" name="userName" 
         ng-model="user.name" ng-model-options="{ debounce: 1000 }" />
  <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
</form>

在这些情况下,如果你要提供一个ng-change,它只会在blur事件上触发,或者在debounce之后触发。

您还可以编写指令,直接利用ngModelController中的$validators$asyncValidators

下面是Angular开发者指南中的一个例子:

app.directive('username', function($q, $timeout) {
  return {
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) {
    var usernames = ['Jim', 'John', 'Jill', 'Jackie'];
      ctrl.$asyncValidators.username = function(modelValue, viewValue) {
        if (ctrl.$isEmpty(modelValue)) {
          // consider empty model valid
          return $q.when();
        }
        var def = $q.defer();
        $timeout(function() {
          // Mock a delayed response
          if (usernames.indexOf(modelValue) === -1) {
            // The username is available
            def.resolve();
          } else {
            def.reject();
          }
        }, 2000);
        return def.promise;
      };
    }
  };
});

和HTML:

<div>
    Username:
    <input type="text" ng-model="name" name="name" username />{{name}}<br />
    <span ng-show="form.name.$pending.username">Checking if this name is available...</span>
    <span ng-show="form.name.$error.username">This username is already taken!</span>
</div>

您当然可以添加ng-model-options来确保这只触发一次。