2

I'm sure this has been asked before, but it's kind of hard to put into a question and, therefore, is also hard to search for...

A piece of my app uses one of several third-party directives. The directives I'm messing with, at the moment, are those in the xeditable lib, but this question isn't just about that lib, as I've come across this need before.

My code, currently, looks something like the following:

<div ng-switch on="editTypeNeeded">
    <div ng-switch-when="textarea" editable-textarea="myVar" onbeforesave="doBeforeSaveStuff($data)" class="whatever" ng-class="whatever2()" ng-mouseover="doStuff()" some-other-directive="doMoreStuff()">
        {{myvar}}
        ...
        and other stuff
    </div>
    <div ng-switch-when="textfield" editable-text="myVar" onbeforesave="doBeforeSaveStuff($data)" class="whatever" ng-class="whatever2()" ng-mouseover="doStuff()" some-other-directive="doMoreStuff()">
        {{myvar}}
        ...
        and other stuff
    </div>
    <div ng-switch-when="checkbox" editable-checkbox="myVar" onbeforesave="doBeforeSaveStuff($data)" class="whatever" ng-class="whatever2()" ng-mouseover="doStuff()" some-other-directive="doMoreStuff()">
        {{myvar}}
        ...
        and other stuff
    </div>
    <div ng-switch-when="date" editable-bsdate="myVar" onbeforesave="doBeforeSaveStuff($data)" class="whatever" ng-class="whatever2()" ng-mouseover="doStuff()" some-other-directive="doMoreStuff()">
        {{myvar}}
        ...
        and other stuff
    </div>
    ...
    etc...
</div>

That's a TON of code duplication when the only thing that changes between each switch option is "editable-textarea", "editable-text", etc.

What I would like is to replace all of the above with something like this:

<div {{directiveName}}="myVar" onbeforesave="doBeforeSaveStuff($data)" class="whatever" ng-class="whatever2()" ng-mouseover="doStuff()" some-other-directive="doMoreStuff()">
    {{myvar}}
    ...
    and other stuff
</div>

which, obviously, doesn't work, but there's got to be a way to do something like that...


Update:

I ended up using the selected answer, but with a slight modification to allow for both a variable directive name AND a directive value. Like so:

.directive('useDirective', function ($compile) {
    return {
        restrict: 'A',
        link: function (scope, elem, attrs) {
            attrs.$observe('useDirective', function(directiveNameAndValue) {
                if (directiveNameAndValue) {
                    var nameAndValue = directiveNameAndValue.split('='),
                        dirName = nameAndValue[0],
                        value = nameAndValue[1] || '';
                    elem.attr(dirName, value);
                    elem.removeAttr('use-directive');
                    $compile(elem)(scope);
                }
            });
        }
    };
});

And I use it like this:

<div use-directive="{{getDirectiveType(value)}}=value" class="pointer" onbeforesave="notifyOfChange($data)">{{value}}</div>

Plunker example: http://plnkr.co/edit/JK5TkFKA2HutGJOrMo8a?p=preview


Update 2:
I've found that the above technique doesn't seem to play nicely with the angular-animate.js lib. It causes that lib to kick up the following error a bunch of times:

 TypeError: Cannot read property 'parentNode' of undefined
     at angular-animate.js:1203
     at angular-animate.js:539
     at n.$digest (angular.js:14358)
     at n.$apply (angular.js:14571)
     at angular.js:16308
     at e (angular.js:4924)
     at angular.js:5312

though, the page still seems to work fine.

2
  • 1
    Write your own directive that embodies all the above logic? Commented Apr 2, 2015 at 0:52
  • I don't see why you couldn't stick that switch logic into the directive's template, and then just pass on or set an attribute on the directive for editTypeNeed. Keep all the HTML inside the directive, and just use the directive in your form to keep things clean. Commented Apr 2, 2015 at 0:53

1 Answer 1

4

You can create a directive to-rule-them-all:

<div lord-of-the-directives="{{directiveName}}">

Which would be like

module.directive('lordOfTheDirectives', function ($compile) {
  return {
    restrict: 'A',
    link: function (scope, elem, attrs) {
      attrs.$observe('lordOfTheDirectives', function(dirName) {
        if (dirName) {
          elem.append('<div ' + dirName + '>');
          $compile(elem)(scope);
        }
      });
    }
  };
});

(not tested but that's the idea)

Update by request in comment, to keep the element as is (with its classes and stuff):

module.directive('lordOfTheDirectives', function ($compile) {
  return {
    restrict: 'A',
    link: function (scope, elem, attrs) {
      attrs.$observe('lordOfTheDirectives', function (dirName) {
        if (dirName) {
          elem.attr(dirName, '');
          elem.removeAttr('lord-of-the-directives');
          $compile(elem)(scope);
        }
      });
    }
  };
});
Sign up to request clarification or add additional context in comments.

2 Comments

I like the idea... Is there a way to make that work when your div has a bunch of other attrs and looks more like this: <div lord-of-the-directives="{{directiveName}}" class="whatever" ng-class="whatever2()" ng-mouseover="doStuff()" some-other-directive="doMoreStuff()">{{myVar}}</div>? Or is the idea that lordOfTheDirectives doesn't take any attrs, and all of the additional attrs/directives need to be added from within lordOfTheDirectives, rather than passed in to it?
Yep, instead of appending the new directive, you can add its name directly to the element and recompile the whole thing. I have updated my answer with that method. Note that I remove the lord directive to not have both actives messing with each other: that become a one-time directive. If that's not what you want you can keep the lord-of-the-dirctives attribute and remove the previous directive before adding a new one.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.