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="angular.js@1.0.x" 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>
Community
  • 1
  • 1
Raman Chodźka
  • 558
  • 1
  • 7
  • 16

1 Answers1

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.

mitch
  • 1,821
  • 14
  • 14
  • 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? – Raman Chodźka Jul 30 '13 at 17:57
  • 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: http://plnkr.co/edit/ClrLRV49UG3klbA9fAww?p=preview – mitch Jul 30 '13 at 18:24
  • 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. – Raman Chodźka Jul 30 '13 at 20:07
  • @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. – mitch Jul 30 '13 at 20:44
  • did not get your idea. – Raman Chodźka Jul 30 '13 at 20:51
  • sorry. I accidentally posted that as a comment, not an edit to original post. See edit above. – mitch Jul 30 '13 at 20:55
  • 1
    Ended using `scope: true` in my directive. – Raman Chodźka Jul 30 '13 at 21:15