AngularJS - 跨多个控制器/ UI元素管理URL更改

时间:2014-09-23 20:57:40

标签: angularjs angularjs-directive angular-ui

构建一个Angular应用程序,我是使用ng-controller和自定义指令构建应用程序的粉丝,例如:

<nav ng-controller="NavController"></nav>
<date-picker></date-picker>

我喜欢这些模式,因为它们具有声明性,并且非常容易理解 - 您确切地知道在哪里寻找支持逻辑。

另一方面,使用ng-view,甚至ui-router插件(在我看来)抽象出一些功能并使其更难阅读。

但是,我的首选模式在以逻辑方式响应应用程序范围内的URL更改时给我一个问题。我希望UI的每个部分都能对URL更改做出适当的响应,但我不知道在哪里放置代码。我假设我有两个选择:

  1. 在每个控制器或指令中单独响应$ location更改
  2. 将此封装在其他负责侦听URL更改的其他类或服务中
  3. 我无法弄明白该怎么做。

    选项一让我完全控制,但我无法利用ng-route - 解析参数等。在检查URL更改时,我真的必须自己动手。< / p>

    使用选项二,我无法引用任何控制器,因为它们已经被声明了!

    更广泛地说,我担心的是,没有立即找到解决方案表明我必须错误地思考问题。这根本错了吗?

    编辑:为了避免疑问,我非常谈论SPA,更具体地说,这是我试图做的事情:

    <nav ng-controller="NavController"><!-- more HTML here --></nav>
    <div ng-controller="VideoController"><!-- more HTML here --></div>
    <div ng-controller="CommentController" ng-show="Active"><!-- more HTML here --></div>
    

    现在,假设我导航到:&#34; / video /:key /&#34; - 我希望VideoController获取视频密钥,并执行加载视频所需的操作。我希望NavController突出显示哪个菜单项是活动的等等。

    如果我导航到&#34; / video /:videokey / comment&#34; - 我希望VideoController加载视频,并使用NavController突出显示哪个是活动的,还有CommentController加载评论/出现在视频中等等。

    这个想法我无法理解 - 响应两个控制器中的URL更改,每个控制器负责单页应用程序中UI的单独部分。

6 个答案:

答案 0 :(得分:4)

这不是您正在寻找的答案,但这是您需要听到的答案(

意见消失!

您将花费大量时间围绕自己的布局和路线创意构建SPA。如果可能的话,我不会感到惊讶 - 如果您在适当的范围内发出事件并让子控制器监听状态更改事件,您可以定义不同的控制器,这些控制器对状态更改做出不同的响应,显示和隐藏其内容,并更新这些内容反映当前对象模型(/ things / 1到things / 2到things / 2 / edit或者其他)。

这几乎就是ui-router的工作原理,除了不是在主页面中嵌入顶级html内容,而是使用ui-view指令基本上处理部分和控制器。

在一天结束时,这样做,你将复制ui-router目前所做的大约50%。如果你很幸运,它将和ui-router一样好用。如果你不是,它会以有趣的方式出错。

你将拥有一个huuuuuge单页面应用程序,它将很难调试。如果您在一个没有包含模板的顶级html页面中拥有所有内容,则您将无法轻松分离关注点或分解行为。其他一些开发人员无法跟进这项工作,并且使用未终结标签的细微错误可能更难找到。另外:与框架作斗争的所有时间都可以用于使应用程序工作。

答案 1 :(得分:2)

正如您建议使用ngRoute,您可以将$rootParams - 服务注入您的控制器并获取所有当前参数。如果您还需要ngRoute选择的当前控制器,您也应该注入$route - 服务。有关如何使用这些服务的更多详细信息,请参阅example in the ngRoute documentation - 或提供角度代码的(最小)版本,以便根据您的用例提供示例。

BTW:如果SPA中也有不同的视图和/或布局(例如不仅仅是视频),您可以使用ngRoute的ng-view指令为所有这些不同的布局设置视口。这样,每个布局都可以拥有自己的模板和特定于布局的控制器,可以重用通用控制器和指令(例如用于注释)。我再次需要更多代码来为此提供一个有意义的示例。

答案 2 :(得分:1)

对于你想做的事,我看到了三个选择。

  1. 使用$rootScope将控制器绑在一起;在这里保留您的URL变量。当他们改变时,您的控制器可以做出相应的反您可以在此处使用$broadcast$emit来传递消息,或者您只需观看$rootScope变量。
  2. 将您的路由封装在发布/订阅服务中。该服务将处理您的路由,而您的控制器将订阅任何已发布的更改。这可能很优雅,具体取决于您想要的行为的复杂程度。
  3. 使用嵌套控制器。外部控制器将处理您的路由;内部控制者,你的观点。
  4. 听起来好像你想避免在每个控制器中放置路由逻辑 - 这很好 - 但是你必须找到一些方法来指定你想要的行为。如果我打算尝试从头开始,我想我更喜欢发布/订阅路线,我自己;我更喜欢在提供商中封装这样的东西(因为它是模块化的,可以很容易地重复使用)。

    尽管如此,我同意其他答案 - 只需简单地使用ngRouteuiRouter即可。通过这样做,您可以在一个地方(沿着路由参数)设置路由,并使用嵌套的控制器/视图或指令。实际上,您可以考虑将Video和Coments对象转换为Angular指令。

答案 3 :(得分:0)

尝试使用angular-ui路由器。它可以是您需要的图书馆。 Github项目: https://github.com/angular-ui/ui-router

答案 4 :(得分:0)

我猜你不是在谈论SPA,单页申请。

所以问题是,

您是否希望对多个页面使用相同的$routeProvider,并为当前页面路径提供正确的控制器?

可悲的是,url路径和angularjs路径路径不同,你不能使用url路径到AngularJS路径路径。

为了解决这个问题。这是我的想法 我们可以从绝对头部确定angularjs路径路径。

  1. 在您的应用中,使用$window.location.href$window.location.pathname

  2. 获取当前路径
  3. 在您的其他区块中使用该路径。

  4. 以下是代码和演示:http://plnkr.co/edit/OS38E38J11FeDS1hyHde?p=preview

      angular.module('myApp', ['ngRoute'])
        .controller('MainController', function($scope) {
         $scope.pageName = "main page";
       })
       .controller('Page1Controller', function($scope) {
         $scope.pageName = "page1";
       })
       .controller('Page2Controller', function($scope) {
         $scope.pageName = "page2";
       })
      .config(function($routeProvider, $windowProvider) {
        var otherwiseFunc = function() {
          var absPath = $windowProvider.$get().location.href;
          console.log('absPath', absPath);
    
          var redirectTo =  absPath.match(/path1/) ? '/page1' :
            absPath.match(/path2/) ? '/page2' :
            '/main';
          return {
            redirectTo: redirectTo
          }
        };
    
        $routeProvider
        .when('/main',  {templateUrl: 'partial.html',controller: 'MainController'})
        .when('/page1', {templateUrl: 'partial.html',controller: 'Page1Controller' })
        .when('/page2', {templateUrl: 'partial.html',controller: 'Page2Controller' })
        .otherwise(
          otherwiseFunc()
        );
      });
    

答案 5 :(得分:0)

可能最简单的方法就是创建一个处理网址的服务。

该服务将返回一组与每个控制器相关的参数,这些参数取决于URL。例如,它会有一个属性Video.id来表示视频ID,Nav.id来表示导航链接等。

服务本身非常简单 - 获取URL,将其拆分为参数,最难的部分是分离哪个控制器所需的参数(根据上述示例中的规则构建对象)。


示例网址:/video/mySupercoolVideo/comment

服务返回类似:

{
    video: "mySupercoolVideo",
    nav: "comment",
    comments: true
    // other url-related data here
}

这是一个非常简单和基本的例子(导航和评论似乎多余,我没有扩展任何顶级属性,所以没有video.id之类的东西),显然你可以扩展/优化这一点。

每个控制器都会在开始时检查URL状态,并根据响应进行操作。

如果您愿意,您也可以使用包装器控制器,因此您不必为每个控制器返回相同的数据,而只需使用该数据填充一个$ rootScope变量。