Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

ngShowIf (Feature Request) #8433

Closed
Closed
@Izhaki

Description

@Izhaki

Abstract

If is often desirable to insert a directive's content upon first usage, and show/hide thereafter.

That is, have an ngIf behaviour once, and ngShow behaviour thereafter.

Current state of affairs

The two directives in question are:

  • ngIf - the directive inserts/removes the contents from the DOM. The directive also creates a new child scope upon each insertion (unfavourable behaviour at times, as it forces outside/inside communication to be based on objects, rather than primitives).
  • ngShow - the directive simply shows or hides the contents. It does not affect the scope.

The request

It would be useful to have a directive that inserts the contents first time its value turns true, and following that simply hides and show it.

Example

Consider a typeahead directive on an input field:

<input typeahead="...">

The directive reveals a dropdown with options and search matches, like so:

image

The initiation of the dropdown may be time-consuming and its existence on the DOM performance-consuming. The dropdown may:

  • Require an AJAX call to obtain the options (in case of remote search).
  • Involve watches.
  • Have to sort the returned options.
  • Each item (and depending on the programmer, there may be hundreds of them):
    • Require DOM listeners (eg, mousedown).
    • Involve a watch (eg, selected).

On a single form, there may be 10 fields with such typeahead directives. And the user may or may not open the dropdowns. When the form represents a new entry, the dropdowns are likely to be opened; but when editing/reviewing an entry, very few (if any) dropdowns will be open.

And so if one chooses:

  • ngIf - quite a bit of work will have to be done every time a dropdown opens (think AJAX requests); and due to the child scope, one has to take some extra step to ensure state preservation (eg, the query that was entered).
  • ngShow - we do a lot of work (10 typeaheds X 10 AJAX calls and many listeners and watches), which may not be needed at all.

A directive that inserts on the first show but show/hide thereafter would solve this.

Code

ngIfShow

Here's a code for and ngIfShow directive, essentially a slimed-down marrige between ngIf and ngShow. As it uses transclude the inside scope is a sibling scope (which quite a few people aren't happy with).

.directive( 'ngIfShow', ['$animate', function($animate) {
  return {
    multiElement: true,
    transclude: 'element',
    priority: 600,
    terminal: true,
    restrict: 'A',
    link: function (scope, element, attr, ctrl, transclude) {
        var rendered = false,
            iClone;
        scope.$watch(attr.ngIfShow, function ngIfShowWatchAction(value) {

          if (!rendered){
            if (value) {
              transclude(function (clone, newScope) {
                iClone = clone;
                clone[clone.length++] = document.createComment(' end ngIfShow: ' + attr.ngIfShow + ' ');

                $animate.enter(clone, element.parent(), element);
                rendered = true;
              });
            }
          } else {
            $animate[value ? 'removeClass' : 'addClass'](iClone, 'ng-hide');
          }
        });
    }
  };
}])

ngShowIf

The following directive does the same thing, but behaves more like ngShow in that it does not affect the scope:

.directive( 'ngShowIf', [ '$compile', '$animate', function( $compile, $animate ) {
    return {
        priority: 600,
        terminal: true,
        restrict: 'A',

        compile: function compile( tElement, tAttributes ) {
            var iContents = tElement.html();
            tElement.empty();

            return function postLink( scope, element, attrs, controller ) {
                var iClone = false;

                scope.$watch( attrs.ngShowIf, function ngShowIfWatchAction( doShow ) {
                    if ( !iClone ) {
                        if ( doShow ) {
                            iClone = $compile( iContents )( scope );
                            $animate.enter( iClone, element );
                        }
                    } else {
                        $animate[ doShow ? 'removeClass' : 'addClass']( iClone, 'ng-hide' );
                    }

                });
            };

        }
    };
}])

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions