AngularJS에서 지시문에서 지시문 추가
선언 된 요소에 지시문 을 더 추가 하는 지시문을 작성하려고합니다 . 예를 들어 datepicker
, datepicker-language
및 을 추가하는 지시문을 작성하려고합니다 ng-required="true"
.
해당 속성을 추가하고 사용하려고하면 $compile
분명히 무한 루프를 생성하므로 필요한 속성을 이미 추가했는지 확인하고 있습니다.
angular.module('app')
.directive('superDirective', function ($compile, $injector) {
return {
restrict: 'A',
replace: true,
link: function compile(scope, element, attrs) {
if (element.attr('datepicker')) { // check
return;
}
element.attr('datepicker', 'someValue');
element.attr('datepicker-language', 'en');
// some more
$compile(element)(scope);
}
};
});
물론 $compile
요소 가 없으면 속성이 설정되지만 지시문은 부트 스트랩되지 않습니다.
이 접근법이 맞습니까? 아니면 잘못하고 있습니까? 같은 행동을하는 더 좋은 방법이 있습니까?
UDPATE : $compile
이것을 달성하는 유일한 방법 이라는 사실을 감안할 때 첫 번째 컴파일 패스를 건너 뛸 수있는 방법이 있습니까 (요소에는 여러 자식이 포함될 수 있음)? 어쩌면 terminal:true
?
업데이트 2 : 지시문을 select
요소에 넣으려고했는데 예상대로 컴파일이 두 번 실행됩니다. 즉, 예상 횟수가 두 배 option
입니다.
단일 DOM 요소에 여러 지시문이 있고 적용되는 순서가 중요한 경우이 priority
속성을 사용하여 응용 프로그램을 주문할 수 있습니다 . 높은 숫자가 먼저 실행됩니다. 지정하지 않으면 기본 우선 순위는 0입니다.
편집 : 토론 후 완벽한 작업 솔루션이 있습니다. 열쇠는 것이었다 속성을 제거 : element.removeAttr("common-things");
또한, 및 element.removeAttr("data-common-things");
(경우에 사용자 지정 data-common-things
html로에서)
angular.module('app')
.directive('commonThings', function ($compile) {
return {
restrict: 'A',
replace: false,
terminal: true, //this setting is important, see explanation below
priority: 1000, //this setting is important, see explanation below
compile: function compile(element, attrs) {
element.attr('tooltip', '{{dt()}}');
element.attr('tooltip-placement', 'bottom');
element.removeAttr("common-things"); //remove the attribute to avoid indefinite loop
element.removeAttr("data-common-things"); //also remove the same attribute with data- prefix in case users specify data-common-things in the html
return {
pre: function preLink(scope, iElement, iAttrs, controller) { },
post: function postLink(scope, iElement, iAttrs, controller) {
$compile(iElement)(scope);
}
};
}
};
});
작업 플 런커는 http://plnkr.co/edit/Q13bUt?p=preview에 있습니다.
또는:
angular.module('app')
.directive('commonThings', function ($compile) {
return {
restrict: 'A',
replace: false,
terminal: true,
priority: 1000,
link: function link(scope,element, attrs) {
element.attr('tooltip', '{{dt()}}');
element.attr('tooltip-placement', 'bottom');
element.removeAttr("common-things"); //remove the attribute to avoid indefinite loop
element.removeAttr("data-common-things"); //also remove the same attribute with data- prefix in case users specify data-common-things in the html
$compile(element)(scope);
}
};
});
우리가 설정해야 할 이유를 설명 terminal: true
하고 priority: 1000
(높은 숫자) :
DOM이 준비되면 angular는 DOM을 따라 등록 된 모든 지시문을 식별하고 priority
이러한 지시문이 동일한 요소 에 있는지 여부에 따라 지시문을 하나씩 컴파일합니다 . 우리는 컴파일 될 수 있도록하기 위해 높은 숫자로 우리의 사용자 정의 지침의 우선 순위를 설정 첫번째 와 함께 terminal: true
, 다른 지침이 될 것이다 생략 이 지시어는 컴파일 후.
사용자 지정 지시문이 컴파일되면 지시문을 추가하고 자체를 제거하여 요소를 수정하고 $ compile 서비스를 사용하여 모든 지시문 (건너 뛴 지시문 포함) 을 컴파일합니다 .
우리가 설정하지 않은 경우 terminal:true
와 priority: 1000
, 어떤 지시어를 컴파일하는 기회가 되기 전에 우리의 사용자 정의 지시어. 그리고 우리의 커스텀 지시어가 $ compile을 사용하여 요소를 컴파일 할 때 => 이미 컴파일 된 지시문을 다시 컴파일하십시오. 이는 특히 사용자 지정 지시문 전에 컴파일 된 지시문이 이미 DOM을 변환 한 경우 예측할 수없는 동작을 유발합니다.
우선 순위 및 터미널에 대한 자세한 정보 는 지시문의 '터미널'을 이해하는 방법을 참조 하십시오 .
템플릿을 수정하는 지시문의 예 는 컴파일 ng-repeat
시 다른 지시문이 적용되기 전에 템플리트 요소의 사본을 작성하는 (priority = 1000) 입니다.ng-repeat
ng-repeat
@Izhaki의 의견 덕분에 다음은 ngRepeat
소스 코드에 대한 참조입니다 . https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js
간단한 템플릿 태그만으로이 모든 것을 실제로 처리 할 수 있습니다. 예는 http://jsfiddle.net/m4ve9/ 를 참조하십시오 . 실제로 슈퍼 지시문 정의에는 컴파일 또는 링크 속성이 필요하지 않습니다.
컴파일 과정에서 Angular는 컴파일하기 전에 템플릿 값을 가져 오므로 추가 지시문을 첨부 할 수 있으며 Angular가이를 처리합니다.
이것이 원래 내부 컨텐츠를 보존해야하는 수퍼 지시어 인 경우 내부를 사용 transclude : true
하고 바꿀 수 있습니다.<ng-transclude></ng-transclude>
도움이 되길 바랍니다 희망이 있다면 알려주세요
알렉스
다음은 동적으로 추가해야하는 지시문을보기로 이동하고 선택적 (기본) 조건부 논리를 추가하는 솔루션입니다. 이것은 하드 코딩 된 로직없이 지시문을 깨끗하게 유지합니다.
지시문은 객체의 배열을 취합니다. 각 객체에는 추가 할 지시문의 이름과 전달할 값 (있는 경우)이 포함됩니다.
조건에 따라 지시문을 추가하는 조건부 논리를 추가하는 것이 유용 할 것이라고 생각할 때까지 지시문에 대한 유스 케이스를 생각하는 데 어려움을 겪고있었습니다 (아래 답변은 여전히 유의됩니다). if
지시어 추가 여부를 결정하는 부울 값, 표현식 또는 함수 (예 : 컨트롤러에 정의)를 포함해야하는 선택적 속성을 추가했습니다 .
또한 사용하고 attrs.$attr.dynamicDirectives
지시어를 (예를 들어 추가하는 데 사용되는 정확한 속성 선언 얻기 위해 data-dynamic-directive
, dynamic-directive
확인하기 위해 하드 코딩 문자열 값없이)를.
angular.module('plunker', ['ui.bootstrap'])
.controller('DatepickerDemoCtrl', ['$scope',
function($scope) {
$scope.dt = function() {
return new Date();
};
$scope.selects = [1, 2, 3, 4];
$scope.el = 2;
// For use with our dynamic-directive
$scope.selectIsRequired = true;
$scope.addTooltip = function() {
return true;
};
}
])
.directive('dynamicDirectives', ['$compile',
function($compile) {
var addDirectiveToElement = function(scope, element, dir) {
var propName;
if (dir.if) {
propName = Object.keys(dir)[1];
var addDirective = scope.$eval(dir.if);
if (addDirective) {
element.attr(propName, dir[propName]);
}
} else { // No condition, just add directive
propName = Object.keys(dir)[0];
element.attr(propName, dir[propName]);
}
};
var linker = function(scope, element, attrs) {
var directives = scope.$eval(attrs.dynamicDirectives);
if (!directives || !angular.isArray(directives)) {
return $compile(element)(scope);
}
// Add all directives in the array
angular.forEach(directives, function(dir){
addDirectiveToElement(scope, element, dir);
});
// Remove attribute used to add this directive
element.removeAttr(attrs.$attr.dynamicDirectives);
// Compile element to run other directives
$compile(element)(scope);
};
return {
priority: 1001, // Run before other directives e.g. ng-repeat
terminal: true, // Stop other directives running
link: linker
};
}
]);
<!doctype html>
<html ng-app="plunker">
<head>
<script src="//code.angularjs.org/1.2.20/angular.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.6.0.js"></script>
<script src="example.js"></script>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
</head>
<body>
<div data-ng-controller="DatepickerDemoCtrl">
<select data-ng-options="s for s in selects" data-ng-model="el"
data-dynamic-directives="[
{ 'if' : 'selectIsRequired', 'ng-required' : '{{selectIsRequired}}' },
{ 'tooltip-placement' : 'bottom' },
{ 'if' : 'addTooltip()', 'tooltip' : '{{ dt() }}' }
]">
<option value=""></option>
</select>
</div>
</body>
</html>
수락 된 솔루션이 저에게 효과적이지 않기 때문에 솔루션을 추가하고 싶었습니다.
지시어를 추가해야했지만 요소를 유지해야했습니다.
이 예제에서는 간단한 ng 스타일 지시문을 요소에 추가합니다. 무한 컴파일 루프를 방지하고 지시문을 유지할 수 있도록 요소를 다시 컴파일하기 전에 추가 한 항목이 있는지 확인하는 검사를 추가했습니다.
angular.module('some.directive', [])
.directive('someDirective', ['$compile',function($compile){
return {
priority: 1001,
controller: ['$scope', '$element', '$attrs', '$transclude' ,function($scope, $element, $attrs, $transclude) {
// controller code here
}],
compile: function(element, attributes){
var compile = false;
//check to see if the target directive was already added
if(!element.attr('ng-style')){
//add the target directive
element.attr('ng-style', "{'width':'200px'}");
compile = true;
}
return {
pre: function preLink(scope, iElement, iAttrs, controller) { },
post: function postLink(scope, iElement, iAttrs, controller) {
if(compile){
$compile(iElement)(scope);
}
}
};
}
};
}]);
다음과 같은 요소 자체의 속성에 상태를 저장하십시오. superDirectiveStatus="true"
예를 들면 다음과 같습니다.
angular.module('app')
.directive('superDirective', function ($compile, $injector) {
return {
restrict: 'A',
replace: true,
link: function compile(scope, element, attrs) {
if (element.attr('datepicker')) { // check
return;
}
var status = element.attr('superDirectiveStatus');
if( status !== "true" ){
element.attr('datepicker', 'someValue');
element.attr('datepicker-language', 'en');
// some more
element.attr('superDirectiveStatus','true');
$compile(element)(scope);
}
}
};
});
이것이 도움이되기를 바랍니다.
1.3.x에서 1.4.x로 변경되었습니다.
Angular 1.3.x에서는 다음과 같이 작동했습니다.
var dir: ng.IDirective = {
restrict: "A",
require: ["select", "ngModel"],
compile: compile,
};
function compile(tElement: ng.IAugmentedJQuery, tAttrs, transclude) {
tElement.append("<option value=''>--- Kein ---</option>");
return function postLink(scope: DirectiveScope, element: ng.IAugmentedJQuery, attributes: ng.IAttributes) {
attributes["ngOptions"] = "a.ID as a.Bezeichnung for a in akademischetitel";
scope.akademischetitel = AkademischerTitel.query();
}
}
이제 Angular 1.4.x에서는 다음을 수행해야합니다.
var dir: ng.IDirective = {
restrict: "A",
compile: compile,
terminal: true,
priority: 10,
};
function compile(tElement: ng.IAugmentedJQuery, tAttrs, transclude) {
tElement.append("<option value=''>--- Kein ---</option>");
tElement.removeAttr("tq-akademischer-titel-select");
tElement.attr("ng-options", "a.ID as a.Bezeichnung for a in akademischetitel");
return function postLink(scope: DirectiveScope, element: ng.IAugmentedJQuery, attributes: ng.IAttributes) {
$compile(element)(scope);
scope.akademischetitel = AkademischerTitel.query();
}
}
(From the accepted answer: https://stackoverflow.com/a/19228302/605586 from Khanh TO).
A simple solution that could work in some cases is to create and $compile a wrapper and then append your original element to it.
Something like...
link: function(scope, elem, attr){
var wrapper = angular.element('<div tooltip></div>');
elem.before(wrapper);
$compile(wrapper)(scope);
wrapper.append(elem);
}
This solution has the advantage that it keeps things simple by not recompiling the original element.
This wouldn't work if any of the added directive's require
any of the original element's directives or if the original element has absolute positioning.
참고URL : https://stackoverflow.com/questions/19224028/add-directives-from-directive-in-angularjs
'Programming' 카테고리의 다른 글
HTML 양식 중첩 제한을 어떻게 극복합니까? (0) | 2020.05.10 |
---|---|
mongoDB보다 Redis가 얼마나 빠릅니까? (0) | 2020.05.10 |
helper와 helper_method는 무엇을합니까? (0) | 2020.05.10 |
간단한 파이썬 루프를 어떻게 병렬화합니까? (0) | 2020.05.10 |
Java 스트림을 1 및 1 요소로 필터링 (0) | 2020.05.09 |