Angular JS 지시문에 대한 포스트 렌더 콜백이 있습니까?
방금 다음과 같이 요소에 추가 할 템플릿을 가져 오는 지시문을 받았습니다.
# CoffeeScript
.directive 'dashboardTable', ->
controller: lineItemIndexCtrl
templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
(scope, element, attrs) ->
element.parent('table#line_items').dataTable()
console.log 'Just to make sure this is run'
# HTML
<table id="line_items">
<tbody dashboard-table>
</tbody>
</table>
또한 DataTables라는 jQuery 플러그인을 사용하고 있습니다. 일반적인 사용법은 $ ( 'table # some_id'). dataTable ()과 같습니다. JSON 데이터를 dataTable () 호출에 전달하여 테이블 데이터를 제공하거나 이미 페이지에 데이터를 보유하고 나머지를 수행 할 수 있습니다. 나머지는 HTML 페이지에 이미 있습니다. .
그러나 문제는 DOM 준비 후 table # line_items의 dataTable ()을 호출해야한다는 것입니다. 위의 지시문은 템플리트가 지시문 요소에 추가되기 전에 dataTable () 메소드를 호출합니다. 추가 후에 함수를 호출 할 수있는 방법이 있습니까?
도와 주셔서 감사합니다!
Andy의 답변 후 업데이트 1 :
링크 메소드가 페이지에있는 모든 후에 호출되도록하고 싶습니다. 그래서 약간의 테스트를 위해 지시문을 변경했습니다.
# CoffeeScript
#angular.module(...)
.directive 'dashboardTable', ->
{
link: (scope,element,attrs) ->
console.log 'Just to make sure this gets run'
element.find('#sayboo').html('boo')
controller: lineItemIndexCtrl
template: "<div id='sayboo'></div>"
}
그리고 div # sayboo에서 "boo"를 볼 수 있습니다.
그런 다음 jquery datatable 호출을 시도하십시오.
.directive 'dashboardTable', ->
{
link: (scope,element,attrs) ->
console.log 'Just to make sure this gets run'
element.parent('table').dataTable() # NEW LINE
controller: lineItemIndexCtrl
templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
}
거기에 운이 없다
그런 다음 시간 초과를 추가하려고합니다.
.directive 'dashboardTable', ($timeout) ->
{
link: (scope,element,attrs) ->
console.log 'Just to make sure this gets run'
$timeout -> # NEW LINE
element.parent('table').dataTable()
,5000
controller: lineItemIndexCtrl
templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
}
그리고 그것은 작동합니다. 타이머가 아닌 버전의 코드에서 무엇이 잘못되었는지 궁금합니다.
두 번째 매개 변수 "delay"가 제공되지 않으면 기본 동작은 DOM이 렌더링을 완료 한 후 함수를 실행하는 것입니다. 따라서 setTimeout 대신 $ timeout을 사용하십시오.
$timeout(function () {
//DOM has finished rendering
});
나는 똑같은 문제가 있었고 대답은 실제로 아니요라고 믿습니다. Miško의 의견 과 그룹 내 토론을 참조하십시오 .
Angular는 DOM을 조작하는 데 필요한 모든 함수 호출이 완료되었음을 추적 할 수 있지만 이러한 함수는 DOM을 반환 한 후에도 여전히 DOM을 업데이트하는 비동기 논리를 트리거 할 수 있으므로 Angular는 이에 대해 알 수 없었습니다. 각도가주는 모든 콜백 수 때로는 작동하지만에 의존하는 안전하지 않을 것입니다.
우리는 setTimeout을 사용하여이 문제를 발견 적으로 해결했습니다.
(모두가 나에게 동의하는 것은 아니라는 점을 명심하십시오. 위의 링크에 대한 의견을 읽고 귀하의 생각을 확인하십시오.)
템플릿을 넣은 후에 실행되는 postLink라고도하는 'link'기능을 사용할 수 있습니다.
app.directive('myDirective', function() {
return {
link: function(scope, elm, attrs) { /*I run after template is put in */ },
template: '<b>Hello</b>'
}
});
지시어를 만들 계획이라면이 글을 읽으십시오. 큰 도움이됩니다 : http://docs.angularjs.org/guide/directive
Although my answer is not related to datatables it addresses the issue of DOM manipulation and e.g. jQuery plugin initialization for directives used on elements which have their contents updated in async manner.
Instead of implementing a timeout one could just add a watch that will listen to content changes (or even additional external triggers).
In my case I used this workaround for initializing a jQuery plugin once the ng-repeat was done which created my inner DOM - in another case I used it for just manipulating the DOM after the scope property was altered at controller. Here is how I did ...
HTML:
<div my-directive my-directive-watch="!!myContent">{{myContent}}</div>
JS:
app.directive('myDirective', [ function(){
return {
restrict : 'A',
scope : {
myDirectiveWatch : '='
},
compile : function(){
return {
post : function(scope, element, attributes){
scope.$watch('myDirectiveWatch', function(newVal, oldVal){
if (newVal !== oldVal) {
// Do stuff ...
}
});
}
}
}
}
}]);
Note: Instead of just casting the myContent variable to bool at my-directive-watch attribute one could imagine any arbitrary expression there.
Note: Isolating the scope like in the above example can only be done once per element - trying to do this with multiple directives on the same element will result in a $compile:multidir Error - see: https://docs.angularjs.org/error/$compile/multidir
May be am late to answer this question. But still someone may get benefit out of my answer.
I had similar issue and in my case I can not change the directive since, it is a library and change a code of the library is not a good practice. So what I did was use a variable to wait for page load and use ng-if inside my html to wait render the particular element.
In my controller:
$scope.render=false;
//this will fire after load the the page
angular.element(document).ready(function() {
$scope.render=true;
});
In my html (in my case html component is a canvas)
<canvas ng-if="render"> </canvas>
I had the same issue, but using Angular + DataTable with a fnDrawCallback
+ row grouping + $compiled nested directives. I placed the $timeout in my fnDrawCallback
function to fix pagination rendering.
Before example, based on row_grouping source:
var myDrawCallback = function myDrawCallbackFn(oSettings){
var nTrs = $('table#result>tbody>tr');
for(var i=0; i<nTrs.length; i++){
//1. group rows per row_grouping example
//2. $compile html templates to hook datatable into Angular lifecycle
}
}
After example:
var myDrawCallback = function myDrawCallbackFn(oSettings){
var nTrs = $('table#result>tbody>tr');
$timeout(function requiredRenderTimeoutDelay(){
for(var i=0; i<nTrs.length; i++){
//1. group rows per row_grouping example
//2. $compile html templates to hook datatable into Angular lifecycle
}
,50); //end $timeout
}
Even a short timeout delay was enough to allow Angular to render my compiled Angular directives.
None of the solutions worked for me accept from using a timeout. This is because I was using a template that was dynamically being created during the postLink.
Note however, there can be a timeout of '0' as the timeout adds the function being called to the browser's queue which will occur after the angular rendering engine as this is already in the queue.
Refer to this: http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has-finished-rendering
Here is a directive to have actions programmed after a shallow render. By shallow I mean it will evaluate after that very element rendered and that will be unrelated to when its contents get rendered. So if you need some sub element doing a post render action, you should consider using it there:
define(['angular'], function (angular) {
'use strict';
return angular.module('app.common.after-render', [])
.directive('afterRender', [ '$timeout', function($timeout) {
var def = {
restrict : 'A',
terminal : true,
transclude : false,
link : function(scope, element, attrs) {
if (attrs) { scope.$eval(attrs.afterRender) }
scope.$emit('onAfterRender')
}
};
return def;
}]);
});
then you can do:
<div after-render></div>
or with any useful expression like:
<div after-render="$emit='onAfterThisConcreteThingRendered'"></div>
I got this working with the following directive:
app.directive('datatableSetup', function () {
return { link: function (scope, elm, attrs) { elm.dataTable(); } }
});
And in the HTML:
<table class="table table-hover dataTable dataTable-columnfilter " datatable-setup="">
trouble shooting if the above doesnt work for you.
1) note that 'datatableSetup' is the equivalent of 'datatable-setup'. Angular changes the format into camel case.
2) make sure that app is defined before the directive. e.g. simple app definition and directive.
var app = angular.module('app', []);
app.directive('datatableSetup', function () {
return { link: function (scope, elm, attrs) { elm.dataTable(); } }
});
Following the fact that the load order cannot be anticipated, a simple solution can be used.
Let's look at the directive-'user of directive' relationship. Usually the user of the directive will supply some data to the directive or use some functionality ( functions ) the directive supplies. The directive on the other hand expects some variables to be defined on its scope.
If we can make sure that all players have all their action requirements fulfilled before they attempt to execute those actions - everything should be well.
And now the directive:
app.directive('aDirective', function () {
return {
scope: {
input: '=',
control: '='
},
link: function (scope, element) {
function functionThatNeedsInput(){
//use scope.input here
}
if ( scope.input){ //We already have input
functionThatNeedsInput();
} else {
scope.control.init = functionThatNeedsInput;
}
}
};
})
and now the user of the directive html
<a-directive control="control" input="input"></a-directive>
and somewhere in the controller of the component that uses the directive:
$scope.control = {};
...
$scope.input = 'some data could be async';
if ( $scope.control.functionThatNeedsInput){
$scope.control.functionThatNeedsInput();
}
That's about it. There is a lot of overhead but you can lose the $timeout. We also assume that the component that uses the directive is instantiated before the directive because we depend on the control variable to exist when the directive is instantiated.
'Programing' 카테고리의 다른 글
ASP.NET_SessionId + OWIN 쿠키는 브라우저로 보내지 않습니다 (0) | 2020.06.25 |
---|---|
[GET] / 자산과 일치하는 경로가 없습니다 (0) | 2020.06.25 |
Django Rest Framework를 사용하여 관련 모델 필드를 어떻게 포함합니까? (0) | 2020.06.25 |
그룹 객체에 변형 대 적용 (0) | 2020.06.25 |
Django TemplateDoesNotExist? (0) | 2020.06.25 |