Search code examples
javascriptangularjsangularjs-scopeangularjs-ng-repeatangular-directive

How does one pass an argument to a directive without over-writing parent scope?


I need to create a directive that acts upon table cells where the table rows are rendered using ng-repeat -- to that end I have relied in part on this answer to a question entitled "Calling a function when ng-repeat has finished". Unlike that Q&A however, I need to pass in an argument to my directive, and for this I have relied in part on this answer (to a question entitled "Angularjs - Pass argument to directive").

So in my case I've added fixed-column-tooltip for my directive, and columnselector as its argument to the <tr> as follows:

<tr fixed-column-tooltip columnselector=".td-keyField" ng-repeat="trData in trDataWatch">

But when per the second answer, I added what I've learned is an "isolate scope" to my directive, I no longer had access to the original scope necessary as per the first answer:

'use strict';

angular.module('cmt.cases.directives')

.directive('fixedColumnTooltip', function ($timeout) {
    return {
        restrict: 'A',
        scope: {
            columnselector: '@'
        },
        link: function (scope, element, attr) {
            if (scope.$last === true) { //undefined because not operating on original scope
        ...

Is there a way to maintain access to the original scope, but also have access to the columnselector argument?


Solution

  • Despite being almost completely new to Angular, I am answering my own question but still want additional answers in case the way I solved my problem is not considered "idiomatic" Angular.

    Specifically, instead of using an isolate scope, I leveraged the third attrs (attributes) link/function argument in my code below, to otherwise get the new columnselector attribute to the html along with my directive. Is this a generally accepted practice?

    'use strict';
    
    angular.module('cmt.cases.directives')
    
    .directive('fixedColumnTooltip', function ($timeout) {
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                if (scope.$last === true) {
                    $timeout(function () {
                        element.parent().find(attrs.columnselector).each(function() {
                            var td = $(this);
                            var span = td.find('span');
    
                            if (span.width() > td.width()){
                                span.attr('data-toggle','tooltip');
                                span.attr('data-placement','right');
                                span.attr('title', span.html());
                            }
                        });
                    });
                }
            }
        }
    });
    

    ADDENDUM: As you can see from comments I have not been able to get the code from this answer to work, despite trying it a couple of different ways. If I'm doing something wrong with regard to incorporating that answer please let me know.

    In the meantime I have found another way to do it, but this is almost certainly more of a "code smell" than leveraging the attrs argument. Specifically I have discovered that I can use an isolate scope, and then access that scope's $parent scope attribute. Then I would begin my code as follows, but I am not advocating this, but rather am just noting it as it seems that TMTOWTDI (there's more than one way to do it) certainly applies to Angular:

    'use strict';
    
    angular.module('cmt.cases.directives')
    
    .directive('fixedColumnTooltip', function ($timeout) {
        return {
            restrict: 'A',
            scope: {
                columnselector: '@'
            },
            link: function (scope, element, attrs) {
                if (scope.$parent.$last === true) {
                    $timeout(function () {
                        element.parent().find(scope.columnselector).each(function() {
                        ...