4

The scope variables updated by the directive are not reflected where we have {{variable}} on the page, however they do show up as being updated in when I do console.log(scope).

<script>
    var demoApp = angular.module('demoApp', []);

    demoApp.controller('demoController', function demoController($scope) {
      $scope.myElementId ="abcd";
      $scope.updateVals = function() {
        $scope.myElementClass = "";
      };
    });

    demoApp.directive('demoSelect', function() {
      return {
        restrict: 'A',
        controller: 'demoController',
        link: function(scope, element, attrs, controller) {
          element.on("click", function(event) {
            event.stopImmediatePropagation();
            event.preventDefault();
            scope.ele = angular.element(event.target);
            angular.element(".selected").removeClass("selected");
            $(scope.ele).addClass("selected");
            var mm = $(scope.ele).get(0);
            scope.myElementId = $(scope.ele).attr("id");
            scope.myElementClass = $(scope.ele).attr("class");
            scope.myElementType = mm.tagName;
          });
        }
      };
    });
  </script>

Here is the plunker for it. What am I missing?

isherwood
  • 58,414
  • 16
  • 114
  • 157
Vikram
  • 6,865
  • 9
  • 50
  • 61

3 Answers3

6

There were a couple of errors in your demo:

  1. jQuery was loaded after Angular. You should always load jQuery before Angular, so the latter is aware of and can take advantage of the former.

  2. The automatic dash-delimited to camelCase convertion is applied only to directives, not the names of your apps or controllers. Thus, you should write demoApp and demoController, instead of demo-app and demo-controller in your HTML.

  3. You were calling some Angular-specific code (updating values in the Scope) from an non-Angular context (jQuery event callback), so in order for having the changes processed by Angular (and the results propagated to the view), you must enclose the relevant code into scope.$apply().

  4. (This is not an error technically, but): You were converting some jQuery objects to jQuery objects again. This instroduces unnecessary overhead and should be avoided.


Taking into account the above, your directive's link function's body should look something like this:

element.on('click' , function(evt) {
    evt.stopImmediatePropagation();
    evt.preventDefault();

    $('.selected').removeClass('selected');
    element.addClass('selected');
    scope.$apply(function () {
        scope.myElementId    = attrs.id;
        scope.myElementClass = attrs.class;
        scope.myElementType  = element.prop('tagName');
    });
});

See, also, this woking demo.

gkalpak
  • 47,844
  • 8
  • 105
  • 118
  • on my local dev, all the issues were ok except that i was missing scope.$apply. thanks for pointing the rest of the issues as well. – Vikram Jan 20 '14 at 04:24
  • one more small question - how will i have this thing work in iFrame context for dynamic elements (you've seen the drag drop across iframe one). ive tried loads of things but again missing something very basic there. – Vikram Jan 20 '14 at 05:36
  • I don't think anything fancy is required. Make sure to have the appropriate CSS styles in the iframe and complie the dropped elements in the context of the iframe. – gkalpak Jan 20 '14 at 15:39
  • may i request to have a look at the code here - http://78.110.163.229/ang/select/outer.html the select directive works perfect outside iFrame but not inside the iframe. i have attached the onclick event on the dragged elements at "stop" function and also compiled the iframeBody as well but still the click event does not fires. – Vikram Jan 20 '14 at 15:52
4

Use scope.$apply:

scope.$apply(function(){
    scope.ele = angular.element(event.target);
    angular.element(".selected").removeClass("selected");
    $(scope.ele).addClass("selected");
    var mm = $(scope.ele).get(0);
    scope.myElementId = $(scope.ele).attr("id");
    scope.myElementClass = $(scope.ele).attr("class");
    scope.myElementType = mm.tagName;
});

You need this because you are modifying scope variables inside event and angularjs does not update dom in this case

karaxuna
  • 26,752
  • 13
  • 82
  • 117
2

Your link function should look like this:

link: function(scope, element, attrs, controller) {
      element.on("click", function(event) {

        event.stopImmediatePropagation();
        event.preventDefault();

        $(".selected").removeClass("selected");
        $(element).addClass("selected");

        var mm = $(element).get(0);
        scope.myElementId = $(element).attr("id");
        scope.myElementClass = $(element).attr("class");
        scope.myElementType = mm.tagName;

      });
    }

Also you have an error in your code. Take care of camelcase names for controller and app.

Nikola Nikolić
  • 356
  • 2
  • 17