0

I created a directive called dt-modal under the dt module. In my main app's module called demo, I use this dt-modal which has an isolated scope. I created this directive such that the HTML form written within the directive is transcluded since I want to reuse this modal for many different forms.

<dt-modal>
    <form ng-controller="ReviewFormController" 
          name="reviewForm" 
          novalidate 
          ng-submit="reviewForm.$valid && submitReview(review)">

      <!-- form contents here --> 

    </form>
</dt-modal>

This transcluded form has a custom controller called ReviewFormController that listens for the submit event. How can I call the close() method on the dt-modal's scope from within submitReview() in ReviewFormController?

Here is a JSBin. If you hit ESC, you can see close() in the directive run.

http://jsbin.com/cukanole/1/edit

If this isn't possible, is there a better design for this directive?

Thanks in advance!

Animal Rights
  • 9,107
  • 6
  • 28
  • 40

2 Answers2

1

My recommendation is to use $emit to trigger the event from the controller and use $on on the directly.

Controller

scope.$emit("ValueChanged", value);

In the directive the event will be captured using $on like:

$scope.$on("ValueChanged", function(event, ars){
   ... //your event has been triggered.    
});

Important:

Directives should be always independent components, if inside the directive there is a call to a method from a controller(outside the directive) this will create a dependency between my directive and the controller and of course this will force one not being able to exist without the other.

If I would have to apply a design principle to a directive it will be the S in SOLID, Single responsibility principle. Directives should be able to encapsulate and work independently.

Dalorzo
  • 19,834
  • 7
  • 55
  • 102
  • I did try this and it works, but then as part of the directive's public API, an outside component has to be sure to emit the proper event so the directive can respond accordingly. I was hoping there was some way that a directive could expose methods as a public API through the HTML hierarchy and the ReviewFormController (a child/transclude) could call any of those public API methods. – Animal Rights May 18 '14 at 01:57
  • well since it is across controllers your scenario suggests $emit. If you need "public" methods that sounds more like you could use a service for that. – Dalorzo May 18 '14 at 02:05
  • I thought about putting the modal opening/closing in a service, but that would require DOM manipulation inside the service and that seemed to go against the Angular conventions. A modal form seems like such a common feature. How do people typically handle this? Is there a better approach than what I have started? – Animal Rights May 18 '14 at 02:31
  • Using parent scope on child controller andadding the attribute to directive and watch it is the other option – Dalorzo May 18 '14 at 02:35
  • The parent scope of the transclude's controller isn't available in ReviewFormController. I think this is because the dt-modal directive has an isolate scope. – Animal Rights May 18 '14 at 02:38
  • Chrome has a plugin for angular and scopes take a look at it. – Dalorzo May 18 '14 at 02:50
  • im not sure how that helps. make my form as part of the template and bypass transclusion? ... im starting to think that i should rethink how i design my modal. – Animal Rights May 22 '14 at 07:34
  • All controllers has a parent the based parent is $rootScope – Dalorzo May 22 '14 at 12:14
  • yea but using $rootScope isn't really ideal if you want this to be used in any application without possible $rootScope property conflicts. – Animal Rights May 24 '14 at 02:11
1

Since you are using an isolated scope, you could pass a control object to the directive...

<dt-modal id="review-form-modal" api="modal.api">

and add the close method to it via two-way binding:

scope: {
  api: '='
},
link: function($scope, $el, attrs) {
  $scope.api = {
    close: function() {
      $el.css({
        display: 'none'
      })
    }
  }
...

Then ng-click can use the control object to call close:

<button type="submit" ng-click="modal.api.close()">Submit</button>

If you want to try this code, here it is on Plunker.

Community
  • 1
  • 1
j.wittwer
  • 9,497
  • 3
  • 30
  • 32