1

I'm trying to write a simple directive for modals in order to get them closed when I submit the form inside them.

I have 2 modals : Loginmodal and signup modal, respectively ordered in my html.

The modals are inside a modals.html which is called with a ng-include inside a html file which is also called in ng-include, so we have here a childscope of a childscope

My directive

.

directive('myModal', function() {
    return {
        restrict: 'A',
        scope : { },
        link: function(scope, element, attr) {
            scope.$parent.$parent.$parent.dismiss = function() {
                console.log(element.hasClass('in')); //returns false
                element.modal('hide');// doesn't work because element is always the second modal
                };
            $(element).bind('shown.bs.modal', function () {
                if (!scope.$$phase && !scope.$root.$$phase)
                    scope.$parent.$parent.$parent.$apply();
                });
            }
        };
});

I don't know why the element inside link is override by the last element?

I'm applying my-modal directive to both my modals but I open the first.

Any help would be very appreciated

Aysennoussi
  • 3,720
  • 3
  • 36
  • 60

1 Answers1

0

Are you sure scope.$parent.$parent.$parent is different from your two modals? If they have the same grand-old-parent, then only the last one will actually define what is that grandOldParent.dismiss function.

Honestly, I am a bit horrified when I see this scope.$parent.$parent.$parent thing. It is alway recommended to avoid using $parent, so chaining 3 of them is even crazier.

Do you really need an isolated scope (scope: {}) if in fact you manipulate its upper parents? Why not using an inherited scope?

There is probably a cleaner way to deal with these scopes. Are you using, or have you looked at https://angular-ui.github.io/bootstrap/ where modals are angularized using a $modal service on which you define template and controller?

Also this is useless:

if (!scope.$$phase && !scope.$root.$$phase)
  scope.$parent.$parent.$parent.$apply();
});

And should be replaced by

if (!scope.$root.$$phase) {
  scope.$apply();
}

(or simply scope.$apply if this event is asynchronous and outside of Angular digest cycle).

Also be aware that scope.$apply does in fact a $rootScope.$digest so it doesn not matter which scope you $apply.

UPDATE: What you could do is add a ng-click="close" in your directive html, and add that to its link function:

scope.close = function() {
  // do stuff on this directive instance
  console.log(element.hasClass('in'));

  // close the modal
  var parent = scope;
  while (!parent.dismiss) { parent = parent.$parent; }
  parent.dismiss();
}

This way each instance of your directive has its own close function (so doesn't override any other one) but still calls the unique dismiss that belongs to the one modal every instance use.

floribon
  • 19,175
  • 5
  • 54
  • 66
  • I know it's ugly to use the $parent.$parent.$parent. The inner developer of myself, tells me that this is crap and there should be something simpler. I don't think the `scope.$parent.$parent.$parent` is different for the two modals because they're in the same file. I'll try to fix stuff and be back to you. – Aysennoussi Apr 21 '15 at 23:11
  • for the Edit, It's not yet available for Angular 1.3 – Aysennoussi Apr 21 '15 at 23:11
  • Ok, then how do I make it for that the directive doesn't override the first modal? Thank you for your time – Aysennoussi Apr 21 '15 at 23:13
  • I'm using an older verison of angular bootstrap with angular 1.3 (even tried 1.4.0-beta.6) and it works like a charm. It alows a code way cleaner where you only load a modal template when you actually need it and with its own controller. – floribon Apr 21 '15 at 23:13
  • Well I don't know what you're trying to do by overriding this parent parent parent dimiss method. Couldn't you just write `scope.dimiss = ...` ? try with `scope: true` instead of `scope: {}` – floribon Apr 21 '15 at 23:14
  • The thing is I can't install it with bower. Conflict in dependencies. – Aysennoussi Apr 21 '15 at 23:14
  • Ok did the replacements mentioned in your last comment : When I call $scope.dismiss(); I get >undefined is not a function Which means it's not in the scope. – Aysennoussi Apr 21 '15 at 23:18
  • Did you miss the fact that my modals are in 2 levels of ng-include ? – Aysennoussi Apr 21 '15 at 23:20
  • Any way you could reproduce that in a minimalist code into a jsfiddle? that would really help get you the best answer – floribon Apr 21 '15 at 23:38
  • ng-include create inherited scopes so you should still be able to access `scope.dismiss` without reaching a parent – floribon Apr 21 '15 at 23:39
  • I'm creating the plunker. It would take some time, so forgive me please if it gets late. I think when applying the same directive on two modals in the same file, the second overrides the first definition of dismiss – Aysennoussi Apr 21 '15 at 23:48
  • @Sekai I've added a workaround in my answer (see update). It is still not clean but should at least avoid your issue. I also try to reach `dismiss` by browsing parents one by one so you are not dependent on a strict scope hierarchy. I guess it is the trade off when trying to combine vanilla bootstrap modals with angular. – floribon Apr 21 '15 at 23:54
  • Thanks for the workaround. Actually I'm closing the modal after a successful login/ signup , so I don't think ng-click would do the job for me – Aysennoussi Apr 21 '15 at 23:57
  • I actually got inspired from this. http://stackoverflow.com/a/19668616/3062150 but it seems it wouldn't work when you apply the same directive attribute to 2 different modals in the same file, one would override the other.. – Aysennoussi Apr 22 '15 at 00:00
  • Any event is fine, as long as the logic is within your directive code and not in that shared `dismiss` function. – floribon Apr 22 '15 at 00:00