Search code examples
javascriptangularjshtml-parsingdom-manipulationangular-directive

Angularjs directive parse contents for variables


I'm trying to build an angular directive that can be used to control grid layouts.

The directive is the tag, but what I'd like to be able to do is parse the DOM elements inside of this tag and create a nested array of rows and column widths. Columns could contain further rows and columns so I would like to be able to parse these.

In this example the classes I would like to select by are:

.row .six .five

I'm not entirely sure how to past the HTML / DOM into the directive for manipulation. I have tried using $compile and a pre function but didn't make much progress.

<grid>
    <div class="ui row">
        <div class="ui six wide column" >lorem</div>
        <div class="ui five wide column" >ipsum</div>
    </div>
    <div class="ui row" >
        <div class="ui five wide column" >lorem</div>
        <div class="ui seven wide column" >ipsum</div>
    </div>
</grid>

Solution

  • Nested directives are just the thing.

    <grid>
        <div grid-row class="ui row">
            <div grid-col class="ui six wide column" >lorem</div>
            <div grid-col class="ui five wide column" >ipsum</div>
        </div>
        <div grid-row class="ui row" >
            <div grid-col class="ui five wide column" >lorem</div>
            <div grid-col class="ui seven wide column" >ipsum</div>
        </div>
    </grid>
    

    Here, we've introduced two new directives, grid-row and grid-col. You will want to attach to your grid directive definition a controller property. This is the common point in which your child directives will communicate. Then, you'll define your children like so:

    .directive('gridRow', function() {
      return {
        restrict: 'AEC',
        require: '^grid',
        link: function(s, e, a, ctrl) {
          // ctrl is the controller you defined at the grid level.
        } 
      }
    });
    

    Notice how we've specified, via require, that there will be a parent element somewhere up the tree called grid. This will give you an additional parameter in your link function that will let you get at your parent's controller.

    Do another one for gridCol.

    The nice thing about doing things this way is that you are leveraging angular's parser, and you are automatically hooking up the children in a pretty dynamic way. You can also continue nesting these items deeper and deeper. Furthermore, if elements start appearing that aren't part of your grid-col/grid-row system, you can handle them gracefully.

    Advice: If you start nesting these things low enough, you'll probably wonder how a child knows where in the hierarchy to attach itself. Since each child can create a scope for its own descendants, one way to do this is through the $scope of the link function:

    link: function(scope, e, a, ctrl) {
      var thisParent = scope.parentNode;
      if (!thisParent) {
        scope.parentNode = ctrl.addChild(scope);
      } else {
        scope.parentNode = thisParent.addChild(scope);
      }
    }
    

    Because of the way prototype inheritance works in java, each child will obscure the preceding definition of parentNode. I assign the parentNode to a thisParent as a little semantic trick. This lets us get at the original value after we've shadowed it with our own.

    All in all, there is not much code to do what you want to do, but there is a rich subset of angular ideas going on in this solution.