3

I have created a directive for a search box which i want to use with different views. Here is the directive -

angular.module('jobSeekerApp')
  .directive('searchBoxDirective', function () {
    return {
      restrict: 'E',
      templateUrl: 'templates/searchbox-template.html',
    };
  });

template for the directive -

<span class="searchButton"><i class="fa fa-search fa-2x"></i></span>
<input ng-change="search()" ng-model="searchTerm" ng-keydown="deleteTerm($event)" type="text" id="search-box" style="width: 0px; visibility:hidden;"/>

I want to use this directive on two views which look like this -

View 1 -

<div class="panel panel-default companies" ng-repeat="company in companies.companiesList">
    <div class="panel-heading text-center"><a ng-href="/companies/{{company.id}}" class="hvr-sink"><h3 class="well">{{company.company_name}}</h3></a></div>
    <div class="panel-body text-center flexcontainer">
        <div>Location: {{company.location}}</div>
        <div>Founded In: {{company.founded_year}}</div>
        <div ng-if="company.opening">Opening: Yes</div>
        <div ng-if="!company.opening">Opening: No</div>
        <div>Number Of Openings: {{company.no_openings}}</div>
    </div>
</div>

View 2 -

<div class="panel panel-default jobs" ng-repeat="job in jobs.jobsList">
    <div class="panel-heading text-center"><a href="{{job.career_url}}" target='_blank' class="hvr-sink"><h3 class="well">{{job.job_name}}</h3></a></div>
    <div class="panel-body text-center flexcontainer">
        <div>Company: {{job.company_name}}</div>
    </div>
</div>

As you can see i am using aliases companies and jobs in my views, due to this my directive is not able to affect the view it is contained in. If i use the companies or jobs in my template , then it works fine. So for example if change the template to -

<span class="searchButton"><i class="fa fa-search fa-2x"></i></span>
<input ng-change="companies.search()" ng-model="companies.searchTerm" ng-keydown="companies.deleteTerm($event)" type="text" id="search-box" style="width: 0px; visibility:hidden;"/>

Then it works with the view associated with companies controller and similarly for jobs.

How can i use the directive with the respective controller instance? Thank you.

doctorsherlock
  • 1,334
  • 4
  • 19
  • 41

3 Answers3

3

Create a simple view for Search

Create it's own controller

Search record by this controller in service and put data in service variable

this.searchResult = [];
this.search = function(searchText){
    // code to search
    this.searchResult = Result;    
}

Now where ever you want to use this result use the watch on this service variable in current controller, like:

$scope.$watch(function() { return servicename.searchResult ; }, function(newVal, oldval) { 
   if(newval != oldval){
       $scope.data = servicename.searchResult;
       /* Do the rest of stuff */
   }
}, true);
Ali Adravi
  • 21,707
  • 9
  • 87
  • 85
  • I would highly discourage using a $watch unless absolutely necessary. They can be intensive if you get out of control with them and are not best practice. – Ohjay44 Sep 01 '16 at 21:28
2

Use isolate scope and explicitly pass the search term to your directive. Something like:

angular.module('jobSeekerApp')
  .directive('searchBoxDirective', function () {
    return {
      restrict: 'E',
      scope: {
        searchTerm: '=' <-- you can use '@' if you don't need two-way binding
      },
      templateUrl: 'templates/searchbox-template.html',
    };
  });

You didn't show where you are actually using your directive, but you would pass the scope property through an attribute (this is using your directive on a <div>):

<div search-box-directive search-term="companies.searchTerm"></div>
Lex
  • 6,758
  • 2
  • 27
  • 42
2

Since the search function is asynchronous, I recommend avoiding the use of ng-change to invoke it. Instead use ng-click on the search icon.

<span class="searchButton" ng-click="$ctrl.searchFn()">
    <i class="fa fa-search fa-2x"></i>
</span>
<input ng-model="$ctrl.searchTerm" type="text" id="search-box">

In the directive, use isolate scope and bindToController.

app.directive('searchBoxDirective', function () {
    return {
      restrict: 'E',
      scope: { 'searchTerm': "=",
               'searchFn': "&"
             },
      controller: function () {},
      controllerAs: '$ctrl',
      bindToController: true,
      templateUrl: 'templates/searchbox-template.html'
    };
});

Usage

<search-box-directive search-term="companies.searchTerm"
                      search-fn="companies.search()" >
</search-box-directive>

<search-box-directive search-term="jobs.searchTerm"
                      search-fn="jobs.search()" >
</search-box-directive>

The search-term attribute creates a bidirectional binding from parent scope to the directive isolate scope. The search-fn attribute creates an expression binding from parent scope to isolate scope.

georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • Thanks, this is i what i have been trying to do. I used ng-change because i wanted to display results as soon as the user starts typing, instead of clicking a button first. Also instead of passing `search-term`, `search-fn` separately, i passed the full object in the scope and used it like `ng-model="$ctrl.obj.searchTerm"` . And just curious how do i pass a function which takes an event like this `ng-keydown="companies.deleteTerm($event)"`? – doctorsherlock Sep 02 '16 at 08:33
  • For information on passing local variables, see [StackOverflow -- Communicating Events from Parent to Child in AngularJS -- Using Expression Binding](http://stackoverflow.com/questions/37439300/communicating-events-from-parent-to-child-in-angularjs-components/37449259#37449259) – georgeawg Sep 02 '16 at 15:33