|
952 | 952 | * not an intended use case and can lead to misbehaving directives, performance issues, and memory
|
953 | 953 | * leaks.
|
954 | 954 | * A common scenario where this happens is a directive that calls `$compile` in a directive link
|
955 |
| - * function on the directive element: |
| 955 | + * function on the directive element. In the following example, a directive adds a mouseover behavior |
| 956 | + * to a button with `ngClick` on it: |
956 | 957 | *
|
957 | 958 | * ```
|
958 |
| - angular.module('app').directive('addInput', function($compile) { |
| 959 | + angular.module('app').directive('addMouseover', function($compile) { |
959 | 960 | return {
|
960 | 961 | link: function(scope, element, attrs) {
|
961 |
| - var newEl = angular.element('<input ng-model="$ctrl.value">'); |
962 |
| - attrs.$set('addInput', null) // To stop infinite compile loop |
| 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 |
963 | 968 | element.append(newEl);
|
964 | 969 | $compile(element)(scope); // Double compilation
|
965 | 970 | }
|
966 | 971 | }
|
967 | 972 | })
|
968 | 973 | ```
|
969 |
| - * At first glance, it looks like removing the original `addInput` attribute is all there is needed |
| 974 | + * At first glance, it looks like removing the original `addMouseover` attribute is all there is needed |
970 | 975 | * to make this example work.
|
971 | 976 | * However, if the directive element or its children have other directives attached, they will be compiled and
|
972 | 977 | * linked again, because the compiler doesn't keep track of which directives have been assigned to which
|
973 | 978 | * elements.
|
974 | 979 | *
|
975 |
| - * This can cause unpredictable behavior, e.g. `ngModel` $formatters and $parsers will be |
976 |
| - * attached again to the ngModelController. It can also degrade performance, as |
977 |
| - * watchers for text interpolation are added twice to the scope. |
| 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: |
978 | 985 | *
|
979 |
| - * Double compilation should therefore avoided. In the above example, the better way is to only |
980 |
| - * compile the new element: |
981 | 986 | * ```
|
982 |
| - angular.module('app').directive('addInput', function($compile) { |
| 987 | + angular.module('app').directive('addMouseover', function($compile) { |
983 | 988 | return {
|
984 | 989 | link: function(scope, element, attrs) {
|
985 |
| - var newEl = angular.element('<input ng-model="$ctrl.value">'); |
986 |
| - $compile(newEl)(scope); // Only compile the new element |
| 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); |
987 | 996 | element.append(newEl);
|
| 997 | + $compile(newEl)(scope); // Only compile the new element |
988 | 998 | }
|
989 | 999 | }
|
990 | 1000 | })
|
|
1000 | 1010 | angular.module('app').directive('addOptions', function($compile) {
|
1001 | 1011 | return {
|
1002 | 1012 | link: function(scope, element, attrs) {
|
1003 |
| - attrs.$set('addInput', null) // To stop infinite compile loop |
| 1013 | + attrs.$set('addOptions', null) // To stop infinite compile loop |
1004 | 1014 | attrs.$set('ngModelOptions', '{debounce: 1000}');
|
1005 | 1015 | $compile(element)(scope); // Double compilation
|
1006 | 1016 | }
|
|
1010 | 1020 | *
|
1011 | 1021 | * In that case, it is necessary to intercept the *initial* compilation of the element:
|
1012 | 1022 | *
|
1013 |
| - * 1. give your directive the `terminal` property and a higher priority than directives |
| 1023 | + * 1. Give your directive the `terminal` property and a higher priority than directives |
1014 | 1024 | * that should not be compiled twice. In the example, the compiler will only compile directives
|
1015 | 1025 | * which have a priority of 100 or higher.
|
1016 |
| - * 2. inside this directive's compile function, remove the original directive attribute from the element, |
| 1026 | + * 2. Inside this directive's compile function, remove the original directive attribute from the element, |
1017 | 1027 | * and add any other directive attributes. Removing the attribute is necessary, because otherwise the
|
1018 | 1028 | * compilation would result in an infinite loop.
|
1019 |
| - * 3. compile the element but restrict the maximum priority, so that any already compiled directives |
| 1029 | + * 3. Compile the element but restrict the maximum priority, so that any already compiled directives |
1020 | 1030 | * are not compiled twice.
|
1021 |
| - * 4. in the link function, link the compiled element with the element's scope |
| 1031 | + * 4. In the link function, link the compiled element with the element's scope |
1022 | 1032 | *
|
1023 | 1033 | * ```
|
1024 | 1034 | angular.module('app').directive('addOptions', function($compile) {
|
|
1027 | 1037 | terminal: true,
|
1028 | 1038 | template: '<input ng-model="$ctrl.value">',
|
1029 | 1039 | compile: function(templateElement, templateAttributes) {
|
1030 |
| - templateAttributes.$set('addOptions', null); |
1031 | 1040 | templateAttributes.$set('ngModelOptions', '{debounce: 1000}');
|
1032 | 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 |
1033 | 1044 | var compiled = $compile(templateElement, null, 100);
|
1034 | 1045 |
|
1035 | 1046 | return function linkFn(scope) {
|
|
0 commit comments