5

I have an angular application that is also using jquery.dataTables. When I use datatables to build a dynamic table with the ng-click angular directive in the table data, it does not fire the ng-click event.

I suspect that I need to use the angular $compile service, but I have not been successful finding clear documentation or examples.

Any help would be greatly appreciated.

UPDATE: I have added some code to the createdRow option in the DataTables method. I seems to be firing now, but I get an error

0x800a01b6 - JavaScript runtime error: Object doesn't support property or method '$apply'

Here is my code:

var app = angular.module('appy', []);
app.controller('myCtrl', [
  function() {
    var _this = this;

    $('#report').DataTable({
      data: [{
        "LastName": "Doe",
        "Link": "<button type=\"button\" ng-click=\"Ctrl.dataTablesAlert()\">Test Alert</a>"
      }],
      columns: [{
        "title": "Last Name",
        "data": "LastName"
      }, {
        "title": "Actions",
        "data": "Link"
      }],
      createdRow: function(row, data, dataIndex) {
        $compile(angular.element(row).contents())(_this);
      }
    });

    this.buttonAlert = function() {
      $('#buttondiv').addClass('success');
    };

    this.htmlAlert = function() {
      $('#htmltablediv').addClass('success');
    };

    this.dataTablesAlert = function() {
      $('#datatablediv').addClass('success');
    };

  }
]);
  div {
    margin-top: 15px;
    padding: 5px;
  }
  div.borderdiv {
    border: 1px solid black;
  }
  td {
    border: 1px solid black;
    padding: 2px
  }
  .success {
    background-color: green;
  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://cdn.datatables.net/1.10.13/css/jquery.dataTables.min.css" rel="stylesheet"/>
<script src="https://cdn.datatables.net/1.10.13/js/jquery.dataTables.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="appy" ng-controller="myCtrl as Ctrl">
  <div id="buttondiv" class=borderdiv>
    <h4>Button with ng-click</h4>
    <button type="button" ng-click="Ctrl.buttonAlert()">Test Alert</button>
  </div>

  <div id="htmltablediv" class="borderdiv">
    <h4>HTML Table with ng-click</h4>
    <table>
      <tr>
        <td>Last Name</td>
        <td>Actions</td>
      </tr>
      <tr>
        <td>Doe</td>
        <td>
          <button ng-click="Ctrl.htmlAlert()">
            Test Alert
          </button>
        </td>
      </tr>
    </table>
  </div>

  <div id="datatablediv" class="borderdiv">
    <h4>DataTables with ng-click</h4>
    <table id="report" class="display"></table>
  </div>

</div>

6
  • Have you checked this one: stackoverflow.com/questions/26819222/… ? Commented Jan 13, 2017 at 19:01
  • Ignacy Kasperowicz - That is a good post, but it doesn't address the datatables portion of my code. Commented Jan 13, 2017 at 19:49
  • I'm not familiar enough with DataTables to give you a solution, but I can point out the problem. The string you are using for the "Link" value: "<button type=\"button\" ng-click=\"Ctrl.dataTablesAlert()\">Test Alert</a>" Is not getting compiled by Angular. It is simple a string which is being placed in the DOM by JQuery, and which the browser is then interpreting it as HTML, but it is not being compiled by Angular and is therefore not being recognized as a directive, or being attached to the scope of your controller. Commented Jan 13, 2017 at 20:45
  • This doesn't answer your question, but why would you use jQuery dataTables when there are so many native angular grids out there? Commented Jan 13, 2017 at 21:49
  • There is a Angular wrapper for Datatables worth checking out - l-lin.github.io/angular-datatables/#/welcome. It merges both worlds. I've used it a little in the past and seems to work fine Commented Jan 14, 2017 at 1:44

3 Answers 3

16

$compile takes in a snippet of HTML and returns what's known as a linking function. This function takes a $scope that will it will use to do all the databinding.

This might have been confusing since you are using the controller as syntax (which is a good thing), so you don't deal directly $scope.

The two things you need to do here are to inject both $compile and $scope into your controller, and then use them.

//Using array injector notation here
app.controller('myCtrl', 
['$scope','$compile',
  function($scope, $compile) {
     //snip...
  }
]);

And then later when you are linking your row, you can call it with the injected $scope like this:

$compile(angular.element(row).contents())($scope);

If you run the snippet below, you can see it all works as expected.

var app = angular.module('appy', []);
app.controller('myCtrl', ['$scope','$compile',
  function($scope, $compile) {
    var _this = this;

    $('#report').DataTable({
      data: [{
        "LastName": "Doe",
        "Link": "<button type=\"button\" ng-click=\"Ctrl.dataTablesAlert()\">Test Alert</a>"
      }],
      columns: [{
        "title": "Last Name",
        "data": "LastName"
      }, {
        "title": "Actions",
        "data": "Link"
      }],
      createdRow: function(row, data, dataIndex) {
        $compile(angular.element(row).contents())($scope);
      }
    });

    this.buttonAlert = function() {
      $('#buttondiv').addClass('success');
    };

    this.htmlAlert = function() {
      $('#htmltablediv').addClass('success');
    };

    this.dataTablesAlert = function() {
      $('#datatablediv').addClass('success');
    };

  }
]);
div {
    margin-top: 15px;
    padding: 5px;
  }
  div.borderdiv {
    border: 1px solid black;
  }
  td {
    border: 1px solid black;
    padding: 2px
  }
  .success {
    background-color: green;
  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://cdn.datatables.net/1.10.13/css/jquery.dataTables.min.css" rel="stylesheet"/>
<script src="https://cdn.datatables.net/1.10.13/js/jquery.dataTables.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="appy" ng-controller="myCtrl as Ctrl">
  <div id="buttondiv" class=borderdiv>
    <h4>Button with ng-click</h4>
    <button type="button" ng-click="Ctrl.buttonAlert()">Test Alert</button>
  </div>

  <div id="htmltablediv" class="borderdiv">
    <h4>HTML Table with ng-click</h4>
    <table>
      <tr>
        <td>Last Name</td>
        <td>Actions</td>
      </tr>
      <tr>
        <td>Doe</td>
        <td>
          <button ng-click="Ctrl.htmlAlert()">
            Test Alert
          </button>
        </td>
      </tr>
    </table>
  </div>

  <div id="datatablediv" class="borderdiv">
    <h4>DataTables with ng-click</h4>
    <table id="report" class="display"></table>
  </div>

</div>

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

1 Comment

You saved one of my day.
0

this all for Jquery datatable.

"Link": "<button type=\"button\" ng-click=\"Ctrl.dataTablesAlert()\">Test Alert</a>"
createdRow: function(row, data, dataIndex) {
    $compile(angular.element(row).contents())(_this);
)

write like this

  "Link": "<button type='button' ng-click='Ctrl.dataTablesAlert()'>Test Alert</a>"
    createdRow: function(row, data, dataIndex) {
        $compile(angular.element(row).contents())(_this);
        $compile(angular.element(data).contents())(_this);
    )

this resolve my problem for Jquery datatable

but if you have angulrjs datatable and you are unable to work with ng-click or other events of angulrjs then you just have to compile what like

app.controller('auditListController', function($scope, $compile){ 
    html = "<a href='javascript:void(0)' ng-click='myfunction("+ myvariable + ")'><i class='icon fa fa-exchange' aria-hidden='true'></i></a>";
    $compile(angular.element(html).contents())($scope);

    $scope.myfunction = function (myvar) {
        console.log("myvar", myvar);
    }
}

Comments

-1

For those who wants to access $compile and $scope from outside angular and apply it to the datatable rows.. here I have the answer

"fnRowCallback": function( nRow, aData, iDisplayIndex ) {

  var $injector = angular.element(document.body).injector();
  var scope = angular.element(document.body).scope();
            
  $injector.invoke(function($compile) {
    $compile(nRow)(scope);
  });
            
},

with this you can add every ng event to the row and it will work! :D

1 Comment

Did you ever click your own "Run code snippet" button once? All it produces is an Error!

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.