【问题标题】:Loading state as a modal overlay in AngularJS在 AngularJS 中加载状态作为模态覆盖
【发布时间】:2015-04-14 05:43:40
【问题描述】:

我想将状态加载为模态,以便我可以覆盖状态而不影响应用程序中的任何其他状态。例如,如果我有这样的链接:

<a ui-sref="notes.add" modal>Add Note</a>

然后我想使用指令中断状态更改:

.directive('modal', ['$rootScope', '$state', '$http', '$compile',
    function($rootScope, $state, $http, $compile){
        return {
            priority: 0,
            restrict: 'A',
            link: function(scope, el, attrs) {  
                $rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
                    event.preventDefault();
                }); 
                el.click(function(e){
                    $http
                    .get('URL HERE')
                    .then(function(resp){
                        $('<div class="modal">' + resp.data + '</div>').appendTo('[ui-view=app]');
                        setTimeout(function(){
                            $('.wrapper').addClass('showModal');
                        },1);
                    });
                }); 
            }   
        }
    }
])

这成功地阻止了状态更改并加载 URL 并将其作为模式附加到应用程序。 问题是它会再次加载整个应用程序...

如何只加载状态?例如模板文件和相邻的控制器。

状态如下:

.state('notes.add',
{
    parent: 'notes',
    url: '/add',
    views: {
        'content': {
            templateUrl: 'partials/notes/add.html',
            controller: 'NotesAddCtrl'
        }
    }
})

如何使用 jQuery 工作的示例:http://dev.driz.co.uk/AngularModal

了解我如何通过 AJAX 访问 StateA 和 StateB 加载,该 AJAX 使用 History API 更改 URL 以反映当前状态更改。

并且无论我是在索引、StateA 还是 StateB 上,我都可以将 StateA 或 StateB 作为模式加载(即使我已经在那个 State 上)并且它不会更改 url 或当前内容,它只是覆盖状态内容。

这就是我希望在 AngularJS 中能够做到的。

注意。此示例不适用于浏览器的后退和前进按钮,因为它是一个快速示例并且没有正确使用历史 API。

【问题讨论】:

  • 看起来您正在导航到不同的 URL,那么您要准确加载什么状态?
  • 是的,notes.add 状态。可以在浏览器中通过 URL 访问,也可以在模态中通过 JS 访问(无需更改 URL(这一切都会发生,但它会再次加载整个应用程序)。
  • 您尝试过这里建议的解决方案吗? stackoverflow.com/questions/21883559/…
  • 那么我需要什么来防止它再次加载整个应用程序?我没有看到防止答案中提到的解决方案。谢谢。
  • 看看这个小提琴:jsfiddle.net/7tzXh/27 你可能想要构造你的代码来匹配它,它实现了你正在寻找的东西

标签: javascript jquery angularjs


【解决方案1】:

几天前我看到了你的问题,尝试设置一些可行的方法似乎很有趣。

我以 uiSref 指令为起点,并修改了代码以使用 angular-bootstrap 的 $modal 来显示所需的状态。

angular.module('ui.router.modal', ['ui.router', 'ui.bootstrap'])
.directive('uiSrefModal', $StateRefModalDirective);

function parseStateRef(ref, current) {
  var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
  if (preparsed) ref = current + '(' + preparsed[1] + ')';
  parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
  if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
  return { state: parsed[1], paramExpr: parsed[3] || null };
}

function stateContext(el) {
  var stateData = el.parent().inheritedData('$uiView');

  if (stateData && stateData.state && stateData.state.name) {
    return stateData.state;
  }
}

$StateRefModalDirective.$inject = ['$state', '$timeout', '$modal'];

function $StateRefModalDirective($state, $timeout, $modal) {
  var allowedOptions = ['location', 'inherit', 'reload'];

  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var ref = parseStateRef(attrs.uiSrefModal, $state.current.name);
      var params = null, url = null, base = stateContext(element) || $state.$current;
      var newHref = null, isAnchor = element.prop("tagName") === "A";
      var isForm = element[0].nodeName === "FORM";
      var attr = isForm ? "action" : "href", nav = true;

      var options = { relative: base, inherit: true };
      var optionsOverride = scope.$eval(attrs.uiSrefModalOpts) || {};

      angular.forEach(allowedOptions, function(option) {
        if (option in optionsOverride) {
          options[option] = optionsOverride[option];
        }
      });

      var update = function(newVal) {
        if (newVal) params = angular.copy(newVal);
        if (!nav) return;

        newHref = $state.href(ref.state, params, options);

        if (newHref === null) {
          nav = false;
          return false;
        }
        attrs.$set(attr, newHref);
      };

      if (ref.paramExpr) {
        scope.$watch(ref.paramExpr, function(newVal, oldVal) {
          if (newVal !== params) update(newVal);
        }, true);
        params = angular.copy(scope.$eval(ref.paramExpr));
      }
      update();

      if (isForm) return;

      element.bind("click", function(e) {
        var button = e.which || e.button;
        if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
          e.preventDefault();

          var state = $state.get(ref.state);
          var modalInstance = $modal.open({
            template: '<div>\
              <div class="modal-header">\
                <h3 class="modal-title">' + ref.state + '</h3>\
              </div>\
              <div class="modal-body">\
                <ng-include src="\'' + state.templateUrl + '\'"></ng-include>\
              </div>\
            </div>',
            controller: state.controller,
            resolve: options.resolve
          });

          modalInstance.result.then(function (selectedItem) {
            $scope.selected = selectedItem;
          }, function () {
            console.log('Modal dismissed at: ' + new Date());
          });
        }
      });
    }
  };
}

你可以这样使用&lt;a ui-sref-modal="notes.add"&gt;Add Note&lt;/a&gt;

指令需要angular-bootstrap 来解析模态对话框。您需要在您的应用中使用 ui.router.modal 模块。

【讨论】:

  • 如果要添加histoyAPI入口,应该在打开modal后进行。
  • 这看起来不错。但是控制器呢?通常我将状态链接到$stateProvider中的控制器...
  • 您可以将控制器传递给模态实例,我认为它应该在options对象var modalInstance = $modal.open({ template: ..., controller: options.controller, resolve: options.resolve });上可用
  • 抱歉,刚刚检查,控制器在状态对象上可用,我将编辑代码以显示更改
  • 在没有 angular-bootstrap 模式的情况下使用它有多容易?因为它看起来不仅仅是显示一个覆盖,而是实际处理所有控制器和其他东西。还是不是这样?
【解决方案2】:

既然被要求为我的评论提供一个例子,

指令示例

  myapp.directive('openModal', function ($modal) {
    return function(scope, element, attrs) {
      element[0].addEventListener('click', function() {
        $modal.open({
          templateUrl : attrs.openModal,
          controller: attrs.controller,
          size: attrs.openModalSize,
          //scope: angular.element(element[0]).scope()
        });
      });
    };
  });

示例 HTML

<button 
  open-modal='views/poc/open-modal/small-modal.html'
  open-modal-size='sm' 
  controller="MyCtrl">modal small</button>

上述指令方式与使用状态没有太大区别,状态有templateUrlcontroller,只是url不变。

.state('state1.list', {
  url: "/list",
  templateUrl: "partials/state1.list.html",
  controller: function($scope) {
    $scope.items = ["A", "List", "Of", "Items"];
  }
})

【讨论】:

    【解决方案3】:

    显然有问题Ui-sref not generating hash in URL (Angular 1.3.0-rc.3) 指的是 https://github.com/angular-ui/ui-router/issues/1397

    这似乎是根据 cmets 修复的。

    我个人不喜欢html5mode,因为它需要在您的服务器上进行额外的工作而没有明显的优势(我不认为拥有“更漂亮的网址”作为有形优势来证明额外工作的合理性)。

    使用路由器时还有另一个性能问题,即每次路由更改时都会重新创建视图 DOM。我提到了一个非常简单的解决方案 在this answer


    顺便说一句,http://dev.driz.co.uk/AngularModal/ 中的示例表现不佳。它不记录历史,所以我不能回去。此外,如果您单击 Index 或 modals 之类的链接,然后重新加载,则不会得到相同的页面。


    更新。 从 cmets 看来,打开模式时不需要更改路线。在这种情况下,最简单的解决方案是不要将ui-sref 放在打开按钮上,而是让模态指令处理它。

    【讨论】:

    • 我在问题中表示,该示例只是解释该概念的快速而肮脏的示例。并且模态不应该出现在重新加载设计上。
    • 我在您的问题中看不到该示例“脏”的任何陈述,也看不到有关模态的功能。这能解决您的问题吗?
    • 请参阅我的问题末尾关于未正确使用历史 API 的注释。无论如何......我没有得到你的答案?你能展示一个如何将状态加载到叠加层中的代码示例吗?
    • 您检查过我答案中的链接吗?你的代码看起来不错。主要问题是 - 你看到你的 url 中的哈希了吗?
    • 其他链接是。但是我不应该看到模态的哈希值,因为我们没有改变状态。
    猜你喜欢
    • 2019-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多