[javascript] 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();
}
}
(허용 된 답변에서 : Khanh TO의 https://stackoverflow.com/a/19228302/605586 ).
답변
경우에 따라 작동 할 수있는 간단한 솔루션은 래퍼를 만들어 $ 컴파일 한 다음 원래 요소를 추가하는 것입니다.
뭔가 …
link: function(scope, elem, attr){
var wrapper = angular.element('<div tooltip></div>');
elem.before(wrapper);
$compile(wrapper)(scope);
wrapper.append(elem);
}
이 솔루션은 원래 요소를 다시 컴파일하지 않아도 작업을 단순하게 유지하는 이점이 있습니다.
추가 된 지시문 require
중 하나라도 원래 요소의 지시문 중 하나이거나 원래 요소에 절대 위치가 있으면 작동하지 않습니다 .