Search code examples
angularjsangularjs-directiveangularjs-scopeangularjs-model

Watch a custom directives inner html in angular


I have a global search variable that is used by the whole app

newspaper.controller("MainController", function($scope) {
    $scope.search = {query:''};
});

Then I have a contenteditable div that I want to bind to $scope.search

app.directive('search', function() {
    return {
        restrict: 'AE',
        replace: true,
        template: '<div ng-model="query" id="search"></div>',
        scope: { 
            query: '='
        },
        controller: function($scope) {
            // I want to watch the value of the element
            $scope.$watch('query', function(newValue, oldValue){
                console.log(newValue);
            },true);
        },
        link: function(scope, element, attrs) {
            // Medium JS content editable framework
            new Medium({
                element: element[0],
                mode: Medium.inlineMode
            });
        }
    }
});

The watch is not firing when I type new values into the div, I guess Im still confused on how to link a directive with a model. Here's the HTML

  <nav ng-controller="MainControllerr">
    <search context="search.query"></np-search>
  </nav>

Solution

  • Don't think you need the watch. It's bad practise to use watch functions in your controllers anyway as it makes them really hard to test.

    Here's a simplified version of what (I think) your trying to do.

    DEMO

    index.html

    <!DOCTYPE html>
    <html ng-app="plunker">
    
      <head>
        <meta charset="utf-8" />
        <title>AngularJS Plunker</title>
        <script>document.write('<base href="' + document.location + '" />');</script>
        <link rel="stylesheet" href="style.css" />
        <script data-require="[email protected]" src="https://code.angularjs.org/1.4.2/angular.js" data-semver="1.4.2"></script>
        <script src="app.js"></script>
      </head>
    
      <body>
    
        <nav ng-controller="MainController">
    
          <pre>{{query}}</pre>
    
          <search query="query"></search>
        </nav>
      </body>
    
    </html>
    

    app.js

    var app = angular.module('plunker', []);
    
    app.controller("MainController", function($scope) {
       $scope.query = {value:''};
    });
    
    
    app.directive('search', function() {
        return {
            restrict: 'AE',
    
            template: '<input ng-model="query.value" id="search"/>',
            scope: { 
                query: '='
            }
        }
    });
    

    EDIT

    If you really want to use a content editable div you'd have to try something like this:

    Also, see this SO question from which I've taken and adapted code to create the demo below. Hope it helps.

    DEMO2

    index.html

    <!DOCTYPE html>
    <html ng-app="plunker">
    
      <head>
        <meta charset="utf-8" />
        <title>AngularJS Plunker</title>
        <script>document.write('<base href="' + document.location + '" />');</script>
        <link rel="stylesheet" href="style.css" />
        <script data-require="[email protected]" src="https://code.angularjs.org/1.4.2/angular.js" data-semver="1.4.2"></script>
        <script src="app.js"></script>
      </head>
    
      <body>
    
        <nav ng-controller="MainController">
    
          <pre>{{query}}</pre>
    
          <div search contenteditable="true" ng-model="query.value">{{ query.value }}</div>
        </nav>
      </body>
    
    </html>
    

    app.js

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

    app.controller("MainController", function($scope) { $scope.query = {value:'rrr'}; });

    app.directive('search', function() {
        return {
            restrict: 'AE',
            require: 'ngModel',
    
            link: function(scope, element, attrs, ctrl) {
    
              element.bind('blur', function() {
                scope.$apply(function() {
                  ctrl.$setViewValue(element.html());
                });
              });
    
              element.html(ctrl.$viewValue);
            }
        }
    });