2

I created an Angular directive to provide a means of attaching an ng-if directive and erase the element, replacing it with its content. I think this should be much easier, possibly using transclusion, but I can't quite work it out. How should I do this?

angular.module('myApp', []).
directive('tyReplace', function () {
    return {
        restrict: 'A',
        replace: true,
        scope: { tyReplace: '@' },
        link: function (scope, element) {
            element.parent().text(scope.tyReplace);
        }
    }
});

Usage:

<td>
    <div ty-replace="{{content}}" ng-if="condition"></div>
    <ul ng-if="othercondition">
        <li ng-repeat="item in items">{{item}}</li>
    </ul>
</td>

I started adding additional display options within the <td>, but we also allow certain cells to be edited by toggling the contenteditable attribute. This approach allows me to continue providing that option.

EDIT

Very soon, I would like to be able to replace {{content}} with something more complex, such as an <input type="text" /><input type="datetime" /> for text and date controls when editing. The current solution won't work when I want more complex markup inside.

8
  • So you want the parent's (in this case the td) innerHTML to be replaced with the contents of the ty-replace variable? Commented May 13, 2014 at 18:08
  • Assuming this is working for you, it looks pretty good to me. Unless there is something you wish it did that it doesn't do ... in which case, what is it? Commented May 13, 2014 at 18:21
  • @Sam, yes, that's correct. Commented May 13, 2014 at 19:20
  • @Marc, I am still getting the hang of directives, and I have a suspicion that this could or should be simpler. In particular, having to specify the directive name in the isolate scope seems odd. I would expect a shortcut for this. (I suppose attrs in the link function is another option.) I originally tried to use <div ty-replace ng-if="">{{content}}</div>, but I couldn't get the content to lift into the parent's content. Commented May 13, 2014 at 19:23
  • Regarding your recent update: when you replace {{content}} with something more complex, how and where from will the markup be generated? Commented May 13, 2014 at 19:31

3 Answers 3

1

UPDATED

Using transclusion in your directive provides you with options for manipulating the DOM with access to the transcluded content in the compile/link function. There, you may use jqLite to overwrite the contents of the parent with the contents of the clone:

JavaScript:

angular.module('myApp', [])
.controller('MyController', function($scope){
  $scope.condition = true;
  // $scope.othercondition = true;
  $scope.items = [1, 2, 3, 4];
  $scope.obj = {
    name: ''
  };
})
.directive('myDirective', function(){
  return {
    transclude: true,
    replace: true,
    template: '<div ng-transclude></div>',
    compile: function(tElem, tAttrs, transclude) {
      return {
        pre: function(scope, element) {
          transclude(scope, function(clone){
            element.parent().empty().append(clone);
          });
        }
      }
    }
  }
});

index.html:

<td>
  <div ng-if="condition" my-directive ><input type="text" ng-model="obj.name" /></div>
  <ul ng-if="othercondition">
    <li ng-repeat="item in items">{{item}}</li>
  </ul>
</td>

Plunker Demo

Sign up to request clarification or add additional context in comments.

3 Comments

This looks pretty good. My original goal was to replace the various options within the outer div of your template with the correct contents. I think, though, that by wanting to include additional content, that your solution addresses my need. Thanks!
Last one for good measure: you can actually do this without transclusion, since your directive doesn't use a template: plnkr.co/edit/OdPLaIuL1ledtQIul79Q?p=preview No big advantage I don't think, but I worked on that a bit more in order to learn a bit more. Just thought I'd pass it along.
The difference here is that the wrapping <div> still renders. I was trying to remove the <div>. It seems replace = true doesn't work as expected in this case. I'm actually surprised by that. I am guessing it is something similar angular does in order to attach the ng-scope class. I noticed that if you add additional markup in an area where you place a raw {{something}}, that gets wrapped in a <span class="ng-scope">, as well.
0

I have created a Plunker that seems to work OK.

Is this what you are looking for in the behavior?

1 Comment

That Plunker seems to follow his original design pretty closely. I think what he has is working for him, but he's looking to see if there are alternatives that are somehow better.
0

I feel like such a goof. See this Plunker. Turns out I just wanted ng-transclude.

2 Comments

I'm not seeing what ng-transclude is doing for you in that Plunker (add and remove it from the attributes to see what I mean -- the ngIfs are doing the work). I do think that ultimately your solution will use transclusion, but the demo doesn't seem to show that.
@MarcKline, I see now that you are right. Thanks for catching that!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.