动态生成的HTML无法触发ng-click事件

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(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脚本
$scope.create = function() {
       alert("Hi");
    };

在AngularJS控制器部分调用的函数未从ng-click事件触发。HTML已成功添加,但ng-click不起作用。告诉我解决方法让它工作。

1
你需要编译新的DOM元素才能让AngularJS工作...你是在指令/控制器中进行这个操作吗?这样你就可以访问作用域了。 - Arun P Johny
@ArunPJohny:我正在从控制器调用该函数。但是我应该在哪里放置Compile函数?在指令中吗? - Kiran Kumar
我不知道确切的情况,但是你可以使用 ng-include 和模板来做类似的事情。 - Chandermani
5个回答

50

不是完美的解决方案,但仍然可以展示动态编译的实现方式。

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

示例: Fiddle

不要使用控制器进行DOM操作,而是应该使用指令来实现。


我已经尝试过这种方法,但不知道 $compile...所以从jquery开始尝试。 - Kiran Kumar
1
@MaximShoustin:为什么我们不应该从控制器中使用DOM操作?有任何原因吗? - Kiran Kumar
4
可用性、代码清晰度和可维护性。控制器应关注业务逻辑,而不是界面操作。 - Maxim Shoustin
1
如果您通过服务添加HTML,而$scope不可用,该如何实现相同的效果? - trainoasis
1
@trainoasis,你可以尝试使用父元素的作用域,例如$('#newTransaction').scope(),即$compile($el)($('#newTransaction').scope()); - Arun P Johny

36
为了使 ng-click 起作用,我们需要使用 $compile 服务对这个源代码进行编译。Angular 应该知道新生成的 HTML,并且这个 HTML 应该包含在消化周期中,以触发 ng-click 和其他事件。
请查看Fiddle 创建“编译器”:
.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 );
        }
      });
    }
  };
});

接着,创建一个虚拟的工厂,模拟您的附加功能:

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

最后像这样调用:

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

在控制器中:

$scope.newTransaction= tempService();
您的例子应该是这样的:
<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>

顺便说一句,现在你可以在代码上使用同样的指令并编译任何动态HTML。


我应该实现指令中的HTML追加,而不是使用jQuery吗? - Kiran Kumar
1
正如我在示例中展示的那样,使用Service并在控制器中添加$scope.newTransaction= tempService(); - Maxim Shoustin
@KiranKumar添加了Fiddle,让您可以查看它是如何工作的。 - Maxim Shoustin
只是分享我的情况,我使用 Handlebar 加载模板来制作表格,因为当数据变得更重时,ng-repeat 会变慢。在将其附加到 DOM 之前,我应用了 @Maxim Shoustin 的解决方案 $compile( handleCompiledContent )( scope );,然后使用 jQuery 插入它。它可以实时工作。但需要指出的一件事是,在对象或数组循环下呈现的 ng-click 中,handlebar 中的 this 将不起作用。它会引用对象/数组变量而不是 handlebar 中的内容。 - 西門 正 Code Guy
@MaximShoustin 我明白,但我正在使用Angular DataTables,您必须定义如何呈现某些单元格-您在那里提供HTML,当然这是在服务内部。在这种情况下,我应该把指令放在哪里? - trainoasis
显示剩余2条评论

25

你可以使用angular.element(this).scope()而不需要使用ng-click

并将

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

改为

'<button id="createHost" class="btn btn-mini btn-success" onclick="angular.element(this).scope().create()"><b>创建</b></button>'是好的


我们能在不使用作用域的情况下完成这个吗?我已经在工厂中创建弹出窗口时声明了我的弹出窗口模板。 - MobileEvangelist
如何发送参数 init? - Sana Ahmed
@Sana angular.element(this).scope().init(1,'false'); 这么简单 - porya ras
我正在使用这个代码,但是它显示“Video未定义”。有任何想法吗?返回 '<input type="radio" name ="VideoRadioButton" onclick="angular.element(this).scope().productChange('+data.ID+',"Video" )" />'; - Sana Ahmed
你不能将ng-repeat迭代的项作为参数传递给函数。 - Piyush Balapure
当调试模式为假时(例如在生产环境中),angular.element(this).scope() 将无法工作,它会返回 undefined https://github.com/angular/angular.js/issues/9515 可以使用 $compile($el)($scope) 代替。 - Rishabh Rajgarhia

3

我需要Cordova在新窗口中打开动态链接,所以我的解决方案是将ng-click放在父元素上,并查看event.target以查看单击了什么内容:

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

那么

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

2

您需要在此处添加$compile服务,它将把像ng-click这样的Angular指令绑定到您的控制器作用域上。类似于:

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

然后将其附加到HTML中:
angular.element(document.getElementById('foo')).append(temp);

您可以按照以下方式将事件绑定到div元素上:
var div = angular.element("divID");
div.bind('click', $scope.addPhoto());

如果出于某种原因您将HTML添加到服务内部,它将无法正常工作。 - trainoasis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接