13

我需要在我的 Angular 应用程序中显示一个模式窗口(基本上只是一个隐藏的 div,它将使用已编译的模板加载)。问题是,我需要在模式打开时更改 URL,以便用户可以复制链接并直接转到模式窗口,还可以使用后退按钮关闭模式窗口并返回上一页。这类似于 Pinterest 在您单击图钉时处理模态窗口的方式。

到目前为止,我已经创建了一个加载模板的指令,使用 $compile 对其进行编译,注入 $scope,然后显示编译后的模板。这工作正常。

问题是,一旦我使用 $location 更改路径,路由控制器就会触发并将模板加载到 ng-view 中。

我想到了两种克服这个问题的方法,但都无法实现:

  1. 当我使用 $location 更改 url 时,以某种方式阻止路由控制器触发。我在 $routeChangeStart 中添加了一个监听器以防止发生默认值,但这似乎不起作用。

  2. 以某种方式向页面添加另一个视图处理程序(页面上基本上有 2 个命名的 ng-view 指令)并且每个都能够处理不同的路由。不过,目前看不到 Angular 支持这一点。

URL 的格式必须是 /item/item_id 而不是 /item?item_id=12345。

任何帮助,将不胜感激。

4

5 回答 5

13

如果您使用此处描述的 ui.router 模块,您现在可以执行此操作:https ://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-open-a-dialogmodal -在一个确定的状态

他们的例子:

$stateProvider.state("items.add", {
    url: "/add",
    onEnter: function($stateParams, $state, $modal, $resource) {
        $modal.open({
            templateUrl: "items/add",
            resolve: {
              item: function() { new Item(123).get(); }
            },
            controller: ['$scope', 'item', function($scope, item) {
                $scope.dismiss = function() {
                    $scope.$dismiss();
                };

                $scope.save = function() {
                    item.update().then(function() {
                        $scope.$close(true);
                    });
                };
            }]
        }).result.then(function(result) {
            if (result) {
                return $state.transitionTo("items");
            }
        });
    }
});

我还将“errorCallback”函数传递给 then() 函数,以通过按 Escape 或单击背景来处理模态解除。

于 2014-01-19T05:05:58.910 回答
7

我有类似的要求。我需要:

  • 触发路由更改的模态窗口,因此它有自己的哈希 url。
  • 当您访问模态 url 时,主屏幕加载然后触发模态弹出。
  • 后退按钮关闭模式(假设您从主屏幕开始)。
  • 当您单击“确定”或“取消”时,模式关闭并且路线更改回主屏幕。
  • 当您单击背景关闭模式时,路线会变回主屏幕。

我找不到所有这些一起工作的好例子,但我能找到零碎的例子,所以我把它们放在一个工作示例中,如下所示:

<!DOCTYPE html>
<html lang="en" ng-app="app">

<head>
  <meta charset="UTF-8">
  <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.0/angular.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.11/angular-ui-router.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.11.2/ui-bootstrap-tpls.js"></script>
  <link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.1/css/bootstrap.css" rel="stylesheet">
  <script>

    // init the app with ui.router and ui.bootstrap modules
    var app = angular.module('app', ['ui.router', 'ui.bootstrap']);

    // make back button handle closing the modal
    app.run(['$rootScope', '$modalStack',
      function($rootScope, $modalStack) {
        $rootScope.$on('$stateChangeStart', function() {
          var top = $modalStack.getTop();
          if (top) {
            $modalStack.dismiss(top.key);
          }
        });
      }
    ]);

    // configure the stateProvider
    app.config(['$stateProvider', '$urlRouterProvider',
      function($stateProvider, $urlRouterProvider) {

        // default page to be "/" (home)
        $urlRouterProvider.otherwise('/');

        // configure the route states
        $stateProvider

          // define home route "/"
          .state('home', {
            url: '/'
          })

          // define modal route "/modal"
          .state('modal', {
            url: '/modal',

            // trigger the modal to open when this route is active
            onEnter: ['$stateParams', '$state', '$modal',
              function($stateParams, $state, $modal) {
                $modal

                  // handle modal open
                  .open({
                    template: '<div class="modal-header"><h3 class="modal-title">Modal</h3></div><div class="modal-body">The modal body...</div><div class="modal-footer"><button class="btn btn-primary" ng-click="ok()">OK</button><button class="btn btn-warning" ng-click="cancel()">Cancel</button></div>',
                    controller: ['$scope',
                      function($scope) {
                        // handle after clicking Cancel button
                        $scope.cancel = function() {
                          $scope.$dismiss();
                        };
                        // close modal after clicking OK button
                        $scope.ok = function() {
                          $scope.$close(true);
                        };
                      }
                    ]
                  })

                  // change route after modal result
                  .result.then(function() {
                    // change route after clicking OK button
                    $state.transitionTo('home');
                  }, function() {
                    // change route after clicking Cancel button or clicking background
                    $state.transitionTo('home');
                  });

              }
            ]

          });
      }
    ]);
  </script>
</head>

<body>

  <a href="#/modal" class="btn btn-default">POPUP!</a>

  <div ui-view></div>

</body>

</html>

您可以在 plunker 查看它的实际操作:http: //plnkr.co/edit/tLyfRP6sx9C9Vee7pOfC

单击 plunker 中的“打开嵌入式视图”链接以查看哈希标签的工作情况。

于 2014-12-03T23:01:35.607 回答
5

hermanschutte 的解决方案有效(请参阅对 Tiago Roldão 帖子的评论)。我正在逐步发布以帮助澄清。

首先,在配置中:

.when('/posts', {
  templateUrl: 'views/main.html',
  controller: 'MainCtrl',
  reloadOnSearch: false     // this allows us to use the back button to get out of modals.
})

然后,在打开模式时(您需要 location 模块):

$location.search('modal');

在模态控制器中:

$scope.$on('$locationChangeSuccess', function() {
  $scope.close(); // call your close function
});
于 2013-08-27T15:30:34.830 回答
2

您可能想尝试获取 AngularJS 源代码,调整并加载您自己的 $route 和 $routeProvider 酿造,然后将它们注入 Angular 本机。

您可能只需要添加一个标志来拦截路由更改的触发器。

另一个更简单的选择是挂钩到事件系统,使用$scope.$on(event_name, function callback(ev) {...})并调用.preventDefault()传入的事件,希望它会停止内部 $route 触发器。定位系统广播$locationChangeStart事件,并且在产生事件之前检查defaultPrevented属性$locationChangeSuccess

不要忘记发布您的结果!

于 2012-12-11T16:02:33.477 回答
2

您提到的命名视图解决方案在 angularJS 邮件列表上进行了很好的讨论,但截至目前(以及近期到中期),$route 系统无法处理多个视图。

正如我在几个地方说过的,在 SO 中也有几次,$route 系统是 angular 非常固执的立场可能成为障碍的情况之一:它的本意是简单,但它不是很简单可配置。我个人不直接使用它,而是通过我在这里找到的解决方案绕过它。

它使事情变得有点复杂,但它为您提供了更大的灵活性 - 这个想法是 $route 仅用于触发(手动)渲染功能,您可以在其中分配值并根据需要触发 $broadcasts (通常,从主控制器)。

我没有尝试过的一件事是“混合”解决方案 - 也就是说,正常使用 $route 系统,并将您的 /item/item_id 路由配置为没有视图参数(以免更改主视图,由其他路由)并通过 $routeChangeStart 事件执行某些操作(请记住,您可以为路由分配任何值,正如您在我引用的页面中看到的那样)

同样,我个人更喜欢完全使用替代方法。

于 2012-12-11T10:59:49.337 回答