1

I'm writing some pretty complex DOM manipulations, which is sure to test my ability to write very complex directives. I have not come across any examples (I think anyway) that demonstrate what I'd like to do.

Let's say I have a list of widgets, which can be of different types. These widgets need to be displayed within an <ul> element. Each type of widget can be different though, so the actual markup for each widget inside the <li> will drastically be different.

If all I needed was standard markup, I don't think it would be that difficult to implement this, but each of these widgets will in turn have to create html fragments that I want Angular to process. They could be something simple like wanting to use ng-click, but maybe something more complex like wanting to use my own custom directive too.

Ideally, I'd want to create directives for each of the widgets separately, just to separate concerns because I think the code for most of them will be quite complex in of themselves. Then, I'd probably want to have another directive check the type of the widget and create an html fragment that uses the individual widget directives. Does that make sense? If not, then I'd probably want to separate concerns in a similar way.

Is there a way to do this?

egervari
  • 22,372
  • 32
  • 121
  • 175
  • 2
    I don't see a fundamental problem with the approach you are suggesting. The exact answer of how to do this depends on the way your widgets are defined -- perhaps you could elaborate on this? In order to create a "super directive" to create the actual widgets, my guess is that `$compile` would come in handy. – Mikke Feb 28 '14 at 22:35
  • @Mikke I have actually never used `$compile` before, and while a lot of newsletters, articles, etc. talk about it, I have never actually seen an example of how to use it. I am still new to angular, so I'm a bit green when it comes to the really complex stuff. I've made about 30+ directives so far, but none of them have needed to use compile yet. My widgets are defined as json though. They are coming from the server. All of the data to construct the widget will be sent as a json object graph. – egervari Feb 28 '14 at 22:40
  • 1
    It depends on your usage. If the other directive are defined inside your own directive template it should get compiled along with your directive. If you are programmatically adding new content with directive you would need $compile. I'm using something like this to add some validation display messages: `inputElement.after($compile('Required')(scope));` – Beyers Feb 28 '14 at 22:41

1 Answers1

6

Although the problem is open to different solution, the strategy you suggest with a "super directive" which is responsible for creating the widgets, contained in some data structure, seems reasonable. As I already said in a comment, I don't see any fundamental problem with this, although streamlining it could be a challenge.

In order to illustrate the basic idea with some working code, using $compile, see this fiddle.

Assuming a scope-bound data structure

$scope.widgets = [
    {directive: 'widget-directive-one'},
    {directive: 'widget-directive-two'},
];

with template

<ul>
    <li ng-repeat="widget in widgets" super-directive="{{ widget.directive }}"></li>
</ul>

The "super directive" could compile the directives given to it like so:

angular.module('myApp').directive('superDirective', function($compile) {
    return {
        restrict: 'A',
        link: function(scope, elem, attrs) {

            // Create an element of the correct type
            var widgetElement = document.createElement(attrs.superDirective);

            // Possibly modify widgetElement here

            // Compile the element and append it to the LI element
            elem.append($compile(widgetElement)(scope));
        }
    };
});

See this directive as a proof of concept. It will probably need a lot of work to function as you want, much of it depending on design choices.

Mikke
  • 2,167
  • 16
  • 22
  • This is a lot simpler than I expected it to be. Works beautifully. Thanks! – egervari Feb 28 '14 at 23:34
  • 2
    Hehe, the first time I learned about `$compile`, I felt like a super hero and thought about this XKCD: https://xkcd.com/208/ – Mikke Feb 28 '14 at 23:53
  • 2
    It might make sense to inject and use `$document` here instead of just using `document` to facilitate unit testing. – Chad Johnson Jan 04 '15 at 09:59