AngularJS - Create a directive that adds a sibling element

14,715

Solution 1

That's because your div "2 hello" is outside the container where your scope is visible. you can use element.append() instead of element.after() to have the scope available.

Directive

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


app.directive('myValidate', function($compile) {
      return {
        template: '<span>1. Hello {{world}}  my scope is {{$id}} (parent: {{$parent.$id}})<span/>',
        replace: true,
        restrict: 'A',
        scope: true,
        compile: function (element) {

          element.append('<div>2. Hello {{ world }}, my scope is {{$id}} (parent: {{$parent.$id}})</div>');

          return function(scope) {
            scope.world = 'World';
            //$compile()(scope);
          };
        }
      };
});

HTML

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script data-require="[email protected]" data-semver="1.1.5" src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="app.js"></script>
  </head>

  <body>
    <input my-validate="" />
  </body>

</html>

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

Solution 2

I was reading and checking the example because I was in the same situation to display validation messages but under the input field and the message can change according to what kind of validation is required.

So I came up with this solution

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

app.controller('ctrl', function($scope, CONSTANTS) {
  $scope.title = "title";
  $scope.CONSTANTS = CONSTANTS;
});

app.constant('CONSTANTS', {
  LENGHT_1: 3,
  LENGHT_2: 4
});

app.directive('dir', function($compile) {
  return {
    scope: true,
    restrict: 'A',
    require: '?ngModel',
    link: function(scope, elem, attrs, ngModel) {
      scope.maxLength = false;
      scope.required = false;
      scope.max = scope.$eval(attrs['ngMaxlength']);
      var tpl = '<div ng-if="maxLength" ng-include="\'length.tpl.html\'"></div>' +
        '<div ng-if="required" ng-include="\'required.tpl.html\'"></div>';
      var el = $compile(tpl)(scope);
      elem.after(el);

      scope.$watch(attrs['ngModel'], function(newValue, oldValue, scope) {
        if (ngModel.$error !== null && ngModel.$error.maxlength) {
          scope.maxLength = true;
        } else {
          scope.maxLength = false;
        }

        if (ngModel.$error !== null && ngModel.$error.required && ngModel.$dirty) {
          scope.required = true;
        } else {
          scope.required = false;
        }
      });
    }
  }
});
<!DOCTYPE html>
<html ng-app="app">

<head>
  <script data-require="[email protected]" data-semver="1.4.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="script.js"></script>
  
  <script type="text/ng-template" id="length.tpl.html">
    max length {{max}}
  </script>
  <script type="text/ng-template" id="required.tpl.html">
    required
  </script>
</head>

<body ng-controller="ctrl">
  <h1>Input Validation</h1> {{title}}
  <br><br>
  <form name="form" novalidate>
    <input dir name="input_one" ng-model="bar" ng-maxlength="CONSTANTS.LENGHT_1" required>
    <br>
    input one: {{form.input_one.$error}}
    <br>
    <br>
    <input dir name="input_two" ng-model="foo" ng-maxlength="CONSTANTS.LENGHT_2">
  </form>
  <br>
  input two: {{form.input_two.$error}}
</body>

</html>

On Plunkr

Hope it helps.

Share:
14,715
martinpaulucci
Author by

martinpaulucci

Updated on August 21, 2022

Comments

  • martinpaulucci
    martinpaulucci almost 2 years

    I'm creating a my-validate directive that looks something like this

    <input my-validate="customValidation" ng-model="model" />
    

    What I want to do is to attach a sybling element to the directive like this

    Error template:

    <ul class"errors">
       <li ng-repeat="for error in errors">{{error}} not valid</li>
    </ul> 
    

    errors is defined in the scope of the directive.

    I've added the error template in the compile function, but the problem I have is that the scope in the link function is not the same as the attached template.

    Here is a plunker to illustrate the issue: http://plnkr.co/edit/ghdtdYruQaaO0Yxxlrt1?p=preview

    'world' is seen in the directive template, but not on the added element :S.

  • martinpaulucci
    martinpaulucci about 11 years
    Thanks, the problem is I need it to be a sibling, not a child element.
  • Artur Udod
    Artur Udod about 11 years
    I suggest you to wrap it with div ( somewhat '<div class='validatedInput'> <input ng-model="world" /> </div>' ) and use element.append
  • martinpaulucci
    martinpaulucci about 11 years
    Te problem now is that the original attributes will be in the div and not in the input: plnkr.co/edit/23iwRQsdB6MxMU16NlHP?p=preview
  • Artur Udod
    Artur Udod about 11 years
    @martinpaulucci, take a look at this plnkr.co/edit/ohDtJqBCrmB8vhygrVQw?p=preview
  • Josh Noe
    Josh Noe over 7 years
    This doesn't address the question.
  • Pistos
    Pistos about 7 years
    This definitely works, and that's nice, but it would be great to be able to specify a single template as a whole for the whole collection of siblings, instead of having to rely on JS-side DOM manipulation to append, prepend, or whatever.