1

I have an object on the scope of a controller that contains some presentation data for an input:

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

app.controller('MainCtrl', function($scope) {
  $scope.settings = {
    value: 'xxx',
    required: true,
    show: true,
    readonly: false
  };
});

In the actual application, this is part of a larger object loaded from the server. I created a directive that would take this presentation object as input and attach the necessary directives:

app.directive('fieldSettings',
  [/*$injectables*/
  function (/*$injectables*/) {
    return {
      priority: 100,
      restrict: 'A',
      scope: {
        fieldSettings: '='
      },
      compile: function (el, attrs) {
        return function (scope, iElement, iAttrs) {
          iAttrs.$set('ng-model', 'fieldSettings.value');
          iAttrs.$set('ng-show', 'fieldSettings.show');
          iAttrs.$set('ng-required', 'fieldSettings.required');
          iAttrs.$set('ng-readonly', 'fieldSettings.readonly');
        }
      }
    };
  }
]);

As this plunk demonstrates, the attributes are added but the logic is not being applied. According to the documentation for angular, the directives I am trying to apply have a priority of 0 and the input directive has a priority of 100. I set mine to 100 but this value seems to have no affect regardless of the value I choose for it.

I want

<input field-settings="settings" />

to behave like

<input ng-model="settings.value" ng-show="settings.show" ng-required="settings.required" ng-readonly="settings.readonly" />

but literally be

<input ng-model="fieldSettings.value" ng-show="fieldSettings.show" ng-required="fieldSettings.required" ng-readonly="fieldSettings.readonly" />

where fieldSettings is the directive's local scope variable bound to the MaintCtrl's local scope variable settings.

km6zla
  • 4,787
  • 2
  • 29
  • 51

1 Answers1

2

Just adding the attributes without compiling won't do anything.

My similar answeres:

Here is a plunker: http://plnkr.co/edit/8kno6iwp3hH5CJFQt3ql?p=preview

Working directive:

app.directive('fieldSettings',
  ['$compile',
  function ($compile) {
    return {
      restrict: 'A',
      scope: {
        fieldSettings: '='
      },
      priority: 1001,
      terminal: true,
      compile: function(tElm,tAttrs){
        tElm.removeAttr('field-settings')
        tElm.attr('ng-model', 'fieldSettings.value');
        tElm.attr('ng-show', 'fieldSettings.show');
        tElm.attr('ng-required', 'fieldSettings.required');
        tElm.attr('ng-readonly', 'fieldSettings.readonly');
        var fn = $compile(tElm);
        return function(scope){
          fn(scope);
        }
      }
    };
  }
Community
  • 1
  • 1
Ilan Frumer
  • 32,059
  • 8
  • 70
  • 84
  • is there a reason to use `elm.attr` over `attrs.$set`? – km6zla Feb 11 '14 at 00:18
  • 1
    Oh! `tElm.removeAttr('field-settings')` was the key. I had tried `$compile` but was getting `Maximum call stack size exceeded` which seems obvious now as it would try to reapply the field-settings directive and get stuck in a loop! – km6zla Feb 11 '14 at 00:24
  • @ogc-nick they would both work the same in this case. `attrs.$set` has more features than just getting/setting an attribute. – Ilan Frumer Feb 11 '14 at 00:29
  • @ogc-nick terminal & high priority are also very important with this self compiling pattern. – Ilan Frumer Feb 11 '14 at 00:35
  • Is it enough to set the priority at 100? 100 is the priority of input and the directives I am adding have priorities of 0 as far as I can tell. I haven't heard of terminal before though I will have to look that one up. Should I be using it? – km6zla Feb 11 '14 at 00:41
  • @ogc-nick Since your directive recompiles the whole element, You must prevent all other directives from being compiled during the first compilation. Your directive must have a terminal option and a higher(not equal) priority than all other directives on the same element. The highest built-in angular directive is `ngRepeat` with a priority of 1000 so I regularly use 1001 . In your case you can use 101 but I recommend using 1001 so it would work with all built-in directives. – Ilan Frumer Feb 11 '14 at 00:58