TonyW
TonyW

Reputation: 18875

angular directive: switch between two templates dynamically

I am trying to create a directive named availableTo that can switch between two different templates depending on some message. For example, if the field is an input with the ng-model directive, I would first need to change it to read-only using the <span> tag. So far, my code can switch the view to read-only, but I cannot seem to switch it back to input:

var directive = {
      restrict: 'A',
      require: '?ngModel',
      link: linkerFn,
      replace: true
    };

function linkerFn(scope, element, attrs, ngModelCtrl) {

        var clonedElement = angular.copy(element);
        var preOuterHTML = clonedElement[0].outerHTML; //this can save the <input> field html code

        scope.$on('mode_changed', function() {
          var curUserRole = userservices.getUserRole();

          if (attrs.availableTo == curUserRole) {
                var e = $compile(preOuterHTML)(scope);
                element.replaceWith(e);
          } else {
                var template = '<span>' + ngModelCtrl.$viewValue + '</span>';
                var e = $compile(template)(scope);
                element.replaceWith(e);
          }

        }); //scope.$on
    } //linkerFn

For an input field:

  <input name="test1" class="form-control" ng-model="name" placeholder="Name 1" available-to="ADMIN"/>

I also noticed that once I change the template in the else block above, the element re-renders, and the preOuterHTML does not contain the original element html any more. This seems to be mission impossible to me, but I would like to hear some expert opinions. Thanks

Upvotes: 0

Views: 1445

Answers (1)

Daniel Beck
Daniel Beck

Reputation: 21475

element.replaceWith(e); Don't do that. In Angular, if you find yourself attempting to modify the DOM directly, you are by definition doing it wrong. You gotta sit back and let Angular do the work.

If you need to replace a directive's entire template, a fairly straightforward approach is to use ng-include with a scope variable containing the desired conditional templateUrl, e.g.

var directive = {
  // ...
  template: '<div ng-include="myTemplateUrl"></div>',
  link: function(scope, el) {
    if (/* whatever */) {
      scope.myTemplateUrl="templates/foo.html";
    } else {
      //...etc
    }
  },
};

(This does add an extra DOM node to the tree, but that's generally harmless.)

It sounds like in your case you may not need to go that far, though; a simple ng-if inside your template is probably enough to swap between your read-only <span> and <input>.

Upvotes: 3

Related Questions