带角度的模态窗口(UI-Router / Directive)

时间:2021-08-25 12:30:19

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.

我花了一些时间来研究使用AngularJS控制模态窗口的通用方法,并且所提出的选项都不是“好”解决方案。

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.

这是关于如何使用ui路由器的模态的官方指南。

This however is using ui.bootstrap.modal

然而,这是使用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.

而关闭,重定向,提交等所有内容都在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.

我不是要解释为什么上面的例子是这样的,或者我应该如何使用它们。我只想看看是否有人提出了更好的解决方案。

3 个解决方案

#1


4  

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

我已经在ui-router的模态/对话插件上工作了一段时间(利用它和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.

我们目前在我们的生产应用程序中使用内部版本,它运行良好。我的目标是在消费者API和性能方面使开源版本更好。

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).

这是你的模态模板将存在的ui-view的名称。要求你的页面中有一个容器,其ui-view属性指向$ uiRouterModalProvider.viewName的视图,它位于你的常规ui旁边查看常规内容(示例将在稍后显示)。

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.

这是所有模态的基础。假设您想要一个具有特定属性的容器,以及在任何给定时间点独立于该模式的内部内容的$ scope。

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.

这是应用程序中根状态的名称。重要的是,这与保存根ui-view元素和模态ui-view元素的状态匹配。

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.

每个模态的公共解析块。这与每个模态状态自身的解析块相互依赖。关于做出决议的API和行为仍在不断变化。


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:

.modalState函数将注册一个状态并引用modalProvider中设置的公共设置,并将给定的状态定义转换为最终状态对象,如下所示:

.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:

这是另一个指令发挥作用的地方; uiModalFill。这需要出现在包装模式模板中。因此:

<!-- 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.
  • 一些API可能会在不久的将来发生变化,包括但不限于resolve属性以及$ state.resolve与常见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.
  • 一些样板要求是由于变化。 API应该易于使用(但是灵活),但是现在消费者必须填写太多细节。
  • 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.

所以,如果我没有成功地吓跑你,那么就把它放到GitHub上并抓住angular-ui-router-modal并试一试。如果您需要任何帮助,我总是可以在GitHub和/或SO上使用。

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

或者,如果你想纠缠于编写一些文档。我完全理解这一点。

#2


0  

Well showing a directive is not something that is nicely done in only declarative way via directives. Also a solution via router/state handling is not something I would do personally.

很好地显示一个指令并不是通过指令以声明方式很好地完成的。通过路由器/状态处理的解决方案也不是我个人会做的事情。

My approach would be to handle modals with a Angular Service. With a service you can imperatively open your modals.

我的方法是使用Angular Service处理模态。通过服务,您可以强制打开您的模态。

You already mentioned the ui.bootstrap.modal which in my opinion is a pretty good solution. But it is also very flexible.

您已经提到了ui.bootstrap.modal,在我看来这是一个非常好的解决方案。但它也非常灵活。

I created a simple dialogService with a method openConfirmDialog which has its own controller:

我用一个方法openConfirmDialog创建了一个简单的dialogService,它有自己的控制器:

(function() {
    'use strict';

    angular.module('app').factory('dialogService', dialogService);

    function dialogService($modal) {
        return {
            openConfirmDialog: openConfirmDialog
        };

        function openConfirmDialog(texts, props) {
            texts = texts || {};
            texts = angular.extend({
                title: '',
                okText: 'Yes',
                cancelText: 'No',
                bodyText: ''
            }, texts);

            props = props || {};
            props = angular.extend({
                okClass: 'btn-primary',
                cancelClass: 'btn-default'
            }, props);

            return $modal.open({
                templateUrl: 'dialogconfirm.html',
                controller: ConfirmController,
                controllerAs: 'vm',
                resolve: {
                    texts: function() {
                        return texts;
                    },
                    props: function() {
                        return props;
                    }
                }
            }).result;
        }

        function ConfirmController($modalInstance, texts, props) {
            var vm = this;
            vm.texts = texts;
            vm.props = props;

            vm.ok = function() {
                $modalInstance.close();
            };

            vm.cancel = function() {
                $modalInstance.dismiss();
            };
        }
    }
}());

This can be used very easily directly in my controllers:

这可以直接在我的控制器中使用:

  vm.openDialog = function() {
    dialogService.openConfirmDialog({
      bodyText: 'Do you really want to close me'
    });
  };

Here is a working Plunker

这是一个有效的Plunker

With this solution I don't have to pollute my controller and/or HTML-view with code for the model. The modal code is very well encapsulated.

使用此解决方案,我不必使用模型的代码污染我的控制器和/或HTML视图。模态代码封装得非常好。

#3


0  

Have you looked into AngularStrap http://mgcrea.github.io/angular-strap/#/modals ?

您是否查看了AngularStrap http://mgcrea.github.io/angular-strap/#/modals?

You can pass in a templateUrl in which you can bind a controller to your modal instance, therefore giving you full control (by setting ng-controller="ModalCtrl" on the top level element of the modal template).

您可以传入一个templateUrl,您可以在其中将控制器绑定到模态实例,从而为您提供完全控制(通过在模态模板的*元素上设置ng-controller =“ModalCtrl”)。

You can programatically fire the modal via ng-click or set attributes in your html using the bs-modal directive.

您可以使用bs-modal指令通过ng-click或在html中设置属性以编程方式触发模态。

http://plnkr.co/edit/h9BwU0dFTTdw59b6oTWv?p=preview

http://plnkr.co/edit/h9BwU0dFTTdw59b6oTWv?p=preview

#1


4  

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

我已经在ui-router的模态/对话插件上工作了一段时间(利用它和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.

我们目前在我们的生产应用程序中使用内部版本,它运行良好。我的目标是在消费者API和性能方面使开源版本更好。

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).

这是你的模态模板将存在的ui-view的名称。要求你的页面中有一个容器,其ui-view属性指向$ uiRouterModalProvider.viewName的视图,它位于你的常规ui旁边查看常规内容(示例将在稍后显示)。

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.

这是所有模态的基础。假设您想要一个具有特定属性的容器,以及在任何给定时间点独立于该模式的内部内容的$ scope。

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.

这是应用程序中根状态的名称。重要的是,这与保存根ui-view元素和模态ui-view元素的状态匹配。

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.

每个模态的公共解析块。这与每个模态状态自身的解析块相互依赖。关于做出决议的API和行为仍在不断变化。


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:

.modalState函数将注册一个状态并引用modalProvider中设置的公共设置,并将给定的状态定义转换为最终状态对象,如下所示:

.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:

这是另一个指令发挥作用的地方; uiModalFill。这需要出现在包装模式模板中。因此:

<!-- 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.
  • 一些API可能会在不久的将来发生变化,包括但不限于resolve属性以及$ state.resolve与常见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.
  • 一些样板要求是由于变化。 API应该易于使用(但是灵活),但是现在消费者必须填写太多细节。
  • 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.

所以,如果我没有成功地吓跑你,那么就把它放到GitHub上并抓住angular-ui-router-modal并试一试。如果您需要任何帮助,我总是可以在GitHub和/或SO上使用。

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

或者,如果你想纠缠于编写一些文档。我完全理解这一点。

#2


0  

Well showing a directive is not something that is nicely done in only declarative way via directives. Also a solution via router/state handling is not something I would do personally.

很好地显示一个指令并不是通过指令以声明方式很好地完成的。通过路由器/状态处理的解决方案也不是我个人会做的事情。

My approach would be to handle modals with a Angular Service. With a service you can imperatively open your modals.

我的方法是使用Angular Service处理模态。通过服务,您可以强制打开您的模态。

You already mentioned the ui.bootstrap.modal which in my opinion is a pretty good solution. But it is also very flexible.

您已经提到了ui.bootstrap.modal,在我看来这是一个非常好的解决方案。但它也非常灵活。

I created a simple dialogService with a method openConfirmDialog which has its own controller:

我用一个方法openConfirmDialog创建了一个简单的dialogService,它有自己的控制器:

(function() {
    'use strict';

    angular.module('app').factory('dialogService', dialogService);

    function dialogService($modal) {
        return {
            openConfirmDialog: openConfirmDialog
        };

        function openConfirmDialog(texts, props) {
            texts = texts || {};
            texts = angular.extend({
                title: '',
                okText: 'Yes',
                cancelText: 'No',
                bodyText: ''
            }, texts);

            props = props || {};
            props = angular.extend({
                okClass: 'btn-primary',
                cancelClass: 'btn-default'
            }, props);

            return $modal.open({
                templateUrl: 'dialogconfirm.html',
                controller: ConfirmController,
                controllerAs: 'vm',
                resolve: {
                    texts: function() {
                        return texts;
                    },
                    props: function() {
                        return props;
                    }
                }
            }).result;
        }

        function ConfirmController($modalInstance, texts, props) {
            var vm = this;
            vm.texts = texts;
            vm.props = props;

            vm.ok = function() {
                $modalInstance.close();
            };

            vm.cancel = function() {
                $modalInstance.dismiss();
            };
        }
    }
}());

This can be used very easily directly in my controllers:

这可以直接在我的控制器中使用:

  vm.openDialog = function() {
    dialogService.openConfirmDialog({
      bodyText: 'Do you really want to close me'
    });
  };

Here is a working Plunker

这是一个有效的Plunker

With this solution I don't have to pollute my controller and/or HTML-view with code for the model. The modal code is very well encapsulated.

使用此解决方案,我不必使用模型的代码污染我的控制器和/或HTML视图。模态代码封装得非常好。

#3


0  

Have you looked into AngularStrap http://mgcrea.github.io/angular-strap/#/modals ?

您是否查看了AngularStrap http://mgcrea.github.io/angular-strap/#/modals?

You can pass in a templateUrl in which you can bind a controller to your modal instance, therefore giving you full control (by setting ng-controller="ModalCtrl" on the top level element of the modal template).

您可以传入一个templateUrl,您可以在其中将控制器绑定到模态实例,从而为您提供完全控制(通过在模态模板的*元素上设置ng-controller =“ModalCtrl”)。

You can programatically fire the modal via ng-click or set attributes in your html using the bs-modal directive.

您可以使用bs-modal指令通过ng-click或在html中设置属性以编程方式触发模态。

http://plnkr.co/edit/h9BwU0dFTTdw59b6oTWv?p=preview

http://plnkr.co/edit/h9BwU0dFTTdw59b6oTWv?p=preview