Ken Ryan Ken Ryan - 2 months ago 10
AngularJS Question

How to make Angular Routing with title instead of id?

I am building a blog with angular and am currently working on the routes. I have the routes working with routeParams but angular gives it a random number and the url ends like

http://localhost/kensproblems/#/posts/2


I want it to either automatically grab the title and use it as the parameter on the URL or use the property called id on the json file such as
http://localhost/kensproblems/#/posts/title-of-post


Is this possible with
$routeParams
?

app.js

angular.module('app', [
'ngRoute',
'app.controllers',
'ui.router',
'ngSanitize'
])

.config(['$routeProvider', '$urlRouterProvider', function($routeProvider, $urlRouterProvider){
$routeProvider
.when('/posts', {
templateUrl: 'views/posts.html',
controller: 'PostListController'
})
.when('/posts/:id', {
templateUrl: 'views/singlepost.html',
controller: 'PostDetailController'
})
.otherwise({
redirectTo: '/'
});
}]);


controllers.js

angular.module('app.controllers', ['app.directives'])
/* Controls the Blog */
.controller('PostListController', ['$scope', '$http', function($scope, $http){
$http.get('data/posts.json').success(function(response){
$scope.posts = response;
});
}])

.controller('PostDetailController', ['$scope', '$http', '$routeParams', '$sce', function($scope, $http, $routeParams, $sce){
$http.get('data/posts.json').success(function(response){
$scope.post = response[$routeParams.id];
console.log($routeParams.id);
console.log($scope.post.id);
});
}])


posts.html

<div class="container" id="postList">
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div ng-repeat="post in posts | orderBy:post.date" class="post">
<h2 class="blog-title"><a href="#/posts/{{posts.indexOf(post)}}">{{post.title}}</a></h2>
<span class="date">Posted on {{post.date | date:'MMM dd, yyyy'}}</span>
<div class="row top10">
<div class="col-md-8">
<div class="post-content">{{post.headline}}</div>
<a class="read-more" href="#/posts/{{posts.indexOf(post)}}">Read more</a>
</div>
</div>
</div>
</div>
</div>
</div>


singlepost.html

<div class="container" id="singlePost">
<div class="row">
<div class="col-md-10 col-md-offset-1">
<h1>{{post.id}}</h1>
<h1>{{post.title}}</h1>
<span class="date">Posted on {{post.date | date:'MMM dd, yyyy'}}</span>

<h3>{{post.headline}}</h3>
<div class="postContent" ng-bind-html="post.content"></div>
</div>
</div>
</div>

Answer

You will need to change two main things, first how the URLs are generated, and second how the post is recovered.

Generate URL from title

Not any title will be valid as URL, so you need to use slugs.

In the view:

<a class="read-more" href="#/posts/{{getSlug(post.title)}}">Read more</a>

In the controller:

$scope.getSlug = function(text){
  return text.replace(/\W+/g, '-');
};

Search post from slug

Currently, you are just fetching the post from the array as an index, which is conceptually wrong (you will notice this after remove one post). Instead, make a search through the array. This will be really simple if you use lodash.

Something like this would work:

$scope.post = _.find(response, function(post) {
  return post.title.replace(/\W+/g, '-') === $routeParams.id;
});