24

I've got a modal with a registration form. The same form should be displayed at the bottom of the landing page not in a modal.

Currently my controller that handles registration modal takes $modalInstance as one of its parameters along $scope etc. If I add ng-controller="SignUpCtrl" to an element in the landing page, it doesn't work, because the controller wasn't created via $modal.open method and so Angular complains about Unknown provider: $modalInstanceProvider <- $modalInstance.

I've got a service for registering users (authService.signUp(data).then/catch...), but the controller itself does a bit more - handles input, emits events (e.g. with translated error messages), sets cookies etc.

What's the best way to handle such case without duplicating almost whole controller code? Should I move the code from controller into yet another, higher-level service?

szimek
  • 6,404
  • 5
  • 32
  • 36
  • Are you using the "UI-Bootstrap" modal http://angular-ui.github.io/bootstrap/#/modal ? It'd help if you can provide some code. I am currently working through a similar issue, it seems. – Shawn Flahave May 21 '14 at 13:57
  • I solved it for now by using events. Instead of passing `$modalInstance` to the controller that handles the form and sign up logic and closing modal there, I'm just triggering an event and listen to it in the controller that called `$modal.open`. Here's a snippet: https://gist.github.com/szimek/76fc9fc295f317c3cac8 – szimek May 21 '14 at 17:14
  • I had already given an answer,it might be helpful [go][1] [1]: http://stackoverflow.com/questions/25872280/angular-bootstrap-ui-modal-with-same-controller-instead-of-new-controller/26624403#26624403 – Sajin M Aboobakkar Oct 29 '14 at 07:13

3 Answers3

27

After struggling for a long while I found a easier trick to reuse our Controller for both modal and normal case.

I found that we can pass caller's scope to modal controller, so I pushed modalInstance into $scope and passed it to the modal controller. Now you don't have unknown provider problem because $scope is a well known one.

Below is an example:

CallerController = function($rootScope, ...) {
   var modalScope = $rootScope.$new();
   modalScope.modalInstance = $modal.open({
        templateUrl: tempUrl,
        controller: ReusableModalController,
        scope: modalScope // <- This is it!
    });

    modalScope.modalInstance.result.then(function (result) {
        // Closed
    }, function () {
        // Dismissed
    });
};

ReusableModalController = function($scope, ...){
    var dataToSendBack = 'Hello World';
    $scope.modalInstance.close(dataToSendBack);
};

Cheers!

KinoP
  • 1,532
  • 15
  • 23
  • 2
    Just beautiful! I ended up using `$scope.modalInstance = $modal.open({ [...] scope: $scope });` – jsruok Apr 17 '15 at 11:26
  • @jsruok Be careful not to overwrite CallerController's variables if you are not willing to then :) – KinoP Apr 20 '15 at 09:16
  • Thanks for this! I was pulling my hair out – HoffZ Jul 08 '15 at 07:44
  • What's the use of `var modalScope = $rootScope.$new();`. According to the https://angular-ui.github.io/bootstrap/#/modal docs, `scope - a scope instance to be used for the modal's content (actually the $modal service is going to create a child scope of a provided scope). Defaults to $rootScope`. – Aniket Sinha Sep 28 '15 at 10:42
  • 1
    @Aniket Sinha So like it says, a modal uses a scope, and the modal will automatically create one if we did not specify it in `$modal.open({... scope: modalScope ...})` . In this case, we want to pass our `modalInstance` into the modal in a tricky way, that is, I create a scope with `var modalScope = $rootScope.$new();` then push `modalInstance` into the fresh `modalScope` , lastly tell the modal "don't create a scope, use what I give you". Sorry for my bad English... – KinoP Sep 29 '15 at 16:44
  • If you want to avoid using $scope: I found out that it's also possible to pass the modalScope via Angular DI. – HoffZ Aug 05 '16 at 11:20
  • @KinoP can I open Modal in current controller using the same controller? – CodeGenius Mar 18 '18 at 09:36
  • @Umar I'm surprised that there are still people using Angular 1. You can try passing `this` to controller, and `$scope` to scope, but it sounds very dangerous to me. – KinoP Mar 22 '18 at 06:02
  • @KinoP yes for some reason I had to use Angular 1 :) Thanks for your reponse, I got it working by passing `$scope` to my modal method. – CodeGenius Mar 27 '18 at 07:45
6

If you are using ui-router you can easily use the resolve from ui-router to provide a $uibModalInstance (was $modalInstance before):

$stateProvider
.state('conductReview', {
    url: '/review',
    templateUrl: '/js/templates/review.html',
    controller: 'yourController',
    resolve: {
        $uibModalInstance: function () { return null; } // <- workaround if you want to use $uibModalInstance in your controller.
    }
})

That way you can use your modal controller like a normal controller. If you inject $uibModalInstance in your controller it will be null.

Thomas
  • 2,127
  • 1
  • 32
  • 45
0

If you want to use same controller for both modal form as well as landing page form, make use of

modalInstance.result.then(function (selectedItem) { $scope.selected = selectedItem; }, function () { $log.info('Modal dismissed at: ' + new Date()); });

You can get all the data from modal form in selectedItem and use this in landing page form. Also, how do you open the modal. If it is through a button, bind that ng-model to open modal using $modal.open.Don't create separate controller for your modal.Use the same one as your landing page. This way you can use 1 controller to open modal as well as other function after the modal is closed.

PS: Code snippet given here is from angular ui's page. Check that page's documentation for selectedItem

Aniket Sinha
  • 6,001
  • 6
  • 37
  • 50
  • Thanks, but there's a problem with this solution, because (if I understood docs correctly) you have to close modal to pass the data. What if you close the modal, but signing up fails e.g. because the email is already taken? That's why I'm handling actual logic inside the controller that's passed to `$modal.open` - to be able to handle errors without closing the modal. – szimek May 21 '14 at 17:09
  • Yes, modal needs to be closed. But you can still perform logic inside the modal to check available email IDs. Why don't you create a plunk or fiddle, it'll help people in solving your problem – Aniket Sinha May 22 '14 at 05:05
  • Sure, but that's exactly the problem. If I move the logic to modal's controller that depends on `$modalInstance`, the question is how to reuse it elsewhere, where there's no `$modalInstance`. Here's my current solution https://gist.github.com/szimek/76fc9fc295f317c3cac8 that doesn't require too much code changes (e.g. creating a new service), but maybe there's something better. – szimek May 22 '14 at 08:23
  • Why dont you use logic directly in Modal Controller. If you want to reuse the code, you can always use service. EDIT : IMO you should use service for your logic, and add that service as a dependency in your modal controller. – Aniket Sinha May 22 '14 at 11:36
  • I thought about using a service, but it's kind of strange to have a service just for sign-up + some logic, especially that I already have `authService` that does basic sign up stuff. Using events seems to be much simpler in this case. – szimek May 22 '14 at 11:48
  • 1
    Thanks I'm using that code to pass data from modal to the caller controller and it works like a charme :D – Whisher Sep 24 '15 at 08:59