5988

Angular `ng-click` not working in DataTables table row

Question:

I have an <strong>angular</strong> application that is also using <strong>jquery.dataTables</strong>. When I use <strong>datatables</strong> 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.

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

<blockquote>

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

</blockquote>

Here is my code:

<pre class="snippet-code-js lang-js prettyprint-override">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'); }; } ]); <pre class="snippet-code-css lang-css prettyprint-override"> div { margin-top: 15px; padding: 5px; } div.borderdiv { border: 1px solid black; } td { border: 1px solid black; padding: 2px } .success { background-color: green; } <pre class="snippet-code-html lang-html prettyprint-override"><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>

Answer1:

$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 <strong>controller as</strong> 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.

<pre class="snippet-code-js lang-js prettyprint-override">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'); }; } ]); <pre class="snippet-code-css lang-css prettyprint-override">div { margin-top: 15px; padding: 5px; } div.borderdiv { border: 1px solid black; } td { border: 1px solid black; padding: 2px } .success { background-color: green; } <pre class="snippet-code-html lang-html prettyprint-override"><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>

Answer2:

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

<pre class="snippet-code-js lang-js prettyprint-override">"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

Recommend

  • Changing placeholder triggers input event in IE 10
  • AngularJS ui-bootstrap modal instance issue
  • Vuejs $emit doesn't fire on callback
  • why adding a space after `(.+?)` can completely change the result
  • Multi-line JSON read using Apache PIG
  • python regex split string while keeping delimiter with value
  • Binding a list with another list with WPF
  • use images instead of text in webgrid asp mvc
  • Adding new column to DataFrame with values dependent on index ref
  • Safari PHP form submission -file upload hangs
  • Gforce min not supported for character in data.table
  • LINQ to Entities does not recognize the method 'System.Collections.Generic.Dictionary`2[System.
  • Strong vs Weak entities MYSQL
  • AWS-SES: Handling Bounces for Invalid ISPs
  • Adding Parent and Child Nodes in TreeView from Sql Server 2008
  • Reduction and collapse clauses in OMP have some confusing points
  • Webgrid not refreshing after delete MVC
  • MongoDb aggregation
  • How to use remove-erase idiom for removing empty vectors in a vector?
  • Jquery UI tool tip close icon
  • Disable Enter in editText android
  • Android fill_parent issue
  • Can I check if a recipient has an automatic reply before I send an email?
  • Retrieving value from sql ExecuteScalar()
  • Dynamically accessing properties of knockoutjs observable array
  • How to extract text from Word files using C#?
  • jquery mobile loadPage not working
  • Properly structure and highlight a GtkPopoverMenu using PyGObject
  • Traverse Array and Display in markup
  • WPF Applying a trigger on binding failure
  • How can I get HTML syntax highlighting in my editor for CakePHP?
  • Qt: Run a script BEFORE make
  • costura.fody for a dll that references another dll
  • Binding checkboxes to object values in AngularJs
  • Observable and ngFor in Angular 2
  • UserPrincipal.Current returns apppool on IIS
  • java string with new operator and a literal
  • Net Present Value in Excel for Grouped Recurring CF
  • jQuery Masonry / Isotope and fluid images: Momentary overlap on window resize
  • How to load view controller without button in storyboard?