|
948 | 948 | *
|
949 | 949 | * ### Double Compilation
|
950 | 950 | *
|
951 |
| - * Double compilation occurs when an already compiled part of the DOM gets compiled again. This is |
952 |
| - * not an intended use case and can lead to misbehaving directives, performance issues, and memory |
953 |
| - * leaks. |
954 |
| - * A common scenario where this happens is a directive that calls `$compile` in a directive link |
955 |
| - * function on the directive element. In the following example, a directive adds a mouseover behavior |
956 |
| - * to a button with `ngClick` on it: |
957 |
| - * |
958 |
| - * ``` |
959 |
| - angular.module('app').directive('addMouseover', function($compile) { |
960 |
| - return { |
961 |
| - link: function(scope, element, attrs) { |
962 |
| - var newEl = angular.element('<span ng-show="showHint"> My Hint</span>'); |
963 |
| - element.on('mouseenter mouseout', function() { |
964 |
| - scope.$apply('showHint = !showHint'); |
965 |
| - }); |
966 |
| -
|
967 |
| - attrs.$set('addMouseover', null); // To stop infinite compile loop |
968 |
| - element.append(newEl); |
969 |
| - $compile(element)(scope); // Double compilation |
970 |
| - } |
971 |
| - } |
972 |
| - }) |
973 |
| - ``` |
974 |
| - * At first glance, it looks like removing the original `addMouseover` attribute is all there is needed |
975 |
| - * to make this example work. |
976 |
| - * However, if the directive element or its children have other directives attached, they will be compiled and |
977 |
| - * linked again, because the compiler doesn't keep track of which directives have been assigned to which |
978 |
| - * elements. |
979 |
| - * |
980 |
| - * This can cause unpredictable behavior, e.g. `ngClick` or other event handlers will be attached |
981 |
| - * again. It can also degrade performance, as watchers for text interpolation are added twice to the scope. |
982 |
| - * |
983 |
| - * Double compilation should therefore be avoided. In the above example, only the new element should |
984 |
| - * be compiled: |
985 |
| - * |
986 |
| - * ``` |
987 |
| - angular.module('app').directive('addMouseover', function($compile) { |
988 |
| - return { |
989 |
| - link: function(scope, element, attrs) { |
990 |
| - var newEl = angular.element('<span ng-show="showHint"> My Hint</span>'); |
991 |
| - element.on('mouseenter mouseout', function() { |
992 |
| - scope.$apply('showHint = !showHint'); |
993 |
| - }); |
994 |
| -
|
995 |
| - attrs.$set('addMouseover', null); |
996 |
| - element.append(newEl); |
997 |
| - $compile(newEl)(scope); // Only compile the new element |
998 |
| - } |
999 |
| - } |
1000 |
| - }) |
1001 |
| - ``` |
1002 |
| - * |
1003 |
| - * Another scenario is adding a directive programmatically to a compiled element and then executing |
1004 |
| - * compile again. |
1005 |
| - * ```html |
1006 |
| - * <input ng-model="$ctrl.value" add-options> |
1007 |
| - * ``` |
1008 |
| - * |
1009 |
| - ``` |
1010 |
| - angular.module('app').directive('addOptions', function($compile) { |
1011 |
| - return { |
1012 |
| - link: function(scope, element, attrs) { |
1013 |
| - attrs.$set('addOptions', null) // To stop infinite compile loop |
1014 |
| - attrs.$set('ngModelOptions', '{debounce: 1000}'); |
1015 |
| - $compile(element)(scope); // Double compilation |
1016 |
| - } |
1017 |
| - } |
1018 |
| - }); |
1019 |
| - ``` |
1020 |
| - * |
1021 |
| - * In that case, it is necessary to intercept the *initial* compilation of the element: |
1022 |
| - * |
1023 |
| - * 1. Give your directive the `terminal` property and a higher priority than directives |
1024 |
| - * that should not be compiled twice. In the example, the compiler will only compile directives |
1025 |
| - * which have a priority of 100 or higher. |
1026 |
| - * 2. Inside this directive's compile function, remove the original directive attribute from the element, |
1027 |
| - * and add any other directive attributes. Removing the attribute is necessary, because otherwise the |
1028 |
| - * compilation would result in an infinite loop. |
1029 |
| - * 3. Compile the element but restrict the maximum priority, so that any already compiled directives |
1030 |
| - * are not compiled twice. |
1031 |
| - * 4. In the link function, link the compiled element with the element's scope |
1032 |
| - * |
1033 |
| - * ``` |
1034 |
| - angular.module('app').directive('addOptions', function($compile) { |
1035 |
| - return { |
1036 |
| - priority: 100, // ngModel has priority 1 |
1037 |
| - terminal: true, |
1038 |
| - template: '<input ng-model="$ctrl.value">', |
1039 |
| - compile: function(templateElement, templateAttributes) { |
1040 |
| - templateAttributes.$set('ngModelOptions', '{debounce: 1000}'); |
1041 |
| -
|
1042 |
| - // The third argument is the max priority. Only directives with priority < 100 will be compiled, |
1043 |
| - // therefore we don't need to remove the attribute |
1044 |
| - var compiled = $compile(templateElement, null, 100); |
1045 |
| -
|
1046 |
| - return function linkFn(scope) { |
1047 |
| - compiled(scope) // Link compiled element to scope |
1048 |
| - } |
1049 |
| - } |
1050 |
| - } |
1051 |
| - }); |
1052 |
| - ``` |
| 951 | + Double compilation occurs when an already compiled part of the DOM gets |
| 952 | + compiled again. This is an undesired effect and can lead to misbehaving directives, performance issues, |
| 953 | + and memory leaks. Refer to the Compiler Guide {@link guide/compiler#double-compilation-and-how-to-avoid-it |
| 954 | + section on double compilation} for an in-depth explanation and ways to avoid it. |
1053 | 955 | *
|
1054 | 956 | */
|
1055 | 957 |
|
|
0 commit comments