$mdDialog stacked/nested dialogs, is it possible?

20.9k views Asked by At

I want to open one $mdDialog on top of the other. If possible, have unlimited overlapping dialogs.

Any ideas?

13

There are 13 answers

7
Salal Aslam On BEST ANSWER

No, it is not possible right now to have multiple $mdDialog. Honestly I really need this feature and tried it to get it to work but no success so far. Lets hope they allow this feature in future releases.
Although there is discussion here, you can find something useful there.

NOTE: This is no longer the correct answer, as opposed to the time period it was answered, look below for more answers.

0
abheist On

Yes it's possible, just add "skipHide: true" where you calling mdDialog.. just like:

$scope.xyz = function(anything) {
  $mdDialog.show({
    controller: "xyzController",
    skipHide: true,
    templateUrl: 'path-to-partial/xyzDialog.html',
    parent: angular.element(document.body),
    clickOutsideToClose: true
  })
}

Update: this is for angularJs (Angular 1)

0
HoffZ On

From Angular Material version 1.1.2 an above: Use option multiple.

Use option skipHide for previous versions.

Example

$mdDialog.show({
    template: 'asdf'
    controller: "xyzController",
    multiple: true // Replace with "skipHide" on Angular Material 1.1.1 or before
})
0
Bob Sheehan On

skiphide has been deprecated. Use multiple key instead. See documentation here

here is a code snippet

myCtrl.demoClick = function moreInfo(thing) {
        $mdDialog.show({
            controllerAs: 'dialogCtrl',
            clickOutsideToClose: true,
            bindToController: true,
            controller: function ($mdDialog) {
                this.click = function click() {
                    $mdDialog.show({
                        preserveScope: true,
                        multiple: true,
                        controllerAs: 'dialogCtrl',
                        controller: function ($mdDialog) {
                            this.click = function () {
                                $mdDialog.hide();
                            }
                        },
                        template: '<md-dialog class="confirm"><md-content>I am in a 2nd dialog!<md-button class="md-raised" ng-click="dialogCtrl.click()">Confirm!</md-button></md-content></md-dialog>'
                    })
                }
            },
            autoWrap: false,
            template: '<md-dialog class="stickyDialog" data-type="{{::dialogCtrl.thing.title}}"><md-content>I am in a dialog!<md-button class="md-raised" ng-click="dialogCtrl.click()">Click me to do something</md-button></md-content></md-dialog>',
            locals: {
                thing: thing
            }
        })}
2
Vincenzo On

As Gabriel Anzaldo Alvarado wrote in the comment, it is possible as you can see on this Plunker: http://plnkr.co/edit/Ga027OYU5nrkua3JxNRy?p=preview

In addition, you can add some css classes to get the same background grey overlay: https://github.com/angular/material/issues/7262

._md-dialog-backdrop:nth-of-type(even) {
    z-index: 81;
}

._md-dialog-backdrop:nth-of-type(odd) {
    z-index: 79;
}

.md-dialog-container:nth-of-type(even) {
    z-index: 80;
}

.md-dialog-container:nth-of-type(odd) {
    z-index: 82;
}

UPDATE:

From Angular Material v1.1.2 the option skipHide has been replaced by multiple.

1
Dorival On

Gabriel Anzaldo Alvarado gave the right answer in my opinion, the correct answer is shared in a Plunker link. But as requested by many users, I'm adding the actual code in case the link becomes unavailable in the future.

Basically, while opening your dialog using the .show({}) function, add the option skipHide: true.

HTML

<body>
 <div ng-controller="AppCtrl as ctrl" 
    ng-cloak="" 
    class="virtualRepeatdemoVerticalUsage" 
    ng-app="MyApp">
  <md-content layout="column">
    <md-button ng-click="ctrl.moreInfo(1)">
       Open Dialog
    </md-button>
  </md-content>
 </div>
</body>

JavaScript

(function () {
  'use strict';

  angular
  .module('MyApp', ['ngMaterial'])

  .controller('AppCtrl', function ($interval, $mdDialog) {
    var vm = this;

    vm.moreInfo = function moreInfo(thing) {
      $mdDialog.show({
        controllerAs : 'dialogCtrl',
        clickOutsideToClose : true,
        bindToController : true,
        controller : function ($mdDialog) {
          this.click = function click() {
            $mdDialog.show({
              controllerAs : 'dialogCtrl',
              controller : function ($mdDialog) {
                this.click = function () {
                  $mdDialog.hide();
                }
              },
              preserveScope : true,
              autoWrap : true,
              skipHide : true,
              template : '<md-dialog class="confirm"><md-content><md-button ng-click="dialogCtrl.click()">I am in a 2nd dialog!</md-button></md-content></md-dialog>'
            })
          }
        },
        autoWrap : false,
        template : '<md-dialog class="stickyDialog" data-type="{{::dialogCtrl.thing.title}}"><md-content><md-button ng-click="dialogCtrl.click()">I am in a dialog!</md-button></md-content></md-dialog>',
        locals : {
          thing : thing
        }
      })
    }
  });
})();

The code above worked for me.

As noted by Vincenzo in another answer, while stacking mdDialogs, the dialogs beneath will not grey-out, there is a CSS trick to solve that: https://stackoverflow.com/a/38013682/366662

UPDATE

This answer works for version 1.1.1, from version 1.1.2 the Material team changed the property from skipHide to multiple. So, be careful when copy-pasting the snippet. Check your Material version and use the correct property accordingly.

0
Patrik Laszlo On

in the latest AngularJs Material dialog, you can find this solution: https://material.angularjs.org/latest/api/service/$mdDialog#multiple-dialogs

It uses multiple dialog preset or configuration.

6
kanpinar On

Absolutely not possible.* for now. It is understandable for some situations but other way is needed too. So, I preferred to use custom popup dialogs variates for same functionalities. Otherwise md-dialog gives pain to doing job.

  • Yes I did that but if 3 nested needed then writing code ok but another one can not understand it. You can do the job by giving scope and preservescope parameters then manage when to close and which to open next ...brrrr...
0
Shannon Hochkins On

I got this to work with very little effort and a bit of angular hacking.

To make things clear, I'm using Angular v1.5.3 & Angular Material v1.0.6.

With previous versions if you add skipHide: true to your dialog definition object, it will allow multiple dialogs. Your issue then comes to the cancel button which will close the wrong dialog box.

The solution is rather calling $mdDialog.cancel we want to call $mdDialog.hide as it correctly resolves the right dialog box. Rather than making sure you have setup every instance correctly, or even making sure 3rd party libs also follow this pattern, we can decorate the $mdDialogProvider.

$provide.decorator

$provide.decorator(name, decorator);

Register a service decorator with the $injector. A service decorator intercepts the creation of a service, allowing it to override or modify the behavior of the service. The object returned by the decorator may be the original service, or a new service object which replaces or wraps and delegates to the original service.

angular.module('module').config(function($provide) {
    $provide.decorator('$mdDialog', function($delegate) {
        var methodHandle = $delegate.show;
        function decorateDialogShow() {
            var args = angular.extend({}, arguments[0], {
                skipHide: true
            });
            return methodHandle(args);
        }
        $delegate.show = decorateDialogShow;
        $delegate.cancel = function() {
            return $delegate.hide(null);
        }
        return $delegate;
    });
});

The above will simply replace the cancel method, with the existing and working hide method. Also sets a global default so that skipHide is set initially on all dialog boxes.

Winner winner!

2
Vinod Patil On

UPDATE: As per @Magnus, it was updated to multiple as of v1.1.2


Add skipHide: true to the second dialog's options object.

This works for me: http://webiks.com/mddialog-with-a-confirmation-dialog/

0
Hubert Pietruczuk On
 $mdDialog.show({
     parent: angular.element(document.body),
     templateUrl: 'template.html',
     clickOutsideToClose: true,
     fullscreen: true,
     preserveScope: true,
     autoWrap: true,
     skipHide: true,
     controllerAs: 'customDialog',
     controller: function ($mdDialog) {
        this.callNewDialog = function (dialogCallback) {
            dialogCallback();
        };
      }});

call in view:

ng-click="customDialog.callNewDialog(vm.addNewCustomer)"

and vm.addNewCustomer will be a function that open new dialog

0
Valery Kozlov On

Actually you can use mdPanels. Little snippet:

return $q(function(resolve, reject){
         $mdPanel.open(Object.assign({
            hasBackdrop: true,
            zIndex: 85, //greater than modal and lower than autocomplete\select
            locals: Object.assign({
                onClose: resolve
            }, locals),
            template: getCommonTemplate(template, heading),
            bindToController:true,
            controller: 'PanelDummyController as $ctrl',
            panelClass: 'rl-modal-panel',
            position: $mdPanel.newPanelPosition()
                .absolute()
                .center()
        }))


 });


controller('PanelDummyController', function (mdPanelRef) {
    'ngInject';

    const close = () => mdPanelRef.close().then(() => {
        this.onClose(Object.assign({}, this));
    });

    this.$mdDialog = {
        cancel: close,
        hide: close
    };
});

and add little styling to class. It's not fully copy of modal, but it's quite good implementation and could be improved to fully copy.

2
gabrielAnzaldo On

Here is a workaround to have nested dialogs.

The idea is when the second is open save the state of the first, and when the second is closed launch the first dialog again.