14

Hey all I'm running into what I thought would be a common routing problem, but I'm unable to figure out a solution. Basically my page has two states, basic and advanced, and I want the URL patterns to be the same for both states but only load the template for the current state at the time (which is transitioned to from within a controller)

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});
}

I assume that navigating to the matched pattern loads the given state which is why when it matches, both templates attempt to load. Is there some way to accomplish the same url pattern but with different templates based only on the current state?

Dzinx
  • 55,586
  • 10
  • 60
  • 78
ThinkingInBits
  • 10,792
  • 8
  • 57
  • 82
  • I'm having the same problem. I thought that UI router was based on states, and not the URL, but this proves the opposite – Sam Vloeberghs Sep 22 '15 at 08:44
  • did you find a solution for this? – Sam Vloeberghs Sep 22 '15 at 08:53
  • I've noticed that when clicking a 2nd state in this configuration ( your advanced ) implemented by a ui-sref a second time loads the correct template. ( the browser address bar updates as it should the first time ) – Sam Vloeberghs Sep 22 '15 at 08:55
  • @SamVloeberghs Check this question http://stackoverflow.com/questions/30900111/open-page-url-in-modal-on-a-page-facebook-photo-urls/ . The solution in the answer depends on the onEnter define in the first state with same pattern. So if you have suppose 10 states with the same pattern , then in the onEnter you handle all the cases , when to redirect to which state, But this require some differentiating feature of each state , In that solution I used hidden parameters but we can use cookies or localStorage as well. Also yes it works with ui-sref , check http://run.plnkr.co/plunks/wRqwPr/#/ – Prayag Verma Sep 22 '15 at 15:12
  • Looks like a repeat of this question - http://stackoverflow.com/questions/23344055/angular-ui-router-different-states-with-same-url – jjbskir Sep 22 '15 at 16:47

4 Answers4

19

Assuming that you cannot have two states with the same URL, why don't you go along and merge the two states into one? Ui-router already allows you to have a custom function to return the template. You just need to add another, hidden custom parameter (let's call it advanced) to the state declaration:

$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
  }
});

And then you call it with:

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

For another possible solution with nested states and a common controller, see issues #217 and #1096 on ui-router's github page.

The solution presented there creates a third state whose controller does the dispatching work, whereas the two states you want to land in (basic and advanced) have empty URLs:

$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
  }
}

This solution is better if your states differ in more aspects than the simple templateUrl (e.g. if they have completely separate controllers, etc.)


Also see another question on StackOverflow about a similar issue.

Community
  • 1
  • 1
Dzinx
  • 55,586
  • 10
  • 60
  • 78
  • Will this work with ui-sref as well? I get your point in providing a solution, but I want to avoid your approach. What if it's not only the template associated with it that might change, but also the data or other attributes on the ui-router state associated with it? – Sam Vloeberghs Sep 22 '15 at 13:23
  • I for example have a use case where ":post" is a dynamic value in an multilanguage context. So I want a state with name 'about' and url ':page' to be able to have an url /about-english and /about-dutch. There are other similar states which have different data attributes ( and others ) bound to those specific states – Sam Vloeberghs Sep 22 '15 at 13:24
  • About the multilanguage part, isn't it the opposite problem? I.e. one state that has two different URLs? – Dzinx Sep 22 '15 at 13:39
  • no it's the original problem: different states with the same url pattern :) contact(/:page) -> /contact-nl or /contact-en about(/:page) -> /about-nl or /about-en The multilanguage part is just a context, not a change to the problem. – Sam Vloeberghs Sep 22 '15 at 14:37
  • when the contact state is defined before the about state, and you visit the about state ( using ui-sref or state.go), the contact state is loaded, because it matches the same URL pattern, before the about state – Sam Vloeberghs Sep 22 '15 at 14:38
  • Are contact-nl and contact-en template names or urls? If urls, do you need a dispatcher of some sort? Please describe your problem in more detail. – Dzinx Sep 22 '15 at 14:39
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/90334/discussion-between-sam-vloeberghs-and-dzinx). – Sam Vloeberghs Sep 22 '15 at 14:39
  • I don't think I should need a dispatcher. I though ui-router is all about states and not URL's..? But I'm definitely limited by the pattern here. – Sam Vloeberghs Sep 22 '15 at 14:57
  • I don't understand: in your first comment, you use the term "url" twice for different things. What is that about? Please give a scenario? – Dzinx Sep 22 '15 at 15:01
  • The scenario is basically a multilanguage app where I have 2 different states on the same level (pseudecode follows) state(url: /':lang/:page', name:'contact') & state(url: /':lang/:page', name:'about') So I'm able to have the following url's: /nl/about-nl /en/about-en /nl/contact-nl /en/contact-en When visiting state contact('nl','contact-nl'). I load the page correctly. But when visiting state about('nl','about-nl') it loads the template for the contact state as it is defined earlier with the same pattern – Sam Vloeberghs Sep 22 '15 at 15:05
  • Ok I almost got it. If you could just give me some sample values for the :page argument in the above two examples? Are they perhaps "contact" and "about"? – Dzinx Sep 22 '15 at 15:11
  • the language specific translations for contact and about ( so contact-nl or contact-en & about-nl or about-en ) – Sam Vloeberghs Sep 22 '15 at 17:42
3

This is very useful in AngularJS,

You Can Specify Dynamic Route for Multiple State with same url pattern

My Code is As follow that can be useful for you,

Module Intialization,

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...

Further,

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");

May be this is helpful for you... Enjoy...

Sagar Naliyapara
  • 3,971
  • 5
  • 41
  • 61
  • This does not answer the question. Your URL patterns are different with each state definition. My use case would be if you were using "/:section" as your url pattern in multiple states. – ThinkingInBits Oct 08 '15 at 00:51
  • I can see where your answer uses the same URL under two or more different $stateProvider.state( ) entries ? – Code Uniquely Jun 28 '16 at 13:37
  • Yes it uses same url under different state but with same patern @CodeUniquely – Sagar Naliyapara Jun 29 '16 at 10:13
  • For the parameter value for `url`, do not use `/foo/{bar:string}` - use `/foo/:bar` instead. Otherwise, it won't work – KimchiMan Oct 11 '21 at 02:03
-1

You need to use nested states

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)
}
vittore
  • 17,449
  • 6
  • 44
  • 82
-1

Why would you want to have two different views mapped on the same url. Did you imagined the problem when the user would want to add a bookmark on one of the states? How would you then know which one was if they have exactly the same url?

I would suggest defining two different states like:

  • /:post - for the simple view
  • /:post/advanced - for the advanced view

This can be done with defining /:post as abstract state and two different states for the simple and advanced view. For convenience simple view will act as default. If you like the idea I can provide you an example solution.

The other option would be to add url parameter for example:

  • /:post - for the simple view
  • /:post?view=advanced

I hope that this answer will help you. At least by seeing the things from different angle ;).

S.Klechkovski
  • 4,005
  • 16
  • 27
  • it's about a url pattern, not a url – Sam Vloeberghs Sep 28 '15 at 13:10
  • Thank you for your insight. We have specific requirements and logic for these route names. A user can't simply bookmark one of these urls... In fact, they will be redirected to a separate view/controller altogether if they try to navigate here directly via the location bar. I did, at the time, however, need the ability to differ controllers and views of the same URL pattern. – ThinkingInBits Oct 08 '15 at 00:49