49

HTML

<table data-ng-table="tableParams" class="table table-bordered table-hover " style="border-collapse:collapse" data-ng-init="host.editSave = false" >
    <tr id="newTransaction">
    </tr>
    <tr data-ng-repeat="host in hosts|filter:search:strict" >
       <td class="hostTableCols" data-ng-hide="host.editSave">{{host.hostCd}}</td>
       <td class="hostTableCols" data-ng-hide="host.editSave">{{host.hostName}}</td>
    </tr>
</table>

Jquery

$('#newTransaction').append(
 '<td contenteditable><input type="text" class="editBox" value=""/></td>'+ 
 '<td contenteditable><input type="text" class="editBox" value=""/></td>'+
 '<td>'+
    '<span>'+
        '<button id="createHost" class="btn btn-mini btn-success" data-ng-click="create()"><b>Create</b></button>'+
    '</span>'+
 '</td>'
);

Angular Script

$scope.create = function() {
       alert("Hi");
    };

Here the function called in the controller part of the AngularJS is not getting trigger from the ng-click event. The Html is getting appended successfully, but the ng-click is not working. Tell me solutions to make it work

Kiran Kumar
  • 1,610
  • 3
  • 16
  • 22
  • 1
    you need to compile the new dom elements for angularjs to work... are you doing this manipulation in a directive/controller where you have access to the scope – Arun P Johny Oct 09 '13 at 09:26
  • @ArunPJohny: Am calling that function from controller. But where should I place the Compile function?? In a directive?? – Kiran Kumar Oct 09 '13 at 09:38
  • I don't know about the exact scenario, but you can do something similar using `ng-include` and templates. – Chandermani Oct 09 '13 at 09:43

5 Answers5

50

Not a perfect fix, still!!! - just to show how dynamic compilation can be done

app.controller('AppController', function ($scope, $compile) {

    var $el = $('<td contenteditable><input type="text" class="editBox" value=""/></td>' +
        '<td contenteditable><input type="text" class="editBox" value=""/></td>' +
        '<td>' +
        '<span>' +
        '<button id="createHost" class="btn btn-mini btn-success" data-ng-click="create()"><b>Create</b></button>' +
        '</span>' +
        '</td>').appendTo('#newTransaction');
    $compile($el)($scope);

    $scope.create = function(){
        console.log('clicked')
    }
})

Demo: Fiddle

Don't use controller for dom manipulation - it has to be done with the help of directives

Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
36

To make ng-click to work we need to compile this source by using $compile service. Angular should know about new generated HTML and therefore this HTML should be included to digest cycle in order to trigger ng-click and other events.

See Fiddle

Create "compilator":

.directive( 'compileData', function ( $compile ) {
  return {
    scope: true,
    link: function ( scope, element, attrs ) {

      var elmnt;

      attrs.$observe( 'template', function ( myTemplate ) {
        if ( angular.isDefined( myTemplate ) ) {
          // compile the provided template against the current scope
          elmnt = $compile( myTemplate )( scope );

            element.html(""); // dummy "clear"

          element.append( elmnt );
        }
      });
    }
  };
});

after, create dummy factory that simulates your append:

.factory( 'tempService', function () {
  return function () { 
    return '<td contenteditable><input type="text" class="editBox" value=""/></td>'+ 
            '<td contenteditable><input type="text" class="editBox" value=""/></td>'+
             '<td>'+
                '<span>'+
         '<button id="createHost" class="btn btn-mini btn-success" data-ng-click="create()"><b>Create</b></button>'+
              '</span>'+
            '</td>';
  };
});

And finally call it like:

<div compile-data template="{{mainPage}}"></div> 

in Controller:

$scope.newTransaction= tempService();

For your example should be something like:

<table data-ng-table="tableParams" class="table table-bordered table-hover " style="border-collapse:collapse" data-ng-init="host.editSave = false" >
    <tr compile-data template="{{newTransaction}}">
    </tr>
    <tr data-ng-repeat="host in hosts|filter:search:strict" >
       <td class="hostTableCols" data-ng-hide="host.editSave">{{host.hostCd}}</td>
       <td class="hostTableCols" data-ng-hide="host.editSave">{{host.hostName}}</td>
    </tr>
</table>

BTW, for now you can use the same directive over your code and compile any dynamic HTML.

Maxim Shoustin
  • 77,483
  • 27
  • 203
  • 225
  • Should I implement appending the html from directive rather than doing it by jquery? – Kiran Kumar Oct 09 '13 at 09:36
  • 1
    As I showed in my example use Service and add in controller `$scope.newTransaction= tempService();` – Maxim Shoustin Oct 09 '13 at 09:39
  • @KiranKumar added Fiddle to allow you to loop up how it works – Maxim Shoustin Oct 09 '13 at 09:52
  • Just share my case, I use **Handlebar** to load template for table since ng-repeat is slow when data become heavier. I apply @Maxim Shoustin solution with `$compile( handleCompiledContent )( scope );` before attach it to the DOM, then I insert it with jQuery. It work on-the-fly. But one thing to point out, the `this` in handlebar for `ng-click will` not work if it is rendered under an object or array loop. It refer to the object/array variable instead in handlebar. – 西門 正 Code Guy Nov 06 '15 at 07:29
  • What if I can' have predefined div with the given compile-data directive, because the service creates it? And also can't compile via $scope inside the service ... I would insert div with compile-data directive, but who would compile that first to recognize the directive ... same problem for me then? – trainoasis Dec 11 '17 at 09:55
  • @trainoasis its not good practice to compile DON inside Service. use directive for that – Maxim Shoustin Dec 11 '17 at 10:15
  • @MaximShoustin i understand that, but I'm using angular datatables and you have to define how to render certain cells - you provide HTML there, and that's inside a service of course. Where would I put a directive in this case? – trainoasis Dec 11 '17 at 10:37
25

you can use angular.element(this).scope() without use of ng-click

and change

'<button id="createHost" class="btn btn-mini btn-success" data-ng-click="create()"><b>Create</b></button>'

To

'<button id="createHost" class="btn btn-mini btn-success" onclick="angular.element(this).scope().create()"><b>Create</b></button>' is good

porya ras
  • 458
  • 5
  • 15
3

I needed to have Cordova open the dynamic link in a new window, so my solution was to put the ng-click on the parent element and look at the event.target to see what was clicked on:

<p ng-bind-html="foo.barhtml" ng-click="generalClick($event)"></p>

then

.controller('FooCtrl', function ($scope) {
    var html = '<a href="http://www.google.com">google.com</a>';

    $scope.foo.barhtml = html.replace(/href/g, 'data-href');

    $scope.generalClick = function(event){
        // have Cordova open the link in a browser window
        window.open(event.target.attributes['data-href'].value, '_system');
    }
})
ricka
  • 1,107
  • 1
  • 11
  • 13
2

you need to add $compile service here, that will bind the angular directives like ng-click to your controller scope.Something like:

var divTemplate = '..your div template';
var temp = $compile(divTemplate)($scope); 

Then append it to the HTML:

angular.element(document.getElementById('foo')).append(temp);

You can also bind the event to the div as following:

var div = angular.element("divID");
div.bind('click', $scope.addPhoto());
Pritam Banerjee
  • 17,953
  • 10
  • 93
  • 108
AMRESH PANDEY
  • 195
  • 1
  • 10