如何使用 $routeProvider 在 AngularJS 1.x 中的 ui-modal 中打开现有的 controller/view

How to open an existing controller/view in an ui-modal in AngularJS 1.x with $routeProvider

我知道 ui-router 可以做到这一点,而且在 angular 2 中这不再是问题。但是对于我们的实际项目,我们暂时受困于 angular 1.x 和 $routeProvider。

所以我想要的是打开一个现有的组件(例如登录表单),它已经 "lives" 在它自己的页面中,或者在模式对话框($uibModal)上必须复制或修改它的控制器或模板。

最简单的例子是登录表单,它有自己的路由,例如可通过 myapp/#/login 访问,现在我还想将其作为模式显示在当前 "page" 的顶部,而无需修改 controller/template 等。当模式消失时,我想要下面的页面与我离开时的状态相同。

此解决方案适用于任何视图,而不仅仅是登录表单(例如)。所以我正在寻找通用解决方案。

有什么想法吗?

经过一番尝试,我终于得到了我要找的东西:

首先我创建了一个服务,它负责打开通用模式。我叫它 genericModalSvc 值得一提的是,我正在使用名为 namedRouteSvc 的服务来获取 每个名称的路线和其他很酷的东西。路由名称定义为自定义 路线上的参数如下:

$routeProvider
    .when('/something', {
        name: 'something', // this is a custom parameter to retrieve the route
        templateUrl: 'something.html',
        controller: 'SomethingCtrl',
        controllerAs: 'ctrl'
    })

以上不是 genericModalSvc 工作所必需的,但它使事情 下面的代码更容易理解:

angular
    .module('my_app.common_stuff')
    .service('GenericModalSvc', GenericModalSvc);

GenericModalSvc.$inject = ['$uibModal', '$rootScope', 'namedRouteSvc'];
function GenericModalSvc($uibModal, $rootScope, namedRouteSvc) {
    this.$uibModal = $uibModal;
    this.$rootScope = $rootScope;
    this.namedRouteSvc = namedRouteSvc;

    this.modalInstance = null;

    // Broadcasts for close signal from modal's close button
    this.$rootScope.$on('GenericModalCloseClicked', this.close.bind(this));
}


GenericModalSvc.prototype.close = function() {
    this.modalInstance.close();
    // Emit a signal here, for other listeners to know the modal has been
    // closed.
    this.$rootScope.$emit('GENERIC_MODAL_CLOSED');
};

GenericModalSvc.prototype.open = function(params) {
    // The params argument here, has route information and parameters I
    // want to transfer to the model
    var route_name = params.route_name, // A string defined in each route
        args = params.args,       // The route parameters
        query = params.query,     // The route query arguments
        options = params.options; // Other options

    this.$rootScope.$emit('GENERIC_MODAL_OPENED');
    var route = this.namedRouteService.getRoute(route_name, args, query);
    route = route.hasOwnProperty('route') ? route.route : null;

    if (!route) {
        throw new Error('Could not find a route for the given parameters');
    }

    var modal_title = params.options.modal_title;

    // The following is the fun part. Now we can use the route to get the
    // controller and controllerAs attributes to define our modal here.
    this.modalInstance = this.$uibModal.open({
        // This template is the modal wrapper for our 'real' template.
        templateUrl: 'partials/generic-modal.html',
        controller: route.controller,
        controllerAs: route.controllerAs,
        backdrop: 'static',
        size: 'lg',
        resolve: {
            show_modal_close: function() {
                return Boolean(options && options.showCloseButton);
            },
            modal_title: function() {
                return modal_title;
            },
            template_to_render: function() {
                // We need to pass this information to our generic-modal.html
                // template to include it there.
                return route.templateUrl;
            },
            dummy: function($routeParams) {
                // This is a rather kind of q&d way of passing the route 
                // parameters and query arguments. On the controller, we
                // must look inside $routeParams for 'modalParameters' first 
                // (because we don't know there if we're inside a modal or not)
                // and if 'modalParameters' doesn't exist, then we retrieve the 
                // normal-ones inside $routeParams. I know, it's not perfect
                // but didn't come up with a better idea.
                $routeParams.modalParameters = {
                    routeParams: args,
                    queryParams: query
                };
            }
        }
    });
};

那么generic-modal.html模板就很简单了:

<p id="generic-modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-label="Close"
        ng-click="$emit('GenericModalCloseClicked')">
        <span aria-hidden="true">X</span></button></p>

<div id="generic-modal-container">
    <div ng-include="$resolve.template_to_render"></div>
</div>

最后是如何使用它的示例:

angular
    .module('my_app.foo')
    .controller('FooCtrl', FooCtrl);
FooCtrl.$inject = ['...','genericModalSvc'];
function FooCtrl(..., genericModalSvc) {
    this.genericModalSvc = genericModalSvc;
}

FooCtrl.prototype.openBarDetailsOnModal = function() {
    var params = {
        route_name: 'bar-detail',
        args: {'id': this.some_id_for_bar},
        query: {},
        options: {}
    };
    this.genericModalSvc.open(params);
};

就是这样了!!!