【问题标题】:AngularUI Router: multiple states with same url patternAngularUI路由器:具有相同url模式的多个状态
【发布时间】:2014-06-11 16:00:37
【问题描述】:

大家好,我遇到了我认为常见的路由问题,但我无法找到解决方案。基本上我的页面有两种状态,基本和高级,我希望两种状态的 URL 模式相同,但只加载当前状态的模板(从控制器内部转换)

config(function ($stateProvider) {

  $stateProvider.state('basic', {
    url: '/:post',
    templateUrl: function (stateParams) {
      return 'post-' + stateParams.post + '-tmpl.html';
    }
  });

  $stateProvider.state('advanced', {
    url: '/:post',
    templateUrl: function (stateParams) {
      return 'post-' + stateParams.post + '-advanced-tmpl.html';
    }
  });
})

controller('myCtrl', function ($state) {
  //
  // In this case, I would expect only the template from
  // the advanced state to load, but both templates are trying
  // to load.
  $state.transitionTo('advanced', {post: 2});
}

我假设导航到匹配的模式会加载给定的状态,这就是为什么当它匹配时,两个模板都会尝试加载。有什么方法可以实现相同的 url 模式,但仅基于当前状态使用不同的模板?

【问题讨论】:

  • 我遇到了同样的问题。我认为 UI 路由器是基于状态的,而不是 URL,但事实恰恰相反
  • 您找到解决方案了吗?
  • 我注意到,当单击由 ui-sref 实现的此配置(您的高级)中的第二个状态时,第二次会加载正确的模板。 (浏览器地址栏第一次更新)
  • @SamVloeberghs 检查这个问题stackoverflow.com/questions/30900111/…。答案中的解决方案取决于具有相同模式的第一个状态中的 onEnter 定义。因此,如果您假设有 10 个具有相同模式的状态,那么在 onEnter 中您处理所有情况,何时重定向到哪个状态,但这需要每个状态的一些差异化特征,在那个解决方案中我使用了隐藏参数但我们可以使用cookie 或 localStorage 也是如此。也是的,它适用于 ui-sref ,检查run.plnkr.co/plunks/wRqwPr/#
  • 看起来像这个问题的重复 - stackoverflow.com/questions/23344055/…

标签: javascript angularjs angular-ui-router


【解决方案1】:

假设您不能有两个具有相同 URL 的状态,为什么不继续将两个状态合并为一个呢? ui-router 已经允许你有一个自定义函数来返回模板。您只需在状态声明中添加另一个隐藏的自定义参数(我们称之为advanced):

$stateProvider.state('basicOrAdvanced', {
  url: '/:post',
  templateUrl: function (stateParams) {
    if (stateParams.advanced) {
      return 'post-' + stateParams.post + '-advanced-tmpl.html';
    } else {
      return 'post-' + stateParams.post + '-tmpl.html';
    }
  },
  params: {
    advanced: False
  }
});

然后你调用它:

$state.transitionTo('basicOrAdvanced', {post: 2, advanced: True})

有关嵌套状态和通用控制器的另一种可能解决方案,请参阅 ui-router 的 github 页面上的问题 #217#1096

那里提出的解决方案创建了第三个状态,其控制器执行调度工作,而您要进入的两个状态(basicadvanced)的 URL 为空:

$stateProvider.state('basicOrAdvanced', {
  url: '/:post',
  controller: function($state, $stateParams) {
    if($stateParams.advanced) {
        $state.go('advanced', {post: $stateParams.post});
    } else {
        $state.go('basic', {post: $stateParams.post});
    }
  },
  params: {
    advanced: False
  }
}

如果您的状态与简单的 templateUrl 相比在更多方面有所不同(例如,如果它们具有完全独立的控制器等),则此解决方案会更好


另见another question on StackOverflow about a similar issue

【讨论】:

  • 这也适用于 ui-sref 吗?我明白你提供解决方案的观点,但我想避免你的做法。如果不仅与其关联的模板可能会发生变化,而且与它关联的 ui-router 状态上的数据或其他属性也会发生变化怎么办?
  • 例如,我有一个用例,其中 ":post" 是多语言上下文中的动态值。所以我想要一个名称为'about' 和 url ':page' 的州能够拥有一个 url /about-english 和 /about-dutch。还有其他类似的状态具有绑定到这些特定状态的不同数据属性(和其他)
  • 关于多语言部分,不就是相反的问题吗? IE。一个州有两个不同的网址?
  • 不,这是原始问题:具有相同 url 模式的不同状态 :) contact(/:page) -> /contact-nl 或 /contact-en about(/:page) -> /about -nl 或 /about-en 多语言部分只是一个上下文,而不是对问题的改变。
  • 当联系状态定义在 about 状态之前,并且你访问 about 状态(使用 ui-sref 或 state.go)时,会加载联系状态,因为它匹配相同的 URL 模式,在 about 状态之前
【解决方案2】:

这在AngularJS中非常有用,

您可以为具有相同 url 模式的多个状态指定 动态路由

我的代码如下,可能对您有用,

模块初始化,

var app = angular.module("koops_app", ['ui.router','ui.bootstrap']);    //here you have to define all your required module for your project that you should inject...

进一步,

app.config(function ($locationProvider, $httpProvider, $stateProvider, $urlRouterProvider, $wampProvider) {


    // When No Routing i.e Default Routing
    $urlRouterProvider.when('', '/dashboard');


    // 404
    $stateProvider.state("404", {
        url: "/404",
        templateUrl: 'template/404.html',
    });


    // When Only One Argument i.e. Only Module Name
    $stateProvider.state("default", {
        url: "/:section",
        templateUrl: 'views/View-to-be-load.html',  //can use $stateParams.section for dynamic
        reload: true,
        resolve: {
            loadController: ['$q', '$stateParams', '$state',
                        function ($q, $stateParams, $state) {
                            var deferred = $q.defer();
                            deferred.resolve();
                            return deferred.promise;
                        }
                    ]
        },
        controllerProvider: function ($stateParams) {
            return 'controllerName';
        }
    });



        // When Two Argument i.e. Module/Controller
        $stateProvider.state("default/title", {
            url: "/:section/:title",
            templateUrl: 'views/View-to-be-load.html',  //can use $stateParams.section for dynamic
            reload: true,
            resolve: {
                loadController: ['$q', '$stateParams', '$state',
                            function ($q, $stateParams, $state) {
                                var deferred = $q.defer();
                                deferred.resolve();
                                return deferred.promise;
                            }
                        ]
            },
            controllerProvider: function ($stateParams) {
                return 'controllerName';
            }
        });



        // When Three Arguments i.e. Module/Controller/id
        $stateProvider.state("default/title/id", {
            url: "/:section/:title/:id",
            templateUrl: 'views/View-to-be-load.html',  //can use $stateParams.section for dynamic
            reload: true,
            resolve: {
                loadController: ['$q', '$stateParams', '$state',
                            function ($q, $stateParams, $state) {
                                var deferred = $q.defer();
                                deferred.resolve();
                                return deferred.promise;
                            }
                        ]
            },
            controllerProvider: function ($stateParams) {
                return 'controllerName';
            }
    });



        // Otherwise
        $urlRouterProvider.otherwise("/404");

也许这对你有帮助... 享受...

【讨论】:

  • 这没有回答问题。您的 URL 模式因每个州定义而异。我的用例是如果您在多个状态下使用“/:section”作为您的 url 模式。
  • 我可以看到您的答案在两个或多个不同的 $stateProvider.state( ) 条目下使用相同 URL 的位置?
  • 是的,它在不同的状态下使用相同的 url,但具有相同的模式 @CodeUniquely
  • 对于url 的参数值,不要使用/foo/{bar:string} - 请改用/foo/:bar。否则就不行了
【解决方案3】:

你需要使用嵌套状态

config(function ($stateProvider) {


  $stateProvider.state('post', {
    url: '/:post',
    templateUrl: function (stateParams) {
      return 'post-' + stateParams.post + '-advanced-tmpl.html';
    }
  });

   $stateProvider.state('post.basic', {
    url: '/:post',
    templateUrl: function (stateParams) {
      return 'post-' + stateParams.post + '-tmpl.html';
    }
  });

})

controller('myCtrl', function ($state, $timeout) {
  // go to advanced
  $state.transitionTo('post', {post: 2});
  // after 500ms switch to basic
  $timeout(function() {
      $state.transitionTo('post.basic', {post: 2});
  }, 500)
}

【讨论】:

  • 谢谢,但是当我进行此更改时,我会在控制台中看到“未捕获的对象”。
  • @ThinkingInBits 更新了答案,我基本上是从你那里复制过来的,你在 uri 中的参数名称和你实际传递的内容应该匹配。
  • 哎呀,这只是一个错字。它们在我的代码中确实匹配。
  • 只是将点符号添加到第二个状态会破坏它。
  • @vittore,这并不能解决他的问题。高级不是基本的孩子状态,而是我相信的兄弟姐妹。
【解决方案4】:

为什么要在同一个 url 上映射两个不同的视图。当用户想要在其中一个状态上添加书签时,您是否想象过这个问题?如果它们的 url 完全相同,你怎么知道是哪一个?

我建议定义两种不同的状态,例如:

  • /:post - 用于简单视图
  • /:post/advanced - 用于高级视图

这可以通过将 /:post 定义为抽象状态以及简单和高级视图的两种不同状态来完成。为方便起见,简单视图将作为默认视图。如果您喜欢这个想法,我可以为您提供一个示例解决方案。

另一种选择是添加 url 参数,例如:

  • /:post - 用于简单视图
  • /:post?view=advanced

我希望这个答案会对你有所帮助。至少从不同的角度看事物;)。

【讨论】:

  • 这是关于 url 模式,而不是 url
  • 感谢您的洞察力。我们对这些路由名称有特定的要求和逻辑。用户不能简单地为这些 url 之一添加书签...事实上,如果他们尝试通过位置栏直接导航到此处,他们将完全重定向到单独的视图/控制器。然而,当时我确实需要能够区分相同 URL 模式的不同控制器和视图。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多