AngularJS 컨트롤러를 동적으로로드
동적으로로드 할 수있는 컨트롤러가있는 각도 앱을 드롭해야하는 기존 페이지가 있습니다.
다음은 API 및 내가 찾은 몇 가지 관련 질문을 기반으로 수행해야 할 최선의 추측을 구현하는 스 니펫입니다.
// Make module Foo
angular.module('Foo', []);
// Bootstrap Foo
var injector = angular.bootstrap($('body'), ['Foo']);
// Make controller Ctrl in module Foo
angular.module('Foo').controller('Ctrl', function() { });
// Load an element that uses controller Ctrl
var ctrl = $('<div ng-controller="Ctrl">').appendTo('body');
// compile the new element
injector.invoke(function($compile, $rootScope) {
// the linker here throws the exception
$compile(ctrl)($rootScope);
});
JSFiddle . 이것은 실제 이벤트 체인을 단순화 한 것이며 위의 줄 사이에 다양한 비동기 호출과 사용자 입력이 있습니다.
위 코드를 실행하려고하면 $ compile에서 반환하는 링커가 다음을 던집니다 Argument 'Ctrl' is not a function, got undefined
. 부트 스트랩을 올바르게 이해했다면 반환하는 인젝터가 Foo
모듈 에 대해 알고 있어야합니다 .
대신를 사용하여 새 인젝터를 angular.injector(['ng', 'Foo'])
만들면 작동하는 것처럼 보이지만 모듈이 부트 스트랩 된 $rootScope
요소와 더 이상 동일한 범위가 아닌 새 항목 을 만듭니다 Foo
.
이 작업을 수행하는 데 올바른 기능을 사용하고 있습니까? 아니면 놓친 것이 있습니까? 저는 이것이 Angular 방식이 아니라는 것을 알고 있지만 Angular를 사용하지 않는 이전 페이지에 Angular를 사용하는 새 구성 요소를 추가해야하며 모듈을 부트 스트랩 할 때 필요할 수있는 모든 구성 요소를 알지 못합니다.
최신 정보:
결정되지 않은 시점에 페이지에 여러 컨트롤러를 추가 할 수 있어야한다는 것을 보여주기 위해 바이올린 을 업데이트했습니다 .
부트 스트랩 전에 컨트롤러에 대해 알 필요가없는 가능한 솔루션을 찾았습니다.
// 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 : 여기에 개별적으로 지정하지 않고도로드 된 모든 컨트롤러 / 지시문 / 서비스를 자동으로 등록 하는 바이올린 이 있습니다.
bootstrap ()은 ng-app처럼 AngularJS 컴파일러를 호출합니다.
// Make module Foo
angular.module('Foo', []);
// Make controller Ctrl in module Foo
angular.module('Foo').controller('Ctrl', function($scope) {
$scope.name = 'DeathCarrot' });
// Load an element that uses controller Ctrl
$('<div ng-controller="Ctrl">{{name}}</div>').appendTo('body');
// Bootstrap with Foo
angular.bootstrap($('body'), ['Foo']);
바이올린 .
나는 한 번 봐 걸릴 제안 ocLazyLoad 라이브러리 실행 시간과 또한 그들이 requireJs 또는 다른 라이브러리를 사용하여 부하에서 (기존 모듈 또는 컨트롤러, 서비스 등) 모듈을 등록합니다.
또한 angularJs 컨텍스트 외부의 자바 스크립트 함수에서 런타임에 여러 뷰를 추가하고 컨트롤러에 바인딩해야했기 때문에 여기에 제가 생각 해낸 내용이 있습니다.
<div id="mController" ng-controller="mainController">
</div>
<div id="ee">
2nd controller's view should be rendred here
</div>
이제 setCnt () 함수를 호출하면 html이 주입되고 컴파일되며 두 번째 컨트롤러에 연결됩니다.
var app = angular.module('app', []);
function setCnt() {
// Injecting the view's html
var e1 = angular.element(document.getElementById("ee"));
e1.html('<div ng-controller="ctl2">my name: {{name}}</div>');
// Compile controller 2 html
var mController = angular.element(document.getElementById("mController"));
mController.scope().activateView(e1);
}
app.controller("mainController", function($scope, $compile) {
$scope.name = "this is name 1";
$scope.activateView = function(ele) {
$compile(ele.contents())($scope);
$scope.$apply();
};
});
app.controller("ctl2", function($scope) {
$scope.name = "this is name 2";
});
다음은이를 테스트하는 예제입니다. http://refork.com/x4bc
도움이 되었기를 바랍니다.
한 번의 호출로 모든 작업을 수행 할 수 있도록 Jussi-Kosunen이 작성한 기능을 방금 개선했습니다.
function registerController(moduleName, controllerName, template, container) {
// Load html file with content that uses Ctrl controller
$(template).appendTo(container);
// 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]);
}
}
angular.injector(['ng', 'Foo']).invoke(function($compile, $rootScope) {
$compile($('#ctrl'+controllerName))($rootScope);
$rootScope.$apply();
});
}
이렇게하면 어디에서나 템플릿을로드 할 수 있고 중첩 된 경우에도 프로그래밍 방식으로 컨트롤러를 인스턴스화 할 수 있습니다.
다음은 다른 컨트롤러 내부에 컨트롤러를로드하는 작업 예제입니다. http://plnkr.co/edit/x3G38bi7iqtXKSDE09pN
config 및 ui-router를 사용하지 않는 이유는 무엇입니까?
런타임에로드되며 컨트롤러를 html 코드로 표시 할 필요가 없습니다.
예를 들어 다음과 같은
var config = {
config: function(){
mainApp.config(function ($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise("/");
$stateProvider
.state('index',{
views:{
'main':{
controller: 'PublicController',
templateUrl: 'templates/public-index.html'
}
}
})
.state('public',{
url: '/',
parent: 'index',
views: {
'logo' : {templateUrl:'modules/header/views/logo.html'},
'title':{
controller: 'HeaderController',
templateUrl: 'modules/header/views/title.html'
},
'topmenu': {
controller: 'TopMenuController',
templateUrl: 'modules/header/views/topmenu.html'
},
'apartments': {
controller: 'FreeAptController',
templateUrl:'modules/free_apt/views/apartments.html'
},
'appointments': {
controller: 'AppointmentsController',
templateUrl:'modules/appointments/views/frm_appointments.html'
},
}
})
.state('inside',{
views:{
'main':{
controller: 'InsideController',
templateUrl: 'templates/inside-index.html'
},
},
resolve: {
factory:checkRouting
}
})
.state('logged', {
url:'/inside',
parent: 'inside',
views:{
'logo': {templateUrl: 'modules/inside/views/logo.html'},
'title':{templateUrl:'modules/inside/views/title.html'},
'topmenu': {
// controller: 'InsideTopMenuController',
templateUrl: 'modules/inside/views/topmenu.html'
},
'messages': {
controller: 'MessagesController',
templateUrl: 'modules/inside/modules/messages/views/initial-view-messages.html'
},
'requests': {
//controller: 'RequestsController',
//templateUrl: 'modules/inside/modules/requests/views/initial-view-requests.html'
},
}
})
});
},
};
이것이 제가 한 것입니다. 실제로 두 부분으로 ng-controller와 범위 정의 함수를 사용한 다음 $ controller 서비스를 사용하여 동적 컨트롤러를 만듭니다.
첫째, HTML-동적 컨트롤러를 인스턴스화 할 정적 컨트롤러가 필요합니다.
<div ng-controller='staticCtrl'>
<div ng-controller='dynamicCtrl'>
{{ dynamicStuff }}
</div>
</div>
정적 컨트롤러 'staticCtrl'은 동적 컨트롤러를 만들기 위해 호출되는 'dynamicCtrl'이라는 범위 멤버를 정의합니다. ng-controller는 이름으로 미리 정의 된 컨트롤러를 사용하거나 동일한 이름의 기능에 대한 현재 범위를 찾습니다.
.controller('staticCtrl', ['$scope', '$controller', function($scope, $controller) {
$scope.dynamicCtrl = function() {
var fn = eval('(function ($scope, $rootScope) { alert("I am dynamic, my $scope.$id = " + $scope.$id + ", $rootScope.$id = " + $rootScope.$id); })');
return $controller(fn, { $scope: $scope.$new() }).constructor;
}
}])
We use eval() to take a string (our dynamic code which can come from anywhere) and then the $controller service which will take either a predefined controller name (normal case) or a function constructor followed by constructor parameters (we pass in a new scope) - Angular will inject (like any controller) into the function, we are requesting just $scope and $rootScope above.
'use strict';
var mainApp = angular.module('mainApp', [
'ui.router',
'ui.bootstrap',
'ui.grid',
'ui.grid.edit',
'ngAnimate',
'headerModule',
'galleryModule',
'appointmentsModule',
]);
(function(){
var App = {
setControllers: mainApp.controller(controllers),
config: config.config(),
factories: {
authFactory: factories.auth(),
signupFactory: factories.signup(),
someRequestFactory: factories.saveSomeRequest(),
},
controllers: {
LoginController: controllers.userLogin(),
SignupController: controllers.signup(),
WhateverController: controllers.doWhatever(),
},
directives: {
signup: directives.signup(), // add new user
openLogin: directives.openLogin(), // opens login window
closeModal: directives.modalClose(), // close modal window
ngFileSelect: directives.fileSelect(),
ngFileDropAvailable: directives.fileDropAvailable(),
ngFileDrop: directives.fileDrop()
},
services: {
$upload: services.uploadFiles(),
}
};
})();
The above code is only an example.
This way you don't need to put ng-controller="someController"
anywhere on a page — you only declare <body ng-app="mainApp">
Same structure can be used for each module or modules inside modules
참고URL : https://stackoverflow.com/questions/15250644/loading-an-angularjs-controller-dynamically
'Program Tip' 카테고리의 다른 글
PHP의 "easter egg"URL을 비활성화하려면 어떻게해야합니까? (0) | 2020.11.21 |
---|---|
Angular의 $ q.reject () 대 deferred.reject () (0) | 2020.11.21 |
HTML 다중 선택 상자 (0) | 2020.11.21 |
런처에서 아이콘을 눌러 시작하면 앱이 완전히 다시 시작됩니다. (0) | 2020.11.21 |
TypeScript 제네릭에 대해 여러 유형 제약 조건을 지정할 수 있습니까? (0) | 2020.11.21 |