3

I have a form based on twitter bootstrap, each field have it's own configuration

// controller (the template shows this in ng-repeat

$scope.fields = [{name:"f1", label:"Field 1", with_button: false},
                 {name:"f2", label:"Field 2", with_button: true}]

I'm trying to make a "conditional directive" that customize the template according to "field.with_button"

// Without button
<div class="controls">
    <input type="text" id="i_{{field.name}}">
</div>

// With button
<div class="controls">
    <div class="input-append">
        <input type="text" id="i_{{field.name}}">
        <span class="add-on">bt</span>
    </div>
</div>

I searched a lot and didn't find any solution, I tried to create only one div and put contents inside with a compiler function but it didn't parse, and if I call $apply it crashes.

How could I make this directive?

wrong My last try:

angular.module('mymodule',[]).directive('ssField', function() {
    return {
        transclude:false,
        scope: {
            field: '='
        },
        restrict: 'E',
        replace:true,
        template: '<div class="controls">{{innerContent}}</div>',
        controller: ['$scope', '$element', '$attrs', function($scope, $element, $attrs) {
            $scope.$eval('$scope.innerContent = \'<input type="text" id="input_{{field.name}}" placeholder="{{field.name}}" class="input-xlarge">\'');
        }]
    };
});

//<ss-field field="{{field}}"></ss-field>

3 Answers 3

6

You can use the $http and $compile services to do what you're after.

http://plnkr.co/edit/Xt9khe?p=preview

This plnkr should demostrate what needs to be done, but basically:

  1. Use $http to load the template depending on the condition.
  2. Compile the loaded template against the current scope with $compile.
angular.module('mymodule',[]).directive('ssField', ['$http', '$compile', function($http, $compile) {
    return {
        transclude:false,
        scope: {
            field: '='
        },
        restrict: 'E',
        replace:true,
        template: '<div class="controls"></div>',
        link: function(scope, element, attrs) {
          var template;
          var withButtonTmpl = 'with_button.html';
          var withoutButtonTmpl = 'without_button.html';

          if (scope.field.with_button) {
            $http.get(withButtonTmpl).then(function(tmpl) {
              template = $compile(tmpl.data)(scope);
              element.append(template);
            });
          } else {
            $http.get(withoutButtonTmpl).then(function(tmpl) {
              template = $compile(tmpl.data)(scope);
              element.append(template);
            });
          }
        }
    };
}]);

You can change the directive to be more robust so the URLs aren't directly embedded in the directive for re-usability, etc., but the concept should be similar.

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

1 Comment

is this not an overkill? What about loading a template with some ng-if??
2

Just to further expand on Cuing Vo's answer here is something similar to what I use(without using external partials and additional $http calls):

http://jsfiddle.net/LvUdQ/

myApp.directive('myDirective',['$compile', function($compile) {
    return {
        restrict: 'E',

        template: '<hr/>',
        link: function (scope, element, attrs, ngModelCtrl) {
            var template = {
                'templ1':'<div>Template 1</div>',
                'templ2':'<div>Template 2</div>',
                'default':'<div>Template Default</div>'
            };
            var templateObj;
            if(attrs.templateName){
                templateObj = $compile(template[attrs.templateName])(scope);
            }else{
                templateObj = $compile(template['default'])(scope);
            }
            element.append(templateObj);
        }    
    };
}]);

However Im not quite sure its by the bible from performance perspective.

Comments

1

In AngularJS, directly manipulate the DOM must only be a last resort solution. Here, you can simply use the ngSwitch directive :

angular.module('mymodule',[]).directive('ssField', function() {
    return {
        transclude:false,
        scope: {
            field: '='
        },
        restrict: 'E',
        replace:true,
        template:
            '<div class="controls" data-ng-switch="field.with_button">' +
                '<input type="text" id="i_{{field.name}}" data-ng-switch-when="false">' +
                '<div class="input-append" data-ng-switch-default>' +
                    '<input type="text" id="i_{{field.name}}">' +
                    '<span class="add-on">bt</span>' +
                '</div>' +
            '</div>',
    };
});

1 Comment

I do like this more than doing a new http request per each template

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.