0

I'm unable to get a variable value from directive to use that back in a controller. I do not have to bind the value to a view. All I need is to set 'cleanInputValue' from directive to $scope.keywords in Controller.

Here is my directive and controller -

Html with md-autocomplete for keywords field - search box.

 <form id="searchbox" ng-controller="navsearchController as sc" title="Search this site" layout="row">
          <md-autocomplete
            md-no-cache="true"
            md-selected-item="sc.currentKeyword"
            md-search-text="keywords"
            md-items="item in querySearch(keywords)"
            md-item-text="item.display"
            md-min-length="3"
            md-input-name="search"
            md-input-id="search-nav"
            md-clear-button="false"
            placeholder="Search"
            md-dropdown-position="bottom"
            flex>
            <md-item-template>
              <span md-highlight-text="keywords" md-highlight-flags="gi">{{item.display}}</span>
            </md-item-template>
          </md-autocomplete>
          <div class="search-button" flex="none">
            <button type="submit" ng-click="sc.search()" title="Search" tabindex="0">GO</button>
          </div>
        </form>

Directive:

.directive('test', function () {
    return {
        require: 'ngModel',
        restrict: 'A',
       scope: {
            text: '@text'
        },
        link:function(scope, element, attrs, modelCtrl){
          modelCtrl.$parsers.push(function(inputValue) {
            if (inputValue === undefined){
            return '';
            }
            var cleanInputValue = inputValue.replace(/[^\w\s\-\"]/gi, '');

            if (cleanInputValue != inputValue) {
            modelCtrl.$setViewValue(cleanInputValue);
            modelCtrl.$render();
          }
          return cleanInputValue;
          });
            //console.log(scope.text);
        }
    };
})

Controller:

.controller('navsearchController', function($timeout, $element, $compile, $scope, $rootScope, $http, $location, DataService, $routeParams, $filter, $route){
    var _this = this;

    $timeout(function () {
       var myAutoCompleteInput = 
        angular.element($element[0].querySelector('#search-nav'));
        myAutoCompleteInput.attr("test", "test");
        //myAutoCompleteInput.attr("text", "blah");
         console.log($scope.keywords);

         $compile(myAutoCompleteInput)($scope);
        });

      _this.search = function(){           

       xyzStorage.set('currentKeyword', $scope.keywords);
       $scope.keywords = $filter('removeSpecialChars')($scope.keywords);
       $location.path('/xyz/search/' + $scope.keywords);

      $location.url($location.path());

      $location.search({
        range: xyzStorage.get('itemPerPage'),
      })
      $route.reload();
    };

})
sunskin
  • 1,620
  • 3
  • 25
  • 49
  • what are you trying to do? Writing such directives is fighting against framework. I am writing in angular for years and used $compile only several times - you really need all this? – Petr Averyanov May 08 '18 at 13:47
  • 1
    I agree that perhaps the entire thing can have a good ol re-write but I am trying to address the OP issue of passing data between directive and controller first. Tackle one thing at a time. – Doug E Fresh May 08 '18 at 13:49

1 Answers1

1

What you really want to do is bind the value from your controller to your directive. Don't think of it as "returning" a value from your directive. Let's take a look.

.directive('test', function () {
    return {
        require: 'ngModel',
        restrict: 'A',
       scope: {
            text: '@text',
            cleanInputValue: '=testTextClean' // Adding a new TWO WAY binding here!
        },
        link:function(scope, element, attrs, modelCtrl){
          modelCtrl.$parsers.push(function(inputValue) {
            if (inputValue === undefined){
            return; // exit the function and don't assign, ok
            }
            // Now we use scope
            scope.cleanInputValue = inputValue.replace(/[^\w\s\-\"]/gi, '');

            if (scope.cleanInputValue != inputValue) {
              modelCtrl.$setViewValue(scope.cleanInputValue);
              modelCtrl.$render();
            }
          // no longer need a return 
          });
        }
    };
})

In your controller you are accessing the input element within the md-autocomplete component

.controller('navsearchController', function($timeout, $element, $compile, $scope, $rootScope, $http, $location, DataService, $routeParams, $filter, $route){
    var _this = this;

    $timeout(function () {
       var myAutoCompleteInput = 
        angular.element($element[0].querySelector('#search-nav'));
        myAutoCompleteInput.attr("test", "test");
        myAutoCompleteInput.attr("test-text-clean", "sc.keywords");
         console.log($scope.keywords);

         $compile(myAutoCompleteInput)($scope);
        });

      _this.search = function(){           

       xyzStorage.set('currentKeyword', $scope.keywords);
       $scope.keywords = $filter('removeSpecialChars')($scope.keywords);
       $location.path('/xyz/search/' + $scope.keywords);

      $location.url($location.path());

      $location.search({
        range: xyzStorage.get('itemPerPage'),
      })
      $route.reload();
    };

})

Now in your controller the value in $scope.keywords will always have the updated value set from your directive.

Doug E Fresh
  • 820
  • 8
  • 22
  • Thanks, Doug. Is there anyway to use 'cleanInputValue' to set $scope.keywords in Controller without updating the view? – sunskin May 08 '18 at 13:34
  • I would recommend against that because it is against design intent. If you access a specific variable name in a parent controller your directive becomes much less re-usable. However, if you really must, read up on this other post https://stackoverflow.com/questions/17900201/how-to-access-parent-scope-from-within-a-custom-directive-with-own-scope-in-an – Doug E Fresh May 08 '18 at 13:39
  • I have an md-autocomplete in my view with no explicit . Could you please tell me if I can assign ng-model="myAutoCompleteInput" text-clean="keywords" in a md-autocomplete? – sunskin May 08 '18 at 13:44
  • I was just guessing at your html because you did not show any in your example. I assumed you had an ng-model only because you required it in your directive. Where you are adding your `test` attribute just also add `text-clean="keywords"`. Also if you add in your html to the question maybe I can help you more exactly. – Doug E Fresh May 08 '18 at 13:47
  • Sure, adding the html now. – sunskin May 08 '18 at 13:48
  • please review the question now. Added html. Thank you! – sunskin May 08 '18 at 13:50
  • So it looks like your 'test' directive is never actually applied to anything. Is this question more on how to use a directive? – Doug E Fresh May 08 '18 at 13:56
  • 'test' is applied. It is successfully preventing user from typing special characters in the search box, but what else is happening is as user types in search box, "keywords" ($scope.keywords) is always null when user enters a search keyword and hits "Go" – sunskin May 08 '18 at 14:28
  • 1
    Oh yikes I see now you are using angular to apply attributes in the code. Sorry I missed that. – Doug E Fresh May 08 '18 at 14:30
  • no worries. Is there anyway we could get the value of 'cleanInputValue' through attributes in controller and assigning it to $scope.keywords? – sunskin May 08 '18 at 14:32