Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement path aliases in ui-router

I'm trying to find a way to implement route aliases in Angular.js using ui-router.

Let's say I have a state with the url article/:articleId and I want to have /articles/some-article-title redirected (internally) to /article/76554 without changing the browser location.

Could this be done with ui-router?

like image 423
Casper Deseyne Avatar asked May 23 '14 13:05

Casper Deseyne


1 Answers

I. Doubled state mapping (reuse of controller, views)

NOTE: This is original answer, showing how to solve the issue with two states. Below is another approach, reacting on the comment by Geert

There is a plunker with working example. Say we have this two objects (on a server)

var articles = [
  {ID: 1, Title : 'The cool one', Content : 'The content of the cool one',},
  {ID: 2, Title : 'The poor one', Content : 'The content of the poor one',},
];

And we would like to use URL as

// by ID
../article/1
../article/2

// by Title
../article/The-cool-one
../article/The-poor-one

Then we can create this state definitions:

// the detail state with ID
.state('articles.detail', {
    url: "/{ID:[0-9]{1,8}}",
    templateUrl: 'article.tpl.html',
    resolve : {
      item : function(ArticleSvc, $stateParams) {

        return ArticleSvc.getById($stateParams.ID);
      },
    },
    controller:['$scope','$state','item',
      function ( $scope , $state , item){ 

        $scope.article = item;
    }],
  })

// the title state, expecting the Title to be passed
.state('articles.title', {
    url: "/{Title:[0-9a-zA-Z\-]*}",
    templateUrl: 'article.tpl.html',
    resolve : {
      item : function(ArticleSvc, $stateParams) {

        return ArticleSvc.getByTitle($stateParams.Title);
      },
    },
    controller:['$scope','$state','item',
      function ( $scope , $state , item){ 

        $scope.article = item;
    }],
  })

As we can see, the trick is that the Controller and the Template (templateUrl) are the same. We just ask the Service ArticleSvc to getById() or getByTitle(). Once resolved, we can work with the returned item...

The plunker with more details is here

II. Aliasing, based on native UI-Router functionality

Note: This extension reacts on Geert appropriate comment

So, there is a UI-Router built-in/native way for route aliasing. It is called

$urlRouterProvider - .when()

I created working plunker here. Firstly, we will need only one state defintion, but without any restrictions on ID.

.state('articles.detail', {
    //url: "/{ID:[0-9]{1,8}}",
    url: "/{ID}",

We also have to implement some mapper, converting title to id (the alias mapper). That would be new Article service method:

var getIdByTitle = function(title){
   // some how get the ID for a Title
   ...
}

And now the power of $urlRouterProvider.when()

 $urlRouterProvider.when(/article\/[a-zA-Z\-]+/,
  function($match, $state, ArticleSvc) {

    // get the Title 
    var title = $match.input.split('article/')[1];
    // get some promise resolving that title
    // converting it into ID
    var promiseId = ArticleSvc.getIdByTitle(title);

    promiseId.then(function(id){

      // once ID is recieved... we can go to the detail
      $state.go('articles.detail', { ID: id}, {location: false}); 
    })

    // essential part! this will instruct UI-Router, 
    // that we did it... no need to resolve state anymore
    return true;
  }
);

That's it. This simple implementation skips error, wrong title... handling. But that is expected to be implemented anyhow... Check it here in action

like image 152
Radim Köhler Avatar answered Sep 29 '22 23:09

Radim Köhler