1

I'm implementing a solution for focusing an element, proposed here. It works fine for a html input, but does not work for a custom directive. Here is the plunk to demonstrate the issue. Clicking "Focus input" upon page load focuses text input next to the button, but clicking "Focus control" doesn't focus text input next to it. Actually, the reason for this is that the $watch function is not called when clicking "Focus control" button (can be seen in browser console).

The question is why that $watch function is not called?

JavaScript

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

app.controller('MainCtrl', function($scope) {
  $scope.name = 'World';
  $scope.inputFocused = false;
  $scope.controlFocused = false;
});

app.directive('focusedWhen', function () {
  return {
    restrict: 'A',
    link: function (scope, element, attrs) {
      scope.$watch(attrs.focusedWhen, function (value) {
        console.log('focusedWhen', value);
        if (value) {
          element.find('input[type=text]').andSelf().trigger('focus');
        }
      });
    }
  };
});
app.directive('myControl', function () {
  return {
    restrict: 'E',
    scope: {},
    template: '<span><input type="text"></span>',
    link: function (scope, element, attrs) {

    }
  };
});

HTML

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="jquery@*" data-semver="1.7.2" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
    <script data-require="[email protected]" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js" data-semver="1.0.7"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <p>Hello {{name}}!</p>
    <div>
      <button ng-click="inputFocused = true;">Focus input</button>
      <input type="text" focused-when="inputFocused" />
    </div>
    <div>
      <button ng-click="controlFocused = true;">Focus control</button>
      <my-control focused-when="controlFocused"></my-control>
    </div>
  </body>

</html>

1 Answer 1

1

It looks like issue is that your $watch is not firing because the value is still true on subsequent clicks. The issue you have is that your focus can be on only on one input. So, all button/inputs pairs need to know how to 'reset' themselves to false when one of their peers is clicked-to-focus. I would instead do the reverse - set the true value of the directive that gets focus instead of the true value triggering the focus. You can create a new kind of directive that listen to clicks (like how ng-click works) and then traverse the directive's children to find the input to trigger its focus. This same directive can have a 'blur' event in its link() to know to set its boolean to false (i.e., controlFocus or inputFocus).

EDIT Your directive myControl was create an isolate scope when you assign to it scope:{}. From this:

The 'isolate' scope differs from normal scope in that it does not prototypically inherit from the parent scope. This is useful when creating reusable components, which should not accidentally read or modify data in the parent scope.

So controlFocus doesn't exist when you assign {} to the scope.

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

7 Comments

I believe this issue has nothing to do with subsequent clicks. The issue is that $watch function does not fire even for the first click on "Focus control" button. Before the first click the value in controller is false. So at least for the first click it should fire. Could you please show your way of solving this issue in a plunker/fiddle/whatsoever?
Forgot that I made change while reviewing the code. I removed the scope from your custom directive because it creates a new isolate scope. I then added replace: true so that the intended markup takes its place. Now, the input derived from your custom directive will take focus. PLUNKER: plnkr.co/edit/ClrLRV49UG3klbA9fAww?p=preview
Ok, it does work when scope: false is set. It works when scope: true is set. It works even if replace: false is set (looks like replace does not affect anything). But, unfortunately, it does not work when scope: {} is set (isolated scope). Could you please update your answer to clearly state the reason of the issue: usage of an isolated scope? So that I could accept it. P.S. perhaps, forking the plunk was a better option. Because now it won't be clear for other viewers what the issue is.
@RamanChodźka From the this documentation: "The 'isolate' scope differs from normal scope in that it does not prototypically inherit from the parent scope. This is useful when creating reusable components, which should not accidentally read or modify data in the parent scope.
Ended using scope: true in my directive.
|

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.