Reputation: 639
I needed to use slugs in the URL instead of the article ID so I changed a couple of things in the articles example given by meanjs but I have a problem, I can list, view and edit, but I can't create new ones. I'm not familiar with the MEAN stack so it is very possible I have something very wrong in my modifications, but I can think a way of making it work.
The slug is generated from the title when creating the article. I would wanted to be edited also but If I put the slug field to be edited as well, the edit feature stops working too...
The code is from the 0.4 branch of meanjs using vertical modules.
In the articles.client.service.js if I change:
angular.module('articles').factory('Articles', ['$resource',
function($resource) {
return $resource('api/articles/:articleSlug', {
articleSlug: '@slug'
}, {
update: {
method: 'PUT'
}
});
}
]);
for:
angular.module('articles').factory('Articles', ['$resource',
function($resource) {
return $resource('api/articles/:articleSlug', {
articleSlug: '@_id'
}, {
update: {
method: 'PUT'
}
});
}
]);
The create feature starts working, but the editing feature stops... -.-
Any help would be appreciate. Thanks
This is my articles.server.routes.js
'use strict';
/**
* Module dependencies.
*/
var articlesPolicy = require('../policies/articles.server.policy'),
articles = require('../controllers/articles.server.controller');
module.exports = function(app) {
// Articles collection routes
app.route('/api/articles').all(articlesPolicy.isAllowed)
.get(articles.list)
.post(articles.create);
// Single article routes
app.route('/api/articles/:articleSlug').all(articlesPolicy.isAllowed)
.get(articles.read)
.put(articles.update)
.delete(articles.delete);
// Finish by binding the article middleware
app.param('articleSlug', articles.articleBySlug);
};
This is my articles.client.service.js
'use strict';
//Articles service used for communicating with the articles REST endpoints
angular.module('articles').factory('Articles', ['$resource',
function($resource) {
return $resource('api/articles/:articleSlug', {
articleSlug: '@slug'
}, {
update: {
method: 'PUT'
}
});
}
]);
This is my articles.client.controller.js
'use strict';
angular.module('articles').controller('ArticlesController', ['$scope', '$stateParams', '$location', 'Authentication', 'Articles',
function($scope, $stateParams, $location, Authentication, Articles) {
$scope.authentication = Authentication;
$scope.create = function() {
var article = new Articles({
slug: this.title.toLowerCase().replace(/ /g, '-'),
title: this.title,
content: this.content
});
article.$save(function(response) {
$location.path('articles/' + response.slug);
$scope.slug = '';
$scope.title = '';
$scope.content = '';
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
$scope.remove = function(article) {
if (article) {
article.$remove();
for (var i in $scope.articles) {
if ($scope.articles[i] === article) {
$scope.articles.splice(i, 1);
}
}
} else {
$scope.article.$remove(function() {
$location.path('articles');
});
}
};
$scope.update = function() {
var article = $scope.article;
article.$update(function() {
$location.path('articles/' + article.slug);
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
$scope.find = function() {
$scope.articles = Articles.query();
};
$scope.findOne = function() {
$scope.article = Articles.get({
articleSlug: $stateParams.articleSlug
});
};
}
]);
This is my articles.server.controller.js
'use strict';
/**
* Module dependencies.
*/
var _ = require('lodash'),
path = require('path'),
mongoose = require('mongoose'),
Article = mongoose.model('Article'),
errorHandler = require(path.resolve('./modules/core/server/controllers/errors.server.controller'));
/**
* Create a article
*/
exports.create = function(req, res) {
var article = new Article(req.body);
article.user = req.user;
article.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(article);
}
});
};
/**
* Show the current article
*/
exports.read = function(req, res) {
res.json(req.article);
};
/**
* Update a article
*/
exports.update = function(req, res) {
var article = req.article;
article.title = req.body.title;
article.content = req.body.content;
article.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(article);
}
});
};
/**
* Delete an article
*/
exports.delete = function(req, res) {
var article = req.article;
article.remove(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(article);
}
});
};
/**
* List of Articles
*/
exports.list = function(req, res) {
Article.find().sort('-created').populate('user', 'displayName').exec(function(err, articles) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(articles);
}
});
};
/**
* Article middleware
*/
exports.articleBySlug = function(req, res, next, slug) {
Article.findOne({'slug': slug}).populate('user', 'displayName').exec(function(err, article) {
if (err) return next(err);
if (!article) return next(new Error('Failed to load article ' + slug));
req.article = article;
next();
});
};
Upvotes: 1
Views: 1465
Reputation: 2240
I cloned the 0.4.0 branch and here's what I did to make the routing by slug working as it should in all scenarios.
1- Add the slug to the article schema and ensure its unique article.server.model.js
slug: {
type: String,
default: '',
trim: true,
unique: true,
required: 'Slug cannot be blank'
}
2- Re-factor the update method in the articles controller to include slug:
exports.update = function(req, res) {
var article = req.article;
article = _.extend(article , req.body);
article.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(article);
}
});
};
3- Add a method to the articles controller to find articles by slug( or actually anything in the req.query)
exports.readBySlug = function(req , res){
Article.findOne(req.query).populate('user',
'displayName').exec(function(err, article) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(article);
}
});
};
3- Then add a route to point to that method( make sure to add the route at the beginning of the route config or at least before the update,delete routes)
app.route('/api/articles/read-slug').get(articlesPolicy.isAllowed ,
articles.readBySlug);
4- Change the policy to allow a GET to that route , in articlesPolicy:
exports.invokeRolesPolicies = function() {
acl.allow([{
roles: ['admin'],
allows: [{
resources: '/api/articles',
permissions: '*'
}, {
resources: '/api/articles/:articleId',
permissions: '*'
}]
}, {
roles: ['user'],
allows: [{
resources: '/api/articles',
permissions: ['get', 'post']
}, {
resources: '/api/articles/:articleId',
permissions: ['get']
}]
}, {
roles: ['guest'],
allows: [{
resources: '/api/articles',
permissions: ['get']
}, {
resources: '/api/articles/:articleId',
permissions: ['get']
}]
}, {
roles: ['admin','user','guest'],
allows: [{
resources: '/api/articles/read-slug',
permissions: ['get']
}]
}]);
};
On the front end:
1- Change the routes in articles routes to use the slug instead of id:
state('articles.view', {
url: '/:articleSlug',
templateUrl: 'modules/articles/views/view-article.client.view.html'
}).
state('articles.edit', {
url: '/:articleSlug/edit',
templateUrl: 'modules/articles/views/edit-article.client.view.html'
});
2- In the views change the links to reflect the route changes article.view({articleSlug: article.slug}
, same for article.edit
3- Add a method to the articles service to get articles by slug:
return $resource('api/articles/:articleId/:controller', {
articleId: '@_id'
}, {
update: {
method: 'PUT'
} ,
getBySlug: {
method: 'GET',
params: {
controller: 'read-slug'
}
}
});
4- Re-factor the findOne method in the articles controller to use the method we just defined(in articles controller):
$scope.findOne = function() {
$scope.article = Articles.getBySlug({
slug: $stateParams.articleSlug
});
5- Finally re-factor the update and create to include the slug (in articles controller):
$scope.update = function() {
var article = $scope.article;
article.slug = article.title.toLowerCase().replace(/ /g, '-');
console.log(article);
article.$update(function() {
$location.path('articles/' + article.slug);
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
$scope.create = function() {
var article = new Articles({
slug: this.title.toLowerCase().replace(/ /g, '-'),
title: this.title,
content: this.content
});
article.$save(function(response) {
$location.path('articles/' + response.slug);
$scope.title = '';
$scope.content = '';
}, function(errorResponse) {
$scope.error = errorResponse.data.message;
});
};
Upvotes: 1