Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

wrapping inputs in directives in angular

I had the idea to wrap inputs into custom directives to guarantee a consistent look and behavior through out my site. I also want to wrap bootstrap ui's datepicker and dropdown. Also, the directive should handle validation and display tooltips.

The HTML should look something like this:

<my-input required max-length='5' model='text' placeholder='text' name='text'/>

or

<my-datepicker required model='start' placeholder='start' name='start'/>

in the directives i want to create a dom structure like:

<div>
 <div>..</div> //display validation in here
 <div>..</div> //add button to toggle datepicker (or other stuff) in here
 <div>..</div> //add input field in here
</div>

I tried various ways to achieve this but always came across some tradeoffs:

  1. using transclude and replace to insert the input into the directives dom structure (in this case the directive would be restricted to 'A' not 'E' like in the example above). The problem here is, that there is no easy way to access the transcluded element as I want to add custom attributes in case of datepicker. I could use the transclude function and then recompile the template in the link function, but this seems a bit complex for this task. This also leads to problems with the transcluded scope and the toggle state for the datepicker (one is in the directives scope, the other in the transcluded scope).

  2. using replace only. In this case, all attributes are applied to the outermost div (even if I generate the template dom structure in the compile function). If I use just the input as template, then the attributes are on the input, but I need to generate the template in the link function an then recompile it. As far as I understand the phase model of angular, I would like to avoid recompiling and changing the template dom in the link function (although I've seen many people doing this).

Currently I'm working with the second approach and generating the template in the link function, but I was wondering if someone had some better ideas!

like image 915
roemer Avatar asked Dec 18 '13 08:12

roemer


People also ask

Can a directive have input?

If we also specify an input property in our directive's class using the same value as the selector property value we can input data into our directive using the attribute on the host element. This instructs Angular that the appBtnGrow class member property is now an input that can receive a value from the host element.

What are different types of directives in Angular?

The three types of directives in Angular are attribute directives, structural directives, and components.


1 Answers

Here's what I believe is the proper way to do this. Like the OP I wanted to be able to use an attribute directive to wrapper an input. But I also wanted it to work with ng-if and such without leaking any elements. As @jantimon pointed out, if you don't cleanup your wrapper elements they will linger after ng-if destroys the original element.

app.directive("checkboxWrapper", [function() {
    return {
      restrict: "A",
      link: function(scope, element, attrs, ctrl, transclude) {
        var wrapper = angular.element('<div class="wrapper">This input is wrappered</div>');

        element.after(wrapper);
        wrapper.prepend(element);

        scope.$on("$destroy", function() {
          wrapper.after(element);
          wrapper.remove();
        });
      }
    };
  }
]);

And here's a plunker you can play with.

IMPORTANT: scope vs element $destroy. You must put your cleanup in scope.$on("$destroy") and not in element.on("$destroy") (which is what I was originally attempting). If you do it in the latter (element) then an "ngIf end" comment tag will get leaked. This is due to how Angular's ngIf goes about cleaning up its end comment tag when it does its falsey logic. By putting your directive's cleanup code in the scope $destroy you can put the DOM back like it was before you wrappered the input and so ng-if's cleanup code is happy. By the time element.on("$destroy") is called, it is too late in the ng-if falsey flow to unwrap the original element without causing a comment tag leak.

like image 140
BrandonLWhite Avatar answered Oct 09 '22 09:10

BrandonLWhite