5

I would like to create an element directive in angularjs that produces an html element from a json blob passed as an attribute. I have tried quite a few variations of the following...

demoApp.directive("element", function() {
    return {
        restrict: "E",
        scope: {
            attributes: "@",
            name: "@"
        },
        template:         
            function(name, attributes) {
                var templateString = "<" + attributes.tag;
                for (attribute in attributes) {
                    if (attribute != "name_displayed" && attribute != "tag") {
                        templateString += " " + attribute + "=\"" attributes[attribute] + "\"";
                    }
                }
                templateString += " name=\"" field + "\"";
                templateString += ">";
                templateString += "</" + attributes.tag + ">";
                return attributes.name_displayed + ": " + templateString;
            }(name, attributes)
    };
});

The html looks like

<div ng-repeat="(name, attributes) in fields">
    <element name="{[{name}]}" attributes="{[{attributes}]}"></element>
</div>

Where an attributes json object looks like

{"name_displayed":"Agency","size":"30","tag":"input","type":"text"}

And a name looks like

agency

It looks like I cannot use a function for a template, and it also looks like I cannot get access to the attributes or name objects.

1
  • Your json is the first problem. Storing markup instructions in a json (presumably being saved to the server) is odd...and I'm tempted to say just silly. Even if you needed to be able to take some user input and form a template to output later, you would save the template as an html file, not as a json. Commented Nov 14, 2013 at 20:38

2 Answers 2

1

Check this out: http://jsfiddle.net/es4Y6/1/

var app = angular.module('hmm', []);

function ctrl($scope) {
    $scope.fields = {
        first: '{"name_displayed": "Agency", "size": "30", "tag": "input", "type": "text"}',
        second: '{"name_displayed": "Foo", "size": "30", "tag": "input", "type": "password"}',
        third: '{"name_displayed": "Bar", "size": "30", "tag": "input", "type": "number"}'
    };
}

app.directive('blah', function() {

    var template = function(name, attributes) {
        var templateString = "<" + attributes.tag;
        for (var attribute in attributes) {
            if (attribute != "name_displayed" && attribute != "tag") {
                templateString += " " + attribute + '="' + attributes[attribute] + '"';
            }
        }
        templateString += ' name="' + name + '"';
        templateString += ">";
        templateString += "</" + attributes.tag + ">";
        return attributes.name_displayed + ": " + templateString;
    };

    return {
        restrict: "E",
        link: function(scope, element, attrs){
            var attributes = angular.fromJson(attrs.attributes);
            var tpl = template(attrs.name, attributes);
            element.html(tpl);
        }
    };

});

I assume that by "json blob" you mean json string. If not, then you mean just JS object. In such case, update $scope.fields and remove angular.fromJson().

<div ng-app="hmm">
    <div ng-controller="ctrl">
        <div ng-repeat="(name, attributes) in fields">
            <blah name="{{name}}" attributes="{{attributes}}"></blah>
        </div>
    </div>    
</div>

It works, however it's a very bad approach to the problem you are trying to solve.

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

1 Comment

Do you have a suggestion for a better approach?
1

You can implement your logic in link function instead of a template. Try this:

HTML

<element ng-repeat="field in fields" />

JavaScript

angular.module('demo', []).
    controller('demoCtrl', ['$scope', function($scope) {
      $scope.fields = [{
        "tag": "input",
        "type": "text",
        "value": "Demo app",
        "name": "my_input",
        "label": "My Text"
      }, {
        "tag": "input",
        "type": "checkbox",
        "checked": "checked",
        "name": "my_checkbox",
        "label": "My Checkbox"
      }, {
        "tag": "input",
        "type": "button",
        "value": "Click Me",
        "name": "my_button"
      }];
    }]).
    directive('element', function() {
      return {
        restrict: "E",
        replace: true,
        template: "<div></div>",
        link: function(scope, element, attrs) {
          var label,
              el,
              key,
              field;

          field = scope.field;

          if('label' in field) {
            label = document.createElement('label');
            label.innerHTML = field.label;
            element.append(label);
            element.append(document.createTextNode(': '));
          }

          el = document.createElement(field.tag);
          for(key in field) {
            if(field.hasOwnProperty(key) && // avoid prototype properties
                key !== 'tag' && // avoid tag
                key !== 'label' && // avoid label
                key[0] !== '$' // avoid angular staff derived from scope
            ) {
              el.setAttribute(key, field[key]);
            }
          }
          element.append(el);
        }
      };
    });

Here is a working example: http://plnkr.co/edit/B1RigXrzA2l1kIVNVXGw?p=preview

Comments

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.