2017-04-07 23 views
0

我有一個指令,動態地加載一些AngularJS(v1.2.16)組件。它從WebAPI請求組件列表,然後加載它所告知的文件,最後使用$compile編譯組件。

但是,組件在初始加載後會丟失$scope!如果組件中有ng-click事件,它將拒絕被解僱!

這是我的指導我的主要JS文件中:

var myApp = angular.module(.......); 

myApp.directive('components', function($compile, $http) { 
    return { 
     restrict: 'A', 
     link: function (scope, element) { 
      $http.get('myapi/getListOfComponents').success(function(components) { 

       var scripts = Array(); 
       var templates = Array(); 

       angular.forEach(components, function (component, key) { 
        if (component.JavaScript !== null) { 
         // This is the path to the JS file to dynamically load 
         scripts.push(widget.JavaScript); 
        } 
       }); 

       // Load the JS file for all the components 
       // I'm using the dynamic loader found here: https://github.com/ded/script.js (it's old, but it works) 
       $script(scripts, function() { 
        angular.forEach(widgets, function (widget, key) { 
         templates.push(
          // Get the raw HTML contents of the component 
          $http.get(widget.Template).success(function (componentHtmlContent) { 
           element.append('<div class="component">' + componentHtmlContent + '</div>'); 
           $compile(element.contents())(scope); 
          })); 
        }); 
       }); 
      }); 
     } 
    } 
}); 

和示例組件模板:

<div ng-controller="myComponentController"> 
    <h2>{{MyHeading}} - {{MyInlineFunction()}} - {{SomeValue}}</h2> 
    <button ng-click="MyClickFunction()">Button</button> 
</div> 

和組件的腳本

serviceWebApp.controllerProvider('myComponentController', function ($scope, $http) { 

    $scope.SomeValue = "Foo"; 

    $http.get('/myapi/getsomedata').success(function (data) { 
     $scope.MyHeading = data; 
    }); 

    $scope.MyInlineFunction = function() { 
     return "SomeInlineValue"; 
    } 

    $scope.MyClickFunction = function() { 
     alert('Something was clicked'); 
    } 
}); 

頁面加載時,調用MyInlineFunction$http.get都很好,並且所有的綁定在模板({{MyHeading}} - {{MyInlineFunction()}} - {{SomeValue}})中獲取正確的值。

另一方面,該按鈕拒絕工作。 MyClickFunction永遠不會被調用。

我也試着在瀏覽器開發者控制檯使用以下命令在該控制器的範圍的內容看:

angular.element($("div[ng-controller~='myComponentController']")).scope()

範圍返回從這個確實包含SomeValue,或範圍中定義的其他值/函數。

因此,$compile似乎只工作一次,並沒有跟蹤範圍。

這是怎麼回事,我該如何解決?

+0

的問題是,您對多個控制器在同一範圍內運行。他們可以互相覆蓋'MyClickFunction'或其他任何東西。它可能應該是'$ compile(element.contents())(scope。$ new())'。如果不是這種情況,請提供http://stackoverflow.com/help/mcve - 一個普通的會很好。 – estus

+0

達尼特!我試圖創建一個plunkr來證明這一點,但我當然可以讓它在那裏工作!但是我看不出我的實際代碼有什麼問題,但我真的不想在此分享生產代碼。第二十二條軍規。 – GTHvidsten

+0

我終於明白是什麼導致了這一點。動態添加後,我做了一些與使用jQuery的元素雜耍。這顯然與AngularJS的內部狀態不知所措,並導致組件失去與其範圍的連接。解決方案是在最底層執行'$ compile',在jQuery玩雜耍之後。現在我每次都會調用ng-click函數:)我將關閉這個問題,而不是刪除它,以供將來的訪問者使用。 – GTHvidsten

回答

0

這裏的問題是我也使用jQuery來移動DOM中的元素。當我移動一個之前已經初始化的組件時,它就失去了與範圍的連接。

我在調試時發現的另一件事是第一個組件被初始化了三次,第二次和第三次。 (或者如果有四個組件,則爲「4,3,2,1」)。這是因爲$compile(element.contents())(scope);位於foreach之內,並且每個後續組件都將被加載多次。

解決方案是將重新編譯移到所有jQuery之下,同時將其移動到所有HTML內容已承諾加載時運行的塊中。

一個工作Plunker可以在這裏看到:http://plnkr.co/edit/ZGnGXb9N3SsoIUjJx5N4?p=preview