Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modal window with Angular (UI-Router / Directive)

I have spent some time now looking into a generic way of controlling a modal window with AngularJS and none of the proposed options are anywhere near 'good' solution.

The Directive Solution

I found this demo, however the downside of that is you have to manually manage and store the state of the modal and cross-scope update it:

scope.$parent[attrs.visible] = true;

Also if you had to add more functionality like actually adding an item with a popup that would involve even more ugly code on the parent page scope.

The UI-Router Solution

This is the official guide on how to use modals with ui router.

This however is using ui.bootstrap.modal

My question is, is there any simple and elegant solution to what is quite frankly a very simple problem...

Something like this for example:

.state('popup', {
        url: '/item/add/',
        views: {
            'popupView': {
                templateUrl: "/myPopup.html",
                controller: "myPopupController"
            }
        },
        type: 'modal'
    })

And things like close, redirect, submit, all are handled in the myPopupController.

I am not seeking an explanation of why the examples are above are as they are, or how I should be using them. I am simply looking if anyone has come up with a better solution.

like image 596
SDekov Avatar asked Jul 14 '15 12:07

SDekov


1 Answers

I've been working for some time now on a modal/dialog plugin for ui-router (utilising it and ui-router-extras).

I'm in the process of porting some in house code to an open source project, and it'd be a huge benefit (certainly so for me) if you would be open to giving it a try.

We are currently using the in house version in our production application and it is working quite well. My goal is to make the open source version even better in terms of consumer API and performance.

The configuration phase

var app = angular.module('mod', ['angular.ui.router.modal', /** ui-router + ui-router-extras **/]);

app.config(function ($uiRouterModalProvider) {
  $uiRouterModalProvider.config({
    viewName: 'my_modal_view_name',
    templateUrl: '/path/to/my_modal_wrapper.html',
    rootState: 'name_of_my_apps_root_state',
    controller: 'my_modal_wrapper_controller',
    resolve: {
      common: function ($http) {
        return $http.get(apiUrl + '/common_modal_settings');
      }
    }
  });
});

Let's go through the different parts:

viewName

This is the name of the ui-view that your modal template will live in. It is required that you have a container in your page with the ui-view attribute pointing to a view of $uiRouterModalProvider.viewName that lives beside your regular ui-view for your regular content (example will be shown later).

The package provides a directive that does this for you

templateUrl

This is the base of all of your modals. Say you wanted a container with certain properties, and a $scope that is independent of the inner contents of the modal at any given point in time.

rootState

This is the name of the root state in your application. It is important that this matches that of the state that holds your root ui-view element and the modal ui-view element.

controller

The name of your common modal controller. Can also be a function (though I would always recommend a named controller that can be unit tested in isolation).

resolve

The common resolve block for each modal. This is interdependent of each modal states own resolve block. The API and behaviour regarding resolves is still in flux.


The registration-of-states phase

Now that the base behaviour is configured, this is how you would register a modal state:

.modalState('home.mySuperModal', {
  url: '/my-super-modal',
  templateUrl: 'superModal.html',
  controller: 'SuperModalController'
});

The .modalState function will register a state and reference the common settings set in the modalProvider and convert the given state definition into the final state object, looking like so:

.state('home.mySuperModal', {
  url: '/my-super-modal',
  views: {
    'modal_view_name@root_state': {
      templateUrl: 'modal_wrapper.html',
      controller: 'ModalWrapperController'
    }
  },
  $$originalState: {
    templateUrl: 'superModal.html',
    controller: 'SuperModalController'
  }
});

The markup

<!-- The outermost ui-view element -->
<html>
  <body ng-app="app">
    <div ui-view></div>
  </body>
</html>

<!-- The content for the root state of your application {living inside the outermost ui-view directive} -->
<div>
  <ui-view></ui-view>
  <ui-modal-view></ui-modal-view>
</div>

Now the container for our modal wrapper is set up. But what about the inner contents?

This is where another directive comes into play; uiModalFill. This needs to be present in your wrapping modal template. As such:

<!-- wrapping modal template -->
<div id="modal_wrapper">
  <h1>Some heading. I'm always present!</h1>

  <hr>

  <ui-modal-fill></ui-modal-fill>
</div>

Conclusion

Some final notes before (if) you feel like giving this a go:

  • A lot of the desired functionality has not yet been ported/implemented. It's a work in progress as it stands.
  • Some of the API's may very well change in the near future, including but not limited to the resolve property and the way a $state.resolve lives together with the common modal.resolve.
  • Some of the boilerplate requirements are due for a change. The API should be made easy to use (yet flexible), but right now there's too much details that the consumer must fill in.
  • The documentation is virtually non existent. The best way to figure out what is actually going on behind the scenes is to look into the source (sorry...).

So, if I haven't managed to scare you off from giving this a go - head on over to GitHub and grab angular-ui-router-modal and give it a go. I'm sort of always available on GitHub and/or on SO if you need any help.

Or if you would just like to pester me about writing some documentation. I would understand that, fully.

like image 76
Kasper Lewau Avatar answered Nov 14 '22 14:11

Kasper Lewau