mymotherland
mymotherland

Reputation: 8248

Directive generate directive field dynamically does not compile

I have created basic HTML field generator directive called field as like below

app.directive('field',function(){
        return {
            restrict : "E",
            scope : {
               fielddata : '='
            },
            link : function(scope,elem,attr){
                var content;
                scope.Options = {
                    id: scope.$id+'_'+elem.attr('id'),
                    label : elem.attr('label'),
                    placeholder : elem.attr("placeholder"),

                };
                scope.contentUrl = 'templates/fields/'+elem.attr('template')+'.html';           
            },
            template: '<div ng-include="contentUrl"></div>'
        }
    }) 

I can able to generate fields as like below

<field id="NAME" template="text" label="First Name" placeholder="Enter First Name" fieldData="NAME"></field>

Check this plunker

Extending the functionality to generate element directive itself. Have simple object to setup the field element and use another directive to generate element directive

Controller

app.controller('contactController', ['$scope', 'dataService',function($scope,dataService) {
        $scope.message = 'Contact us! . This is just a demo.';
        dataService.getContactData().then(function(data) {
            $scope.NAME = data.first_name;
            $scope.LNAME = data.last_name;
            $scope.DESC = data.description;
        });
          $scope.fields= [
            {
              elements: [
                            {
                                template_name : "text"
                                id: "NAME",
                                label: "First Name",
                                placeholder : "First Name"
                            }           
                        ],
              html: '<div ng-repeat="elem in t.elements"><field id="{{elem.id}}" template="{{elem.template_name}}" label="{{elem.label}}"  placeholder="{{elem.placeholder}}""  fieldData="{{elem.id}}" ></field></div>'
            }
          ];
    }]);
    app.directive("bindCompiledHtml", function($compile, $timeout) {
      return {
        template: '<div></div>',
        scope: {
          rawHtml: '=bindCompiledHtml'
        },
        link: function(scope, elem, attrs) {
          scope.$watch('rawHtml', function(value) {
            if (!value) return;
            var newElem = $compile(value)(scope.$parent);
            elem.contents().remove();
            elem.append(newElem);
          });
        }
      };
    });
});

Say contact.html

<div ng-repeat="t in fields" bind-compiled-html="t.html"></div>

My issue is, bindCompiledHtml Directive generate elements and calling field directive as expected but it value is not get parsed. Setting Break point from field directive link function gives elem as below

[
<field id=​"{{elem.id}​}​" template=​"{{elem.template_name}​}​" label=​"{{elem.label}​}​" placeholder=​"{{elem.placeholder}​}​" class=​"ng-isolate-scope">​
<!-- ngInclude: contentUrl -->
</field>​
]

and console gives following error

/templates/fields/%7B%7Belem.template_name%7D%7D.html 404 (Not Found) 

To see 404 Please check this plunker console

How do we call field directive once all its value get parsed with isolate scope ? Please advice

Upvotes: 0

Views: 105

Answers (1)

Kirill Slatin
Kirill Slatin

Reputation: 6173

Dinesh, without a given original problem your code is rather over complicated. That is why I was asking about the purposes of these nested compilations.

The trouble you experience in current code is the definition of directive. Once it's attributes are not defined in isolated scope via = their values will never be evaluated and will always remain plain text. That is why you can't use expressions in ng-repeat like {{t.template_name}} when you define values for attributes of your field directive. Directive should have the following implementation

app.directive('field', function() {
  return {
    restrict: "E",
    scope: {
      placeholder: '=',
      label: '=',
      id: '=',
      template: '='
    },
    link: function(scope, elem, attr) {
      var content;
      scope.Options = {
        id: scope.$id + '_' + scope.id,
        label: scope.label,
        placeholder: scope.placeholder
      };
      scope.contentUrl = scope.template + '.html';
    },
    template: '<div ng-include="contentUrl"></div>'
  }
});

As a result you will always have to define an expression to these attributes. Thus in 'manual' markup you would use "'my text'" instead of "my text"

<field id="NAME" template="'text'" label="'First Name'" 
     max-size="40" placeholder="'Enter First Name'" 
     type="edit" ></field>

Updated plunker

Upvotes: 1

Related Questions