Append html to an element in directive and make a local function to interact with it

harkor picture harkor · Jan 30, 2014 · Viewed 99.1k times · Source

In my AngularJS application, I have different complex inputs everywhere. For example, some inputs have a directive to use autocompletion with Google Places or with autocompletion from Twitter Bootstrap.

I'm searching for a way to make a directive which displays an erase button when we add some text like iOS feature.

I made this one, but the scope.erase doesn't start, nor does the ng-show.

Is it possible to add HTML after the text input and "play" with them inside the controller?

My test:

app.directive('eraseBtn', function($parse, $compile){

return {
    require: 'ngModel',
    restrict: "A",
    transclude: true,
    link : function(scope, element, attrs, model){

        element.parent().append('<button ng-click="erase()" ng-show="model.length > 0" class="erase-btn">x</button>');

        scope.erase = function(){
            console.log('Erase test');
        }
    }
}
});

I don't want to use a template because all of my inputs' HTML are really different.

Answer

Vadim picture Vadim · Jan 30, 2014

You can create custom inputs inside link function of your directive depending on values of the model. If you want that elements to be bind to model or use directives to build them, you should use $compile (and don't forget to call compiled template with model):

HTML

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

  <head>
    <script data-require="angular.js@*" data-semver="1.2.10" src="http://code.angularjs.org/1.2.10/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body ng-controller="demoController">
    <div demo-directive ng-repeat="input in inputs"></div>
  </body>

</html>

JavaScript

angular.module('demo', []).
  controller('demoController', function($scope) {
    $scope.inputs = [{
      inputType: 'checkbox',
      checked: true,
      label: 'input 1'
    }, {
      inputType: 'text',
      value: 'some text 1',
      label: 'input 2'
    }];

    $scope.doSomething = function() {
      alert('button clicked');
    };
  }).
  directive('demoDirective', function($compile) {
    return {
      template: '<div><label>{{input.label}}: </label></div>',
      replace: true,
      link: function(scope, element) {
        var el = angular.element('<span/>');
        switch(scope.input.inputType) {
          case 'checkbox':
            el.append('<input type="checkbox" ng-model="input.checked"/><button ng-if="input.checked" ng-click="input.checked=false; doSomething()">X</button>');
            break;
          case 'text':
            el.append('<input type="text" ng-model="input.value"/><button ng-if="input.value" ng-click="input.value=\'\'; doSomething()">X</button>');
            break;
        }
        $compile(el)(scope);
        element.append(el);
      }
    }
  });

Plunker: http://plnkr.co/edit/pzFjgtf9Q4kTI7XGAUCF?p=preview