📜 ⬆️ ⬇️

AngularJS - Are you sure you know how ng-if works?


Not so long ago, I already wrote about the behavior of ng-if directives , but then I ran into checking the conditions, but today there was another problem.

There are a lot of elements like tooltip, popover, modal windows and so on in the project. I think you all understand what these elements are and I will not talk about them. For many of them, absolute positioning is used. If we didn't use custom directives, then there would be no problems - all modal windows would lie at the end of the body and would be shown when needed. But since all these elements are declared as directives, there is a problem with positioning, since a directive can have a parent with relative positioning and so on.

<div style="position: relative; overflow:hidden"> <button ng-click="visible = true">Greeting</button> <modal visible="visible"> Hello, Habr! </modal> </div> 


The modal window should be positioned relative to the browser window, but in this case will be positioned relative to the parent element.
The simplest solution to this problem is to remove an element from the current directive:
')
 module.directive('modal',[ '$rootElement', function( $rootElement ){ return { restrict: 'E', ... link: function(scope, element){ element.appendTo($rootElement); scope.$on('$destroy', function(){ element.remove(); }); ... } } }] }); 


That is, we extract the element from the current context and insert it into the root element of the application. When a directive is deleted, the element is deleted. Everything seems to be OK, but there are no problems until the ng-if directive is used together with this approach.

ng-if with a negative result of the condition completely removes the DOM element, I think many people know it, but not many know how it happens.

Here are the sources and the watcher ng-if attribute itself.
If the result is positive, the document.createComment(' end ngIf: ' + $attr.ngIf + ' '); comment document.createComment(' end ngIf: ' + $attr.ngIf + ' '); and two values ​​are put into the block.clone variable:


In the source code of the page, you most likely often see this:


In this screenshot, the ng-if="!task.id" is positive and the li element for which the directive is declared is in the DOM tree and is between the comments and. The second condition ng-if="validation.task.app_id" is negative and there is nothing between the comments.

If negative, destroy the child scope and delete items . And the most interesting thing about the getBlockElements function:
 /** * Return the DOM siblings between the first and last node in the given array. * @param {Array} array like object * @returns {DOMElement} object containing the elements */ function getBlockElements(nodes) { var startNode = nodes[0], endNode = nodes[nodes.length - 1]; if (startNode === endNode) { return jqLite(startNode); } var element = startNode; var elements = [element]; do { element = element.nextSibling; if (!element) break; elements.push(element); } while (element !== endNode); return jqLite(elements); } 

What makes this function clear from its description is the return code.
And the nodes argument in our case is an array of two elements that I described yours. That is, the function returns all elements between the main element for which the ng-if directive and the closing comment were declared, and if the comment was not found, it will return all elements after the main element.

For example, such a template (# angular-application is the root element of the application):
 <div id="angular-application"> ... <div style="position: relative; overflow: hidden"> <div style="position: absolute; right: 0; bottom: 0"> <modal ng-if="isFirstModal()" id="modal-1">...</modal> <modal ng-if="isSecondModal()" id="modal-2">...</modal> </div> <div style="position: absolute; left: 0; bottom: 0"> <popover ng-if="isFirstPopover()" id="popover-1">...</popover> <popover ng-if="isSecondPopover()" id="popover-2">...</popover> </div> </div> ... </div> 

Compile in this html:
 <div id="angular-application"> ... <div style="position: relative; overflow: hidden"> <div style="position: absolute; right: 0; bottom: 0"> <!-- ngIf: isFirstModal() --> <!-- end ngIf: isFirstModal() --> <!-- ngIf: isSecondModal() --> <!-- end ngIf: isSecondModal() --> </div> <div style="position: absolute; left: 0; bottom: 0"> <!-- ngIf: isFirstPopover() --> <!-- end ngIf: isFirstPopover() --> <!-- ngIf: isSecondPopover() --> <!-- end ngIf: isSecondPopover() --> </div> </div> ... <div id="popover-1" class="popover">...</div> <div id="modal-1" class="modal-window">...</div> <div id="modal-2" class="modal-window">...</div> <div id="popover-2" class="popover">...</div> </div> 

That is, as was written above, all the modal windows and popowers, so that the positioning is not disturbed and their layout is moved at the end of the application, but the comments remained in the same place. And now, the getBlockElements function for .- - #popover-1 , #modal-1 , #modal-2 , #popover-2 . ng-if="isFirstPopover()" DOM .

:
:) . . , , , , . - , - ; . , , , ; ng-if . . , . , - enable; . , ng-if , 600; , . : element.find('[append-to-root]').appendTo($rootElement); ng-if , :
<div ng-if="condition"> <my-custom-directive>...</my-custom-directive> </div>

Source: https://habr.com/ru/post/225891/


All Articles