简体   繁体   中英

AngularJS override directive controller function

Here is the problem. I have some 3rd party directive called main-directive .

app.directive('mainDirective', function() {
  return {
    scope: {
      foo: '&'
      // attrs
    },
    controller: function($scope) {

      $scope.click = function() {
        window.alert($scope.foo());
      }

    },
    template: '<button ng-click="click()">Click me</button>'
  }
});

So I want to make my own directive called parent-directive which assign application specific default values to third party directive attributes.

app.directive('parentDirective', function() {
  return {
    scope: {
      foo: '&?',
      attr2: '='
        // lots of attrs
    },
    controller: function($scope) {


      $scope.attr1 = "some default value"

      $scope.foo = function() {
        return "not overrided"
      }

      if (this.foo) {
        $scope.foo = this.foo
      }

    },
    template: '<div class="some-styling"><main-directive foo="foo()" attr1="attr1" attr2="attr2"></main-directive></div>'
  }
});

What if I want to make another child-directive that keeps parent-directive logic. Overloading attribute is easy i can use "compile" function. But what about overriding functions is it possible?

app.directive('childDirective', function() {

  return {
    scope: false,
    require: 'parentDirective',
    link: function(scope, element, attr, controller) {

      controller.foo = function() {
        return "overrided";
      }

    },
    compile: function(element, attr) {
      attr.attr2 = "attr2";
    }
  }
});

Whole thing can be easily done by using child scope instead of isolated. Or by using extending by template. But if I extends directive with template I would have to copy parent "scope" and "template" definition to child-directive and forward all the non-default attributes this doesn't seem like an elegant solution.

So the key question, is there a way to override parent-directive function using isolated scope without forwarding attributes.

Here is the DEMO

Ok, I have done some research and it turns out that there can be several approaches there

Scope inheritance

Since child-directive is not creating own scope it just creating new methods at parent-directive parent scope. So we can modify attributes during compile and specify overridden foo method.

app.directive('parentDirective', function() {
  return {
    scope: {
      fooImpl: '&?',
      // lots of attrs
    },
    controller: function($scope) {

      $scope.foo = function() {
        if ($scope.fooImpl) {
          return $scope.fooImpl();
        }
        return "not overrided";
      }

    },
    template: '<div class="some-styling"><main-directive foo="foo()"></main-directive></div>'
  }
});

app.directive('childDirective', function() {

  return {
    scope: false,
    require: 'parentDirective',
    controller: function($scope) {

      $scope.foo = function() {
        return "overrided";
      }

    },
    compile: function(element, attr) {
      attr.fooImpl = "foo()";
    }
  }
});

Here is the DEMO1

Add to isolated scope

Angular provides special function. That can get isolated scope from element. So we can override our method during linking phase.

app.directive('parentDirective', function() {
  return {
    scope: {
      fooImpl: '&?',
      // lots of attrs
    },
    controller: function($scope) {

      $scope.foo = function() {
        if ($scope.fooImpl) {
          return $scope.fooImpl();
        }
        return "not overrided";
      }

    },
    template: '<div class="some-styling"><main-directive foo="foo()"></main-directive></div>'
  }
});

app.directive('childDirective', function() {

  return {
    scope: false,
    require: 'parentDirective',
    link: function(scope, element, attr) {
      var innerScope = angular.element(element[0]).isolateScope();
      innerScope.foo = function() {
        return "overrided";
      }
    }
  }
});

Here is the DEMO2

Controller method

If we use controllerAs syntax. That means we exposing controller object variables as a scope. We can override function in child directive during linking phase.

app.directive('parentDirective', function() {
  return {
    scope: {
      fooImpl: '&?',
      // lots of attrs
    },
    controller: function($scope) {

      var vm = this;

      vm.foo = function() {
        return "not overrided";
      }

    },
    controllerAs : 'vm',
    template: '<div class="some-styling"><main-directive foo="vm.foo()"></main-directive></div>'
  }
});

app.directive('childDirective', function() {

  return {
    scope: false,
    require: 'parentDirective',
    link: function (scope, element, attr, controller) {

       controller.foo = function() {
        return "overrided";
      }


    }
  }
});

Here is the DEMO3

Transclusion

Practically you can do the same thing with seperate parent and child directive and using transclusion. But anyway it would be combination of above approaches. Thanks for "Extending an existing directive in AngularJS"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM