Search code examples
javascriptangularjsangular-ui-datepicker

Angular erases invalid content from input inside directive


I have directive where I'm dynamically adding other directives and attributes:

app.directive('lrDatetime', function ($compile) {
return {
  restrict: 'AE',
  require: ["ngModel"],
  scope: {
    ngModel: "=",
    item: "="
  },
  templateUrl: "template.html",
  compile: function compile(element, attrs) {
    var datepicker = element.find(".lr-datepicker");

    if (attrs.required === "true") {
        datepicker.attr("ng-required", "true");
    }

    return {
        pre: function preLink(scope, iElement, iAttrs, controller) { },
        post: function postLink(scope, iElement, iAttrs, controllers) {
            $compile(iElement.contents())(scope);
        }
    };
  },
  controller: function ($scope) {
    $scope.opened = false;
      $scope.open = function ($event, obj, which) {
          $scope.opened = true;
      };

      $scope.format= "dd.MM.yyyy"; 

  }
};
});

And template:

<input type="text" 
class="lr-datepicker" 
is-open="opened"  
uib-datepicker-popup="{{format}}"
datepicker-append-to-body="true"
ng-model="ngModel" />
<span class="input-group-btn">
    <button type="button" class="btn btn-default" 
    ng-click="open($event, item, 'isOpened')">
        Open
    </button>
</span>

Now when I have value binded and trying to type something into input it gets erased. I know that if model is invalid angular sets it to "undefined", but if I would do the same outside the directive it keeps the content of the input.

And if I will just move those attributes to template and delete call to $compile - everything work as expected. But huge minus of such approach is that I cannot control attribute appearance, it always will be rendered.

What am I missing?

Plunker


Solution

  • Found the solution - ngModel attribute should be added manually in link function before $compile:

    app.directive('lrDatetime', function ($compile) {
        return {
          restrict: 'AE',
          require: ["ngModel", "^form"],
          scope: {
            ngModel: "=",
            item: "=",
            required: "=",
            name: "@"
          },
          templateUrl: "template.html",
          link: function (scope, element, attrs, controllers) { 
            scope.itemForm = controllers[1];
    
            scope.opened = false;
            scope.open = function ($event, obj, which) { 
                scope.opened = true;
            };
    
            scope.format= "dd.MM.yyyy"; 
    
            var datepicker = element.find("input");
    
            if (scope.required === true) {
                datepicker.attr("ng-required", "true");
            }
    
            datepicker.attr("name", scope.name);
            datepicker.attr("ng-model", "ngModel");
            datepicker.attr("uib-datepicker-popup", scope.format);
    
            $compile(element.contents())(scope);
    
         }
      };
    });
    

    template:

    <input type="text" 
    class="lr-datepicker" 
    is-open="opened"  
    datepicker-append-to-body="true" />    
    <span class="input-group-btn">
        <button type="button" class="btn btn-default" 
        ng-click="open($event, item, 'isOpened')">
            Open
        </button>
    </span>
    <span ng-if="itemForm[name].$error.required">required!</span>
    

    Plunker

    Think the issue pops up because template gets compiled twice and model gets rerebinded.