3

I have a directive that will get/set focus on an element depending on a value.

When the element is focused or blurred the directive will update the boolean value. However, sometimes I only want to set the focus when a condition is met and I use a non-assignable value, which throws this error:

Error: [$compile:nonassign] Expression '$last && !domain.url' used with directive 'focusWhen' is non-assignable!

WORKING DEMO HERE (check the console for errors)

I understand why it's throwing an error, but how can I detect non-assignable values from inside the directive and then prevent it from attaching focus/blur events that attempt to assign the value when the focus changes?


Here's an example usage of the directive used with a non-assignable value. In this case, it would set the focus on the last item in the repeater if it is also a blank value

<div ng-repeat="domain in domainList">
  <input type="text" ng-model="domain.url" focus-when="$last && !domain.url"/>
</div>

And here's the directive code;

testApp.directive("focusWhen", ["$timeout", function ($timeout) {

    function getLink($scope, $element) {
        $scope.$watch("focusWhen", function (val) {
            //only focus when needed and when this element doesn't already have focus
            if (val && $element[0] !== document.activeElement) {
                //Small delay needed before we can get focus properly
                $timeout(function () {
                    $element.focus();
                });
            }
        });
        $element.on("blur.focusWhen", function () {
            if ($scope.focusWhen) {
                $timeout(function () {
                    $scope.focusWhen = false;
                });
            }
        });
        $element.on("focus.focusWhen", function () {
            if (!$scope.focusWhen) {
                $timeout(function () {
                    $scope.focusWhen = true;
                });
            }
        });
    }

    return {
        restrict: "A",
        scope: {
            focusWhen: "="
        },
        link: getLink
    };
}]);

1 Answer 1

1

Updated

Programmatic way to detect this (instead of having one more attribute) is using $parse fn. This is how angular does it to check and throw the error. It tries to parse the two-way binding attribute and if it does not have an assign attribute it throws the error. ($parse($attrs['focusWhen']).assign).

http://jsfiddle.net/cw7fftfx/


Old Answer:

How about one more attribute to indicate if you want to set back the focus value?

<input type="text" ng-model="domain.url" one-way="true" focus-when="$last && !domain.url"/>

    $element.on("blur.focusWhen", function () {
        if ($scope.focusWhen) {
            $timeout(function () {
                if (!$attr.oneWay) {
                $scope.focusWhen = false;
                }
            });
        }
    });

http://jsfiddle.net/j1v6jk8k/3/

Sign up to request clarification or add additional context in comments.

3 Comments

I was considering this, and i'll for sure do this unless there's a programmatic way to detect non-assignable values. perhaps I could attempt to assign the value inside a try/catch, and it that throws an error I would know? That seems hacky though.
Looks like the programmatic way to detect thing is using $parse fn. This is how angular does it to check and throw the error. It tries to parse the attribute and if it does not have an assign attribute it throws the error. ($parse($attrs['focusWhen']).assign). Update fiddle jsfiddle.net/cw7fftfx
ah yes, that's it! Thanks you! if you post this as an answer, i will mark it as correct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.