[英]Inject new angular js controller with html dynamically after page loaded
我一直在研究一個角度js站點,它從webservice / api獲取數據。 一個api返回html和angular js代碼以動態添加控制器或我們想要添加新的任何新的角度組件。 這個字符串將以api響應
<div id="homecontainer" class="flex-center p-page" loader style="overflow:hidden;">
<div class="column-1">
<div class="grid m-0 col-xs-12">
<div ng-repeat="Widget in V3Widgets track by $index" class="grid-item">
<div class="grid-sizer"></div>
{{Widget}}
</div>
</div>
<div ng-controller="WelcomeController">
{{greeting}}
</div>
<script>
var app = angular.module('demo', [])
//RestService is Other Module Which is Already Working fine
.controller('WelcomeController', function ($scope,RestService) {
$scope.greeting = 'Welcome!';
});
angular.bootstrap(document, ['demo']);
</script>
</div>
</div>
現在我有一個指令將此字符串綁定到頁面
<renderdynamicwidgethtml ng-if="Widget.Id==null && Widget.Html!=null" html="Widget.Html"/>
指令的js
.directive('renderdynamicwidgethtml', ['$compile', function ($compile) {
return function (scope, element, attrs) {
scope.$watch(
function (scope) {
return scope.$eval(attrs.html);
},
function (value) {
element.html(value);
$compile(element.contents())(scope);
}
);
};
}])
scope.$eval
應該將字符串轉換為角度組件,但它失敗並出現此錯誤。
[ng:btstrpd] http://errors.angularjs.org/1.3.17/ng/btstrpd?p0=document
從Angular 1.5開始,有一個新的
angular.component
工具。 這個答案使用單詞Component來引用Angular提供的所有可能的工具(如指令,過濾器等),包括angular.component
。
首先讓我從AngularJS設置的方式開始:
當加載javascript文件時,你可以看到所有的角度代碼都以angular.module
, angular.controller
, angular.directive
等angular.directive
。所有那些都接收一個函數作為參數( angular.module
除外,它接收一個名字和依賴項列表)。 此時,這些組件未創建,只是注冊。
一旦角已注冊的所有的模塊和組件,它已准備好被自舉 ,或者與ng-app
指令或與手動angular.bootstrap
。 兩種方法都接收一個字符串作為參數,這是根模塊的名稱。 使用該根模塊,Angular深入了解它的依賴關系(可以看作依賴關系樹),並開始將組件從它的葉子(沒有依賴關系的模塊)加載到根模塊的組件。
對於每個模塊,Angular按照一定的順序構建它們,從常量開始,然后是提供者,然后按照它們注冊的順序運行配置塊,然后加載值,然后是工廠/服務,然后是指令,最后按照它們的順序運行塊。注冊(我不完全確定訂單,我建議你仔細檢查)
最后,在完成所有設置后,它會“密封”應用程序,因此無法注冊任何其他內容。
該錯誤意味着您要兩次引導您的應用程序。 當使用ng-app
指令並調用angular.bootstrap
,或者只是angular.bootstrap
調用angular.bootstrap
,就會發生這種情況。
正如文檔在手動初始化部分所述:
在加載或定義模塊后,應調用
angular.bootstrap()
。 在應用程序引導后,您無法添加控制器,服務,指令等。
因此,為了動態加載新控制器,您應該在引導過程之前注冊它們。 你可以通過從API中檢索所需的數據來實現這一點(你需要任何其他工具,比如Jular,因為還沒有設置Angular),使用JavaScript的標准eval
函數執行代碼(在請求的承諾中執行此操作),然后使用angular.bootstrap
手動引導AngularJS。
為了實現這一點,使用eval
執行的代碼應該是純粹的javascript,我建議通過更改API的響應(如果可能)或者在獲得響應后以編程方式將其與HTML分開。
此外,如果您需要多次執行此操作,請確保在注冊所需的所有組件之前,使用eval
執行的代碼不會引導Angular。
為什么使用scope.$eval
而不是$compile
服務,用於編譯模板字符串。
link: function (scope, ele, attrs) {
scope.$watch(attrs.html, function(html) {
$compile(ele.contents())(scope);
});
}
試試這個:
app.directive('dynamic', [ '$compile',
function ($compile) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.dynamic, function (html) {
ele.html(html);
$compile(ele.contents())(scope);
});
}
};
}]);
您可以將整個字符串放在js中的$scope.myString
中,並放在HTML <div dynamic="myString"></div>
。 它應該編譯並呈現一切。
我找到了一個可能的解決方案,在引導之前我不需要知道控制器:
// Make module Foo and store $controllerProvider in a global
var controllerProvider = null;
angular.module('Foo', [], function($controllerProvider) {
controllerProvider = $controllerProvider;
});
// Bootstrap Foo
angular.bootstrap($('body'), ['Foo']);
// .. time passes ..
// Load javascript file with Ctrl controller
angular.module('Foo').controller('Ctrl', function($scope, $rootScope) {
$scope.msg = "It works! rootScope is " + $rootScope.$id +
", should be " + $('body').scope().$id;
});
// Load html file with content that uses Ctrl controller
$('<div id="ctrl" ng-controller="Ctrl" ng-bind="msg">').appendTo('body');
// Register Ctrl controller manually
// If you can reference the controller function directly, just run:
// $controllerProvider.register(controllerName, controllerFunction);
// Note: I haven't found a way to get $controllerProvider at this stage
// so I keep a reference from when I ran my module config
function registerController(moduleName, controllerName) {
// Here I cannot get the controller function directly so I
// need to loop through the module's _invokeQueue to get it
var queue = angular.module(moduleName)._invokeQueue;
for(var i=0;i<queue.length;i++) {
var call = queue[i];
if(call[0] == "$controllerProvider" &&
call[1] == "register" &&
call[2][0] == controllerName) {
controllerProvider.register(controllerName, call[2][1]);
}
}
}
registerController("Foo", "Ctrl");
// compile the new element
$('body').injector().invoke(function($compile, $rootScope) {
$compile($('#ctrl'))($rootScope);
$rootScope.$apply();
});
小提琴 。 唯一的問題是你需要存儲$ controllerProvider並在不應該使用它的地方使用它(在引導程序之后)。 在注冊之前,似乎沒有一種簡單的方法可以獲得用於定義控制器的函數,因此我需要遍歷模塊的_invokeQueue,這是未記錄的。
更新:要注冊指令和服務,而不是$ controllerProvider.register,只需分別使用$ compileProvider.directive和$ provide.factory。 同樣,您需要在初始模塊配置中保存對這些的引用。
UDPATE 2:這是一個小提琴,可以自動注冊所有加載的控制器/指令/服務,而無需單獨指定它們。
您可以將javascript eval
用於eval js代碼,將$compile
用於html代碼。 我已經設置了一個帶有完整測試代碼的plunker。 鏈接在這里。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.