4

I am using the directive multiple times on the page like

<div data-escape-amp-curation-multi-value-selector
         data-binding-path="model"
         data-search-key="journeys"
         data-current-selection-model="model.journeys">
    </div>

<div data-escape-amp-curation-multi-value-selector
         data-binding-path="model"
         data-search-key="targets"
         data-current-selection-model="model.targets">
    </div>

My directive looks like

var directive : ng.IDirective = {
  restrict : 'A',
  template : '<select multiple="multiple" data-options="sources/>',
  compile() {
    return {
      pre(scope : any, element : any, attrs : any) {
        scope.readonly = attrs.readonly === 'true';
      },
      post(scope : any, element : any, attrs : any) {
        var binder = $parse(attrs.currentSelectionModel),
        valueDropDown = element.find('select.value-dropdown'),
        kendoMultiSelect = valueDropDown.data('kendoMultiSelect'),
        defer = $q.defer();

        /**
         * The method to do search.
         * @type {void}
         */
        scope.doSearch = () =  > {
          scope.showSpinner = true;
          scope.$evalAsync(() =  > {
              if (!cache) {
                metadataService.getMetadata(attrs.searchKey).then(
                  result =  > {
                    cache = result;
                    scope.sources = result;
                    defer.resolve();
                  },
                  error =  > {
                    defer.reject(error);
                  });
              } else {
                scope.sources = cache;
                defer.resolve();
              }

              defer.promise.then(() =  > {
                  kendoMultiSelect.setDataSource(scope.sources);
                  scope.showSpinner = false;
                  kendoMultiSelect.value(binder(scope));
                });
            });
        };

        kendoMultiSelect.bind('change', () =  > {
            binder.bind(scope, kendoMultiSelect.value());
          });

        /**
         * Set cache to null on location change
         */
        scope.$on(LOCATION_CHANGE_START, () =  > {
            cache = null;
          });

        scope.$watch(() =  > scope.$eval(attrs.currentSelectionModel), () =  > {
            if (!scope.sources) {
              if (angular.isDefined(cache) && cache !== null) {
                $timeout(() =  > {
                    scope.sources = cache;
                    kendoMultiSelect.setDataSource(scope.sources);
                    kendoMultiSelect.value(binder(scope));
                  });
              } else {
                scope.doSearch();
              }

            } else {
              kendoMultiSelect.setDataSource(scope.sources);
              kendoMultiSelect.value(binder(scope));
            }
          });
      }
    }
  }
}

When this code renders it gives call to backend service to populate sources based on the search key but scope.sources for bot instance gets the same value. It gets the value of last search service call. What I am missing here? Thanks.

1 Answer 1

3

scope is prototypically inherited in Angular, but a new scope is only created if you request it. In this case, you are simply decorating the existing scope defined by the parent that your directive is sitting in.

This means that anything added to scope will be overwritten by the same directive.

You can tell your directive to create a new scope by simply adding the scope:true property to your directive definition object:

{
  restrict : 'A',
  scope:true
  //more stuff
}

Here is a simple example to show the difference between a directive that creates a new scope, and one that does not.

(function() {
  'use strict';

  function NoNewScope() {
    return {
      restrict: 'A',
      template: [
        '<div class="form-group">',
        '  <label>Name - <code>{{name}}</code></label>',
        '  <input class="form-control" type="text" ng-model="name" />',
        '</div>'
      ].join(''),
      link: function(scope) {
        scope.name = "No New Scope";
      }
    };
  }

  function NewScope() {
    return {
      restrict: 'A',
      scope: true,
      template: [
        '<div class="form-group">',
        '  <label>Name - <code>{{name}}</code></label>',
        '  <input class="form-control" type="text" ng-model="name" />',
        '</div>'
      ].join(''),
      link: function(scope) {
        scope.name = "New Scope";
      }
    };
  }

  angular.module('my-app', [])
    .directive('noNewScope', NoNewScope)
    .directive('newScope', NewScope);

}());
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>

<div ng-app="my-app" class="container">

  <div class="row">
    <div class="col-xs-6">
      <h3>NO new scope</h3>
      <div no-new-scope></div>
      <div no-new-scope></div>
    </div>
    <div class="col-xs-6">
      <h3>NEW scope</h3>
      <div new-scope></div>
      <div new-scope></div>
    </div>
  </div>

</div>

Sign up to request clarification or add additional context in comments.

2 Comments

Isolated scope was one issue and also the cache variable which was declared globally was messing with values. Thanks for your help.
@AbhishekAgrawal - If you need a way to cache things, then I would use the built in $cacheFactory. You can inject that into your directive, and create a custom named cache. Then so long as you follow some sane naming for your cache keys you won't have to worry about declaring something globally.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.