1

I'm relatively new to creating custom Angular directives, and I'm trying to replace a page in my application which is a series of JQuery components which get nested inside one another.

I'm wanting to create a set of custom Angular directives, which I can nest within each other allowing the user to build up a kind of form, whilst allowing them to delete and re-add any directives nested within one another if they need to.

I'm not sure how dynamically insert one directive into another, based on a user's choice, and how to set this within the directive so that a given directive's child can be deleted, re-added and then recompiled.

So far the only (unsophisticated) method I have come up with is to 2-way bind to an attribute on the directive's isolate scope, and have this determine the inner content of the directive like so, however upon changing this attribute object on the parent scope, the directive's DOM doesn't recompile, so I'm convinced there is a much better way to do this.

I'm assuming I might need to use transclusion or the link function in some way, so any guidance on this is much appreciated!

Current attempt:

app.directive("testCustomMapperThings", function($compile) {
    var testTemplate1 = '<div>THIS IS FIRST THE DYNAMICALLY COMPILED TEST TEMPLATE 1</div>';
    var testTemplate2 = '<div>THIS IS SECOND THE DYNAMICALLY COMPILED TEST TEMPLATE 2</div>';

    var getTemplate = function(contentType) {
        var template = '';

        switch(contentType) {
            case 'testTemplate1':
                template = testTemplate1;
                break;
            case 'testTemplate2':
                template = testTemplate2;
                break;
        }

        return template;
    };

    var linker = function(scope, element, attrs) {

        //reads the scope's content attribute (2 way bound to this directive's isolate scope) and sets as DOM
        element.html(getTemplate(scope.content.testContent)).show();

        //compiles the 2 way bound DOM, recompiles directive on changes to attributes. todo: CHECK DIRECTIVE RECOMPILES ON CHANGES TO ATTRIBUTES
        $compile(element.contents())(scope);
    };

    return {
        restrict: "E", //can only be an element
        link: linker, //link function
        scope: { //isolate scope, 2 way bind to a 'content' attribute
            content:'='
        }
    };

});

Use of this directive in the DOM, where I attempted to alter the $scope.content object but the directive's inner content didn't recompile:

 <test-custom-mapper-things content="testContent"></test-custom-mapper-things>
    <button ng-click="changeContent()">Click to change the test content and see if DOM recompiles</button>

Controller for the parent scope of the directive:

$scope.testContent = {
   testContent : "testTemplate1"
};

$scope.changeContent = function() {

  if($scope.testContent.testContent == 'testTemplate1') {
    $scope.testContent.testContent = 'testTemplate2';
  } else {
    $scope.testContent.testContent = 'testTemplate1';
  }
};

1 Answer 1

1

Use the $compile service and scope.$watch method.

Example:

Javascript:

angular.module('dynamicTemplate',[])
  .directive("testCustomMapperThings",['$compile',function($compile) {

   return {
        resctrict: 'AE',
        scope: {
          active: '='
        },
        link: function (scope, el, attrs, ctrl) {
          var t1 = "<div>I'm 1st Template</div>",
              t2 = "<div>I'm 2nd Template</div>",
              newTemplate;
          function loadTemplate() {
            el.html('');

            switch(scope.active) {
              case 'template1':
                 newTemplate = t1;
              break;
              case 'template2':
                 newTemplate = t2;
              break;
            }
            el.append($compile(newTemplate)(scope));
          }

          loadTemplate();
          scope.$watch('active', function(newVal, oldVal){
            if(newVal === oldVal) return;
              loadTemplate();
          });
        }  

    }
}])
.controller('templateController', function() {
  var vm = this;
  vm.option = true;
  vm.templateOption =  vm.option? 'template1' : 'template2';

  vm.change = function() {
    vm.option = !vm.option;
    vm.templateOption =  vm.option? 'template1' : 'template2';
  }
});

HTML

<div ng-app="dynamicTemplate">
  <div ng-controller="templateController as t">
     <button ng-click="t.change()"></button>
    <test-custom-mapper-things active="t.templateOption"></test-custom-mapper-things>

  </div>
</div>

Codepen: http://codepen.io/gpincheiraa/pen/vLOXGz

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

1 Comment

This worked well thanks, since gained a greater understanding of the $compile service and managed it. Thanks again.

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.