Tim Rogers
Tim Rogers

Reputation: 21713

How do you create a specialized version of an existing directive?

My plan was to create a specialized version of the ngOptions directive that goes and fetches a select's options from an external JSON source. My plan was to some inject the ngOptions directive (the base) into my directive (the specialization).

angular.module('App.directives')
  .directive ("selectDatasource", function ($http, ngOptions) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            scope.dataSource = {};
            attrs.ngOptions = "k as v for (k,v) in dataSource";
            ngOptions.link (scope.element, attrs);
            $http.get(attrs).success(function(r) { scope.dataSource = r; });
        }
    };
});

You could then use this as follows:

<select name="myOption" select-datasource="http://mysite/datasource"/>

However, ngOptions fails to inject and I can't see how directives can be injected. I'm not sure this is the best approach anyway. If not, what is a good approach in general terms for specializing one of the built-in directives?

Upvotes: 2

Views: 123

Answers (2)

Yoshi
Yoshi

Reputation: 54649

You could do the following:

app.directive('selectDatasource', ['Loader', function (Loader) {
  return {
    scope: true,
    compile: function (tElement, attr) {
      attr.ngOptions = 'v.value as v.label for v in values';

      return function link(scope) {
        Loader.load(attr.selectDatasource).then(function (response) {
          scope.values = response;
        });
      };
    }
  };
}]);

though because of the predefined format of ngOptions you'd lose a lot of flexibility. Thus it might be better to use something like:

app.directive('datasource', ['Loader', function (Loader) {
  return function (scope, element, attr) {
    // add to scope
    scope[attr.datasourceTarget] = [];

    // observer changes
    attr.$observe('datasource', function (nv, ov) {
      if (nv !== ov) {
        Loader.load(nv).then(function (response) {
          scope[attr.datasourceTarget] = response;
        });
      }
    });
  };
}]);

with:

<select
  data-ng-model="test2"
  data-datasource="http://mysite/datasource"
  data-datasource-target="values"
  data-ng-options="v.value as v.label for v in values"
></select>

demo:

http://jsbin.com/rukemaha/4/

Upvotes: 1

package
package

Reputation: 4801

Do not remove ng-options from select. Instead write new directive that fetches data from external source and sets scope variable, which then is used by the ng-options. Something like this:

//template
<select ng-model="myModel.myValue" ng-options="k as v for (k,v) in dataSource" select-data-source="http://mysite/datasource/"></select>

//directive
module.directive('selectDataSource', function($http) {
  return {
    link: function($scope, $element, $attrs) {
      var url = $attrs.selectDataSource;
      $http.get(url).success(function(response) {
        $scope.dataSource = response;
      });
    }
  }
})

Upvotes: 1

Related Questions