WJK
WJK

Reputation: 683

How can I prevent ngClick to go through parent directive when nesting custom directives inside each other

This question follows on a previous question I had about getting these directives to work:

Previous question about dynamically generating a grid

My Html looks like this:

<div ng-grid ng-collection="entries" ng-collection-headings="headings" ng-button-click="theAction(inp)">
    <div ng-checkbox-column></div>
</div>

It generates Html that looks like this:

<div ng-grid="" ng-collection="entries" ng-collection-headings="headings" ng-button-click="theAction(inp)" class="ng-isolate-scope">
    <table class="table table-bordered">
        <thead>
            //Truncated for brevity
        </thead>
        <tbody>
            <tr id="item0" ng-repeat="item in ngCollection" class="ng-scope">
                <td ng-checkbox-column="">
                    <label>
                        <input type="checkbox" ng-model="item.checked" ng-click="tempButtonClicked()" class="ng-pristine ng-untouched ng-valid"> From the checkbox directive.
                    </label>
                </td>
            </tr>
            //Truncated for brevity
        </tbody>
    </table>
</div>

The problem I am having is that the ng-click events of the ng-checkbox-column directive cannot be assigned from the myCtrl controller outside of the ng-grid directive. This is because I created an isolate scope for the ng-grid directive and the ng-checkbox-column directive is sharing the scope of the ng-grid directive.

Is there a way around this?

What options do I have to get this to work?

Is my only option to use events?

The reason I created an isolate scope for the ng-grid directive is so that it can be used more than once in the same controller. Therefore I cannot share the scope of the myCtrl controller.

Here is a plunker showing the problem:

Plunker with working dynamic grid, but limited ng-click

Looking at the plunker, you can click on the buttons to see where the click happens in relation to the "scopes". I would like the checkboxes to fire their own events and update that status without having to go through the ng-grid directive.

Currently I am handling the ng-click with this:

ng-click='tempButtonClicked()'

and in the ng-checkbox-column controller:

$scope.tempButtonClicked = function () {
  var val = "From the checkbox directive";
  $scope.buttonClicked(val);
};

where $scope.buttonClicked(val); is a reference to a function defined in the controller of the ng-grid directive:

$scope.buttonClicked = function (inp) {
  if (typeof inp != 'undefined')
    inp = inp + ", through the grid directive.";
  else
    inp = "From the grid directive.";

    $scope.ngButtonClick({ inp: inp });
};

which in turn, binds it to a function specified through an isolate scope variable defined in the ng-grid directive as:

scope: {
  ngButtonClick: "&"
},

I hope what I'm explaining makes sense and someone can shed some more light on the subject.

What I would like to have:

Is a way to bind a function to the ng-checkbox-column directive so that I can call a function in the myCtrl controller when it is clicked.

The reason for this is that I may have multiple column templates in a grid and perform different actions depending on which column is clicked.

With the current way it works, I'll have to define x-amount of functions in the ng-grid directive to use with the templates, which seems like a sloppy way of doing it.

I would LOVE to be able to do this:

<div ng-checkbox-column ng-click-action="someDefinedFunctionInMyCtrl()"></div>

which generates this:

<td ng-checkbox-column="">
    <label>
        <input type="checkbox" ng-model="item.checked" ng-click="someDefinedFunctionInMyCtrl()"> From the checkbox directive.
    </label>
</td>

Upvotes: 0

Views: 824

Answers (1)

jlowcs
jlowcs

Reputation: 1933

Got it working using an expression as an attribute and ng-transclude:

http://plnkr.co/edit/3XmsE3d44v8O0AxOoUeF?p=preview

JS:

.directive("ngGrid", function () {
    return {
        [...]
        transclude: true, //added transclude
        template: function (element, attrs) {
            var children = element.html();
            children = children.trim().replace(/div/g, "td");
            var htmlText = [
              "<input type='button' ng-click='buttonClicked()'",
                " value='From the grid directive' />",
              "<table class='table table-bordered'>",
                "<thead><tr>",
                  "<th ng-repeat='heading in ngCollectionHeadings'>{{heading}}</th>",
                "</tr></thead>",
                //added ng-transclude
                "<tbody><tr id='item{{$index}}'",
                   " ng-repeat='item in ngCollection' ng-transclude>",
                  children,
                "</tr></tbody>",
              "</table>"
            ].join('');
            return htmlText;
        },
        [...]
    };
})

.directive("ngCheckboxColumn", function () {
    return {
        restrict: "A",
        scope: {
            //expression as an attribute
            myClick: '&'
        },
        [...]
        controller: function ($scope, $element) {
          $scope.tempButtonClicked = function () {
              var val = "From the checkbox directive";
              //call the expression (val will be available in the expression)
              $scope.myClick({val: val});
          };
        }
    };
})

HTML:

<div ng-controller="myCtrl">
    <div ng-grid ng-collection="entries"
         ng-collection-headings="headings" ng-button-click="theAction(inp)">
        <!-- added the my-click expression (use val here) -->
        <div ng-checkbox-column my-click="ctrl.theAction(val)"></div>
    </div>
    <p id="btnClickVal">{{actionVal}}</p>
    <input type="button" ng-click="theAction(fromParent)"
        value="From the parent controller" />
</div>

Upvotes: 1

Related Questions