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

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>

$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>

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);
}
}

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

Related

How to display specific items in ng-repeat with ng-mouseover?

I need to show an Edit icon on a particular element, but as hovering it shows the Edit icon on each element in ng-repeat. How can I achieve it using controllerAs?
Please provide me solution for the same. I'm able to achieve through normal behaviour($scope), but unable to get it through a controller.
Here is the link -- >
enter code herehttp://plnkr.co/edit/ETMyoDLR8HPFIZFrA90n?p=preview
Here is what you need. Instead of setting the hoverEdit with the controller use it instead with the task.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function() {
var ma = this;
ma.tasks = [{
name: 'Item One'
},
{
name: 'The second item'
},
{
name: 'Three items is the best'
}
];
ma.hoverIn = function(task) {
task.hoverEdit = true;
};
ma.hoverOut = function(task) {
task.hoverEdit = false;
};
});
/* Put your css in here */
li {
width: 200px;
list-style-type: none;
padding: 6px 10px;
}
li:hover {
background: #EEE;
}
.animate-show {
-webkit-transition: all linear 0.5s;
transition: all linear 0.5s;
opacity: 1;
}
.animate-show.ng-hide-add,
.animate-show.ng-hide-remove {
display: inline-block !important;
}
.animate-show.ng-hide {
opacity: 0;
}
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body ng-controller="MainCtrl as ma">
<ul>
<li ng-repeat="task in ma.tasks" ng-mouseover="ma.hoverIn(task)" ng-mouseleave="ma.hoverOut(task)">
{{task.name}}
<span ng-show="task.hoverEdit" class="animate-show">
<a>
<img src="https://cdn2.iconfinder.com/data/icons/freecns-cumulus/16/519584-081_Pen-16.png" alt="">
Edit
</a>
</span>
</li>
</ul>
</body>
</html>
Hope it helps :)
you can use $index to set the index to hoverEdit
ma.hoverIn = function(index){
ma.hoverEdit = index;
};
ma.hoverOut = function(){
ma.hoverEdit = null;
};
checking if you are hovering the index and display it using ng-show
<ul>
<li ng-repeat="task in ma.tasks" ng-mouseover="ma.hoverIn($index)" ng-mouseleave="ma.hoverOut()">
{{task.name}}
<span ng-show="ma.hoverEdit == $index" class="animate-show">
<a>
<img src="https://cdn2.iconfinder.com/data/icons/freecns-cumulus/16/519584-081_Pen-16.png" alt="">
Edit
</a>
</span>
</li>
here is the plunker
There is one more solution please have a look into this added plunker code.
<body ng-controller="MainCtrl as ma">
<ul>
<li ng-repeat="task in ma.tasks" ng-mouseover="ma.hoverIn(task)" ng-mouseleave="ma.hoverOut(task)">
{{task.name}}
<span ng-show="task.hoverEdit" class="animate-show">
<a>
<img src="https://cdn2.iconfinder.com/data/icons/freecns-cumulus/16/519584-081_Pen-16.png" alt="">
Edit
</a>
</span>
</li>
</ul>
</body>
var app = angular.module('plunker', ['ngAnimate']);
app.controller('MainCtrl', function() {
var ma=this;
ma.tasks = [
{name: 'Item One', hoverEdit : false},
{name: 'The second item', hoverEdit : false},
{name: 'Three items is the best', hoverEdit : false}
];
ma.hoverIn = function(obj){
obj.hoverEdit = true;
};
ma.hoverOut = function(obj){
obj.hoverEdit = false;
};
});

Reusing methods in AngularJS instead of defining multiple variables

I've created a sample application in angularjs.
I've different variables(colorNew and numberNew) which hold the value of similar operation.
How can I optimize the code(create a method), so that I can reuse my angular.forEach as well as split method shown in the code instead of writing them multiple times?
Here's the code:
angular.module('myApp', [])
.controller('myCtrl', ['$scope', function($scope){
$scope.myData = [
{
'car': 'Ford',
'color': 'Black, White, Blue',
'number': '1, 2, 3',
'model': 'Figo'
}, {
'car': 'Ford',
'color': 'Red, Black, Silver',
'number': '4,5',
'model': 'Endeavour'
},{
'car': 'Jaguar',
'color': 'White',
'number': '6',
'model': 'F-Type'
},
];
$scope.getData = function(){
$scope.color = this.car.color.split(',');
$scope.number = this.car.number.split(',');
console.log($scope.color);
$scope.colorNew = angular.forEach($scope.color, function() {
return $(this);
}).join(' | ');
$scope.numberNew = angular.forEach($scope.number, function() {
return $(this);
}).join(' | ');
console.log($scope.colorNew);
console.log($scope.numberNew);
};
}]);
.parent {
border: 1px solid lightgrey;
border-radius: 5px;
background-color: skyblue;
height: 100px;
margin-top: 5px;
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<button id="createData">Create Data</button>
<div class="container">
<div ng-repeat="car in myData" class="parent">
<div>
<label>Car:</label>
<span>{{car.car}}</span>
</div>
<br />
<div>
<label>Model:</label>
<span>{{car.model}}</span>
</div>
<br />
<button ng-click="getData(obj)">Click Me!</button>
</div>
</div>
</div>

AngularFire searching key node and assign to variable

I 'm working on my AngularFire project. I've a database something like this https://dinosaur-facts.firebaseio.com/. What I'm doing is first searching the key like the dinosaurs child such as "linhenykus" and storing its keys and values to other $scope variable.
my code in controller
var ref = new Firebase("https://dinosaur-facts.firebaseio.com/dinosaurs" + "/" + $scope.dinoID); //$scope.dinoID is variable containg dino keys
$scope.detRef = $firebaseArray(ref);
I'm getting the output like
[{"$value":-85000000,"$id":"appeared","$priority":null},{"$value":0.6,"$id":"height","$priority":null},{"$value":1,"$id":"length","$priority":null},{"$value":"theropoda","$id":"order","$priority":null},{"$value":-75000000,"$id":"vanished","$priority":null},{"$value":3,"$id":"weight","$priority":null}]
How to fetch the keys and values?
Use ngRepeat directive, as below:
<tr ng-repeat="obj in array track by $index">
To filter the array you can use the filter of Angular:
<tr ng-repeat="obj in array | filter: criteria track by $index">
Here's an example:
(function() {
angular
.module('app', [])
.controller('MainCtrl', MainCtrl);
MainCtrl.$inject = ['$scope', '$filter'];
function MainCtrl($scope, $filter) {
$scope.criteria = {};
$scope.array = [
{
"$value":-85000000,
"$id":"appeared",
"$priority":null
},
{
"$value":0.6,
"$id":"height",
"$priority":null
},
{
"$value":1,
"$id":"length",
"$priority":null
},
{
"$value":"theropoda",
"$id":"order",
"$priority":null
},
{
"$value":-75000000,
"$id":"vanished",
"$priority":null
},
{
"$value":3,
"$id":"weight",
"$priority":null
}
];
$scope.getObj = function(value, id) {
$scope.obj = $filter('filter')($scope.array, {
"$value": value,
"$id": id
})[0];
}
$scope.getObj("theropoda", "order");
}
})();
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
</head>
<body ng-controller="MainCtrl">
<input type="text" placeholder="Search by value" ng-model="criteria.$value">
<input type="text" placeholder="Search by id" ng-model="criteria.$id">
<!-- Note that this button is just as an example to change the object displayed below -->
<button type="button" ng-click="getObj()">Change the value of object</button>
<hr>
Obj: <span ng-bind-template="Value: {{obj.$value}}, Id: {{obj.$id}}"></span>
<p></p>
<table width="100%">
<thead>
<tr>
<th>Value</th>
<th>Id</th>
<th>Priority</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="obj in array | filter: criteria track by $index">
<td ng-bind="obj.$value"></td>
<td ng-bind="obj.$id"></td>
<td ng-bind="obj.$priority"></td>
</tr>
</tbody>
</table>
</body>
</html>

angularjs ng-repeat with dynamic json/object

I am looking a solution for dynamic data structure(inconsistent like different property name and property length) with ng-repeat. sample code are below.
$scope.data = [{
"table":[{
"employee":"name1",
"value1":"10",
"value2":"20"
},
{
"employee":"name2",
"value1":"15",
"value2":"30"
}]
},
{
"table":[{
"company":"name1",
"compnayValue":"12"
},
{
"company":"name2",
"compnayValue":"12"
}]
}]
<ul>
<li ng-repeat="item in data">
<table>
<tr ng-repeat="row in item.table">
<td>{{??}}</td>
<td>{{??}}</td>
</tr>
</table>
</li>
</ul>
You could enumerate all properties and display their values by another ng-repeat on td:
<li ng-repeat="item in data">
<table>
<tr ng-repeat="row in item.table">
<td ng-repeat="(key, value) in row">
{{row[key]}}
</td>
</tr>
</table>
</li>
but that would break the tabular format of data since some rows would have more tds. To prevent that you could first find out the set of all keys on all rows, do a th repeat with those first and then display them on the corresponding td below, e.g.:
<th ng-repeat="propertyName in allPossiblePropertyNames">
{{propertyName}}
</th>
and
<td ng-repeat="propertyName in allPossiblePropertyNames">
{{row[propertyName ]}}
</td>
Use <tbody> to represent an object inside table array and (key,value) syntax mentioned in iterating over object properties section to iterate over it's properties like:
angular.module('test', []).controller('testCtrl', function($scope) {
$scope.data = [{
"table": [{
"employee": "name1",
"value1": "10",
"value2": "20"
}, {
"employee": "name2",
"value1": "15",
"value2": "30"
}]
}, {
"table": [{
"company": "name1",
"compnayValue": "12"
}, {
"company": "name2",
"compnayValue": "12"
}]
}]
});
ul {
padding: 0;
}
ul li {
list-style-type: none;
margin-bottom: 10px;
}
table {
width: 100%;
table-layout: fixed;
background: #ebebeb;
}
tbody:nth-child(odd) tr {
color: #fff;
background: dodgerblue;
}
tbody:nth-child(even) tr {
color: #fff;
background: hotpink;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="test" ng-controller="testCtrl">
<ul>
<li ng-repeat="item in data">
<table>
<tbody ng-repeat="row in item.table">
<tr ng-repeat="(key, value) in row">
<td>
{{key}}
</td>
<td>
{{value}}
</td>
</tr>
</tbody>
</table>
</li>
</ul>
</div>
Check this plunker, you can define template depends on your data :
https://plnkr.co/edit/fVGhKZy5gnBzuPwspy5s?p=preview
Use angular filter :
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.data = [{
"table":[{
"employee":"name1",
"value1":"10",
"value2":"20"
},
{
"employee":"name2",
"value1":"15",
"value2":"30"
}]
},
{
"table":[{
"company":"name1",
"compnayValue":"12"
},
{
"company":"name2",
"compnayValue":"12"
}]
}]
})
.filter('isCompnay', function() {
return function(input) {
console.log(input.employee === undefined)
return input.company ? input : undefined;
};
})
.filter('isEmployee', function() {
return function(input) {
console.log(input.employee === undefined)
return input.employee ? input : undefined;
};
});

backbone : Collection and View rendering

I am really confused on this one and need help. (unrelated : "up for about 36 hours now to finish somthing and this last part is just making me go crazy")
My Code
<body>
<div id="mycontainer" style="margin: 20px; ">
</div>
<script type="text/template" id="Myelement_template">
<% _.each( results, function( item, i ){ %>
<div id="Myelement" style="width: 200px; height:325px; padding: 10px; background-color: #2980b9;">
<div id="image" style="width: 190px; height: 200px; margin: auto; background-color: #f1c40f;">
<img src="<%= item.get('category').url %>" style="max-width: 90%;margin-top: 10px;">
</div>
<div id="type" style="float:left;width: 90px; height: 25px; margin-left: 5px;margin-top: 5px;background-color: #f1c40f;">
<%= item.get("category").type %>
</div>
<div id="name" style="float:left;width: 90px; height: 25px; margin-left: 10px; margin-top: 5px;background-color: #f1c40f;">
<%= item.get("category").name %>
</div>
</div>
<% }); %>
</script>
<script type="text/javascript">
$(document).ready(function() {
CategoryModel = Backbone.Model.extend({
defaults: {
url: '',
type: '',
name: ''
}
});
MyListModal = Backbone.Model.extend({
url: 'myrestapi',
defaults: {
category: CategoryModel
}
});
MyElementView = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render'); // bind 'this' in 'render'
this.model = new MyListModal();
this.model.bind('change', this.render);
this.model.fetch(this.render);
},
el: $("#mycontainer"),
render: function(){
console.log(this.model.get("category"));
}
});
var myModelView = new MyElementView();
});
</script>
Question
My rest api will return many BaseModel objects. All of them need to be rendered. How do I do that?
console.log(this.model.get("category")); - code reaches here. not printing. debugging shows that rest api call was made and cata has been returned
How do I render all returned elements?
Sample Data returned by rest API
[
{
"category": {
"id": 1,
"name": "name1",
"type": "mytype1",
"url": "myurl1"
},
"user": {
"id": 153
},
"status": 1
},
{
"category": {
"id": 1,
"name": "name2",
"type": "type2",
"url": "url2"
},
"user": {
"id": 153
},
"status": 1
},
]

Resources