Get Id for item in EJS template and inject into service - angularjs

I am trying to figure out how to pass an Id into my PlayerDetailController and then use it in my service.
I have the following route in my main app.js:
var players = require('./routes/players');
app.use('/players', players);
And I have the following routes inside of the players route (routes/players.js):
var express = require('express');
var router = express.Router();
/* GET /players listing. */
router.get('/', function(req, res, next) {
res.render('players', { title: 'Players', action: 'list'});
});
/* GET /player details. */
router.get('/:id', function(req, res, next) {
res.render('players', { title: 'Player Details', action: 'details'});
});
module.exports = router;
And the following in my main players.ejs template:
<!-- other layout elements -->
<% if (action === 'details') { %>
<% include partials/players/details %>
<% } else if (action === 'update') { %>
<% include partials/players/update %>
<% } else if (action === 'remove') { %>
<% include partials/players/remove %>
<% } else { %>
<% include partials/players/list %>
<% } %>
<!-- other layout elements -->
And my deatils.ejs partial:
<div ng-controller="PlayerDetailsController">
<div class="playerDetails">
<p>
<strong>{{ player.name}} - {{ player.position }} - {{ player.team }}</strong><br/>
Touchdowns: {{ player.touchdowns }}<br/>
Yards: {{ player.yards }}
</p>
</div>
</div>
And my PlayerDetiails service:
// PlayerDetails Service
app.factory('PlayerDetails', ['$http', function($http){
// below is where I'm trying to inject the Id
return $http.get('/api/player/:id');
}]);
But I have no idea how I can pass the Id that shows up in my route (ex. http://localhost:3000/players/550130d32f3345bc065f2ecf) into my controller so that my controller calls and receives the correct player data. How would I do this? I thought I could add a id property to the res.render call, but I'm not sure what I would do with it in the EJS template. Any help would be greatly appreciated!

Pass the id to your template:
router.get('/:id', function(req, res, next) {
res.render('players', { title: 'Player Details', action: 'details', id: req.params.id});
});
ng-init receives the id:
<div ng-controller="PlayerDetailsController" ng-init="id = <%= id %>">
PlayerDetails service:
app.service('PlayerDetails', ['$http', function($http){
return {
get: function(id) {
return $http({
url: '/api/player/:id',
params: {id: id},
method: 'GET'
});
}
};
}]);
Somewhere in PlayerDetailsController:
// id is from ng-init
PlayerDetails.get($scope.id).success(function(player) {
$scope.player = player;
}).error(function(error) {
console.error(error);
});

The ID passed to the route will be available in req.params.id. You can retrieve the player from the database using the ID and pass the object to the view.
Something like this, using Mongoose ODM. If you are using another database/ODM/ORM the code will be different but the idea is the same:
/* GET /player details. */
router.get('/:id', function(req, res, next) {
Player.find({ _id: req.params.id }, function(err, player) {
if(err) return next(err);
res.render('players', { title: 'Player Details', action: 'details', player: player });
});
});
Then you will have access to the Player document via player attribute.
If you want to create an API that will return all the player data in JSON format, you can do it like this:
/* GET /api/player/:id */
router.get('/:id', function(req, res, next) {
Player.find({ _id: req.params.id }, function(err, player) {
if(err) return next(err);
res.json(player);
});
});

Related

MEAN Stack, MongoDB record creation not working

I am developing a MEAN stack application. I am trying to simply create a record in MongoDB from a form. I have verified in the debugger that the data binding is working between the view and the controller. In the server side controller code, checking the req.body before trying to save the record returns "undefined" (see below in the code). In the Angular controller, I have examined the "results" value in the callback when the announcement.$save function is executed and it shows the heading and details values to be populated as they should. However, the data is not persisted to the database and I get the following error:
{ [ValidationError: Announcement validation failed]
message: 'Announcement validation failed',
name: 'ValidationError',
errors:
{ details:
{ [ValidatorError: Path `details` is required.]
properties: [Object],
message: 'Path `details` is required.',
name: 'ValidatorError',
kind: 'required',
path: 'details',
value: undefined },
heading:
{ [ValidatorError: Path `heading` is required.]
properties: [Object],
message: 'Path `heading` is required.',
name: 'ValidatorError',
kind: 'required',
path: 'heading',
value: undefined } } }
What am I missing? Here is my code:
The form in my html file:
<form ng-submit="AnnouncementsVm.createAnnouncement()">
<fieldset class="form-group">
<label for="heading">Heading:</label>
<textarea class="form-control" id="heading" rows="1"
ng-model="AnnouncementsVm.announcementHeading"></textarea>
</fieldset>
<fieldset class="form-group">
<label for="details">Details:</label>
<textarea class="form-control" id="details" rows="3"
ng-model="AnnouncementsVm.announcementDetails"></textarea>
</fieldset>
<p><input type="submit" value="Submit →"><p>
</form>
The angular route for this page partial is defined as:
$routeProvider.
when('/announcements', {
templateUrl: '/views/partials/announcements.html',
controller: 'Announcements.Controller',
controllerAs: 'AnnouncementsVm'
});
Here is my controller code:
angular.module('app').
controller('Announcements.Controller', AnnouncementsCtrl);
function AnnouncementsCtrl($log, $resource) {
$log.debug('Executing AnnouncementsCtrl');
var vm = this;
var Announcement = $resource('/api/announcements');
Announcement.query( function(results) {
vm.announcements = results;
});
vm.announcements = [];
vm.createAnnouncement = function() {
var announcement = new Announcement({
heading: vm.announcementHeading,
details: vm.announcementDetails
});
announcement.$save( function(result) {
vm.announcements.push(result);
vm.announcementHeading = '';
vm.announcementDetails = '';
});
};
}
The REST API route is defined as:
app.post('/api/announcements', announcementsController.create);
The server side controller (announcements-controller.js):
'use strict';
var Announcement = require('../models/Announcement.js');
module.exports.create = function(req, res) {
var announcement = new Announcement(req.body);
console.log(req.body); // returns "undefined"
announcement.save( function(err, result) {
if (err) console.log(err);
console.log('Save Announcement Result: ' + result);
res.json(result);
});
};
module.exports.list = function(req, res) {
Announcement.find({}, function (err, results) {
if (err) throw err;
res.json(results);
});
};
And finally, I am using this Mongoose model (Announcements.js)
'use strict';
var mongoose = require('mongoose');
var AnnouncementSchema = new mongoose.Schema({
heading: {type: String, required: true},
details: {type: String, required: true},
image: {type: String, required: false}
});
module.exports = mongoose.model('Announcement', AnnouncementSchema);
How is configured your routes in Angular? Are you passing the controller as 'AnnouncementsVm'?
Have you tried to access the values of the ng-models announcementHeading and announcementDetails from the controller?
Try to put
vm.createAnnouncement = function() {
$log.log(vm.announcementHeading);
$log.log(vm.announcementDetails);
});
};
And check if you are getting the correct values
Problem solved. I had not integrated the body-parser for the route so the request wasn't being populated correctly. Here is the updated ExpressJS route:
'use strict';
var bodyParser = require('body-parser');
var path = require('path');
var announcementsController = require('../controllers/announcements-controller.js');
module.exports = function(app) {
// create application/json parser
var jsonParser = bodyParser.json();
// create application/x-www-form-urlencoded parser
var urlencodedParser = bodyParser.urlencoded({ extended: false });
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname, '../../client/views/index.html'));
});
// REST API
app.get('/api/announcements', announcementsController.list);
app.post('/api/announcements', jsonParser, announcementsController.create);
};

PUT request overwrites array values with duplicate values

I am making a small polls app with Yeoman's angular-fullstack generator. I have got to the stage where I am implementing user selection to increment a polls count, which then updates my MongoDB database via a PUT request.
Question is set up ready for the user to vote:
The object has individual IDs:
Then, a user goes to vote on an answer. Looks fine on the client:
But it's not updating correctly in the server. All items in the array change to the first item, with identical IDs:
When the client is refreshed, it loads the data from the server and obviously this is not what I want:
What am I doing wrong?
Here is the view:
<form ng-submit="submitForm()">
<div ng-repeat="answer in poll.answers">
<label><input type="radio" name="option" ng-model="radioData.index" value="{{$index}}"/>
{{ answer.value }} - {{ answer.votes }} Votes
</label>
</div>
<button class="btn btn-success" type="submit">Vote!</button>
</form>
Here is the controller:
'use strict';
angular.module('angFullstackCssApp')
.controller('ViewCtrl', function ($scope, $routeParams, $http) {
$http.get('/api/polls/' + $routeParams._id).success(function (poll) {
console.log(poll);
$scope.poll = poll;
$scope.radioData = {
index: 0
};
$scope.submitForm = function () {
console.log($scope.radioData.index);
$scope.poll.answers[$scope.radioData.index].votes += 1;
console.log('scope poll answers:- ', $scope.poll.answers);
console.log('scope poll answers[index]:- ', $scope.poll.answers[$scope.radioData.index]);
console.log('votes:- ', $scope.poll.answers[$scope.radioData.index].votes);
// Change database entry here
$http.put('/api/polls/' + $routeParams._id, {answers: $scope.poll.answers}).success(function () {
console.log('success');
});
};
});
});
Here is the relevant server-side code, all left from the default of my route and endpoint setting up:
router.put('/:id', controller.update);
// Updates an existing poll in the DB.
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Poll.findById(req.params.id, function (err, poll) {
if (err) { return handleError(res, err); }
if(!poll) { return res.status(404).send('Not Found'); }
var updated = _.merge(poll, req.body);
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.status(200).json(poll);
});
});
};
And the schema:
var PollSchema = new Schema({
creator: String,
title: String,
answers: [{
value: String,
votes: Number
}]
}, { versionKey: false });

AngularJS to Mongoose params on queries

I'm using the mean stack and I can´t figure out how to pass params to mongoose query from the angular controller.
From the mean stack (https://github.com/meanjs/mean) example, we have:
On the server side
an article model
/**
* Article Schema
*/
var ArticleSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true,
required: 'Title cannot be blank'
},
content: {
type: String,
default: '',
trim: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Article', ArticleSchema);
an article controller with a function to obtain a list of all articles and another function to obtain an article by Id
/**
* 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.articleByID = function(req, res, next, id) {
Article.findById(id).populate('user', 'displayName').exec(function(err, article) {
if (err) return next(err);
if (!article) return next(new Error('Failed to load article ' + id));
req.article = article;
next();
});
};
and the articles routes
/**
* Module dependencies.
*/
var users = require('../../app/controllers/users.server.controller'),
articles = require('../../app/controllers/articles.server.controller');
module.exports = function(app) {
// Article Routes
app.route('/articles')
.get(articles.list)
.post(users.requiresLogin, articles.create);
app.route('/articles/:articleId')
.get(articles.read)
.put(users.requiresLogin, articles.hasAuthorization, articles.update)
.delete(users.requiresLogin, articles.hasAuthorization, articles.delete);
// Finish by binding the article middleware
app.param('articleId', articles.articleByID);
};
on the client side
we have an articles module with a routes config file
// Setting up route
angular.module('articles').config(['$stateProvider',
function($stateProvider) {
// Articles state routing
$stateProvider.
state('listArticles', {
url: '/articles',
templateUrl: 'modules/articles/views/list-articles.client.view.html'
}).
state('viewArticle', {
url: '/articles/:articleId',
templateUrl: 'modules/articles/views/view-article.client.view.html'
});
}
]);
an articles controller
angular.module('articles').controller('ArticlesController', ['$scope', '$stateParams', '$location', 'Authentication', 'Articles',
function($scope, $stateParams, $location, Authentication, Articles) {
$scope.authentication = Authentication;
$scope.find = function() {
$scope.articles = Articles.query();
};
$scope.findOne = function() {
$scope.article = Articles.get({
articleId: $stateParams.articleId
});
};
}
]);
and a list view
<section data-ng-controller="ArticlesController" data-ng-init="find()">
<div class="page-header">
<h1>Articles</h1>
</div>
<div class="list-group">
<a data-ng-repeat="article in articles" data-ng-href="#!/articles/{{article._id}}" class="list-group-item">
<small class="list-group-item-text">
Posted on
<span data-ng-bind="article.created | date:'mediumDate'"></span>
by
<span data-ng-bind="article.user.displayName"></span>
</small>
<h4 class="list-group-item-heading" data-ng-bind="article.title"></h4>
<p class="list-group-item-text" data-ng-bind="article.content"></p>
</a>
</div>
<div class="alert alert-warning text-center" data-ng-if="articles.$resolved && !articles.length">
No articles yet, why don't you create one?
</div>
My question is:
If I want to find all the article of a user, how can I pass a variable param to the find() function in the angular view?
I thought that the Articles.query() in the angular controller works as a mongodb or mongoose command, but I wasn't able to implement it.
Pass an object in query() method and it will be sent to server as query variables. In server use req.query to get those variables:
Client:
$scope.articles = Articles.query({user: 'user_id'});
Server:
Article.find({user: req.query.user}).sort('-created').populate('user', 'displayName').

AngularJS show 2 models in one controller

I have 2 models(Project and Task) to show in my home.html, and I want my them to be displayed like this: http://plnkr.co/edit/ItNvBNBIrLxqwRrhye5p, where both scope of data is shown and filtered based on the same color.
I use an Angular controller (projectCtrl.js) to control data on my web page(home.html), and use an Angular service (projectService.js) to grab data from my api file (api.js) written with express framework and mongoose.
But my code doesn't show anything, so I have no idea what's wrong.
home.html:
<div class="row" ng-if="main.loggedIn">
<div ng-controller="ProjectController">
<div class="panel col-md-8">
<!-- Project heading setup -->
<div class="panel-group" ng-repeat="eachProject in project.projects | reverse track by $index">
<div class="panel panel-info">
<div class="panel-heading" data-toggle="collapse" ng-click="true" data-target="#projectDetails{{$index}}" href="#projectDetails{{$index}}">
<h4>{{eachProject.title}}: {{eachProject.short_description}}</h4>
</div>
<div class="panel-collapse collapse out" id="projectDetails{{$index}}">
<p class="panel-body">
<!-- Project detail table, where project data displays-->
<table class="table table-responsive table-bordered table-hover">
<tr>
<th>Description: </th>
<td>{{eachProject.description}}</td>
</tr>
</table>
<!-- Task heading setup -->
<div class="panel-group" ng-repeat="eachTask in task.tasks | filter: { projectID: eachProject.id }">
<div class="panel panel-success">
<div class="panel-heading" data-toggle="collapse" ng-click="true" data-target="#taskDetails{{$index}}" href="#taskDetails{{$index}}">
<h5>{{eachTask.title}}</h5>
</div>
<div class="panel-collapse collapse out" id="taskDetails{{$index}}">
<p class="panel-body">
<!-- Task detail table, where tasks data displays -->
<table class="table table-responsive table-bordered table-hover">
<tr>
<th>Description: </th>
<td>{{eachTask.description}}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
ProjectCtrl.js
angular.module('projectCtrl', ['projectService'])
.controller('ProjectController', function(Project, Task, socketio) {
var vm = this;
Project.all()
.success(function(data) {
vm.projects = data;
})
vm.createProject = function() {
// Wrong due date prevention
var start = new Date(vm.projectData.start_date);
var due = new Date(vm.projectData.due_date);
if (start > due) {
alert("Due date can't be earlier than start date, please decide a new due date.");
return;
}
// Create project
vm.message = '';
Project.create(vm.projectData)
.success(function(data) {
// Clear up the project
vm.projectData = '';
vm.message = data.message;
$('#createProject').modal('hide');
})
}
Task.all()
.success(function(data) {
vm.tasks = data;
})
vm.createTask = function() {
// Wrong due date prevention
var start = new Date(vm.taskData.taskStart_date);
var due = new Date(vm.taskData.taskDue_date);
if (start > due) {
alert("Due date can't be earlier than start date, please decide a new due date.");
return;
}
// Create task
vm.message = '';
Task.create(vm.taskData)
.success(function(data) {
// Clear up the task
vm.taskData = '';
vm.message = data.message;
$('#createTask').modal('hide');
})
}
socketio.on('project', 'task', function(data) {
vm.projects.push(data);
vm.tasks.push(data);
})
})
.controller('AllProjectsController', function(projects, socketio) {
var vm = this;
vm.projects = projects.data;
socketio.on('project', function(data) {
vm.projects.push(data);
})
})
projectService.js
angular.module('projectService', [])
.factory('Project', function($http) {
var projectFactory = {};
projectFactory.create = function(projectData) {
return $http.post('/api', projectData);
}
projectFactory.allProjects = function() {
return $http.get('/api/all_projects');
}
projectFactory.all = function() {
return $http.get('/api');
}
projectFactory.deleteProject = function(id) {
return $http.post('/api/deleteProject', {id: id});
}
return projectFactory;
})
.factory('Task', function($http) {
var taskFactory = {};
taskFactory.create = function(taskData) {
return $http.post('/api', taskData);
}
taskFactory.allTasks = function() {
return $http.get('/api/all_tasks');
}
taskFactory.all = function() {
return $http.get('/api');
}
taskFactory.deleteTask = function(id) {
return $http.post('/api/deleteTask', {projectID: id});
}
return taskFactory;
})
.factory('socketio', function($rootScope) {
var socket = io.connect();
return {
on: function(eventName, callback) {
socket.on(eventName, function() {
var args = arguments;
$rootScope.$apply(function() {
callback.apply(socket, args);
})
})
},
emit: function(eventName, data, callback) {
socket.emit(eventName, data, function() {
var args = arguments;
$rootScope.apply(function() {
if(callback) {
callback.apply(socket, args);
}
})
})
}
}
})
api.js
var User = require('../models/user');
var Project = require('../models/project');
var config = require('../../config');
var secretKey = config.secretKey;
var jsonwebtoken = require('jsonwebtoken');
var fields = '...'; // a lot of fields, deleted them just to make it short
// Create tokens for users with jsonwebtoken
function createToken(user) {
var token = jsonwebtoken.sign({
id: user._id,
firstname: user.firstname,
lastname: user.lastname,
username: user.username
}, secretKey, {
expirtesInMinute: 1440
});
return token;
}
module.exports = function(app, express, io) {
var api = express.Router();
api.get('/all_projects', function(req, res) {
Project.find({}, function(err, projects) {
if (err) {
res.send(err);
return;
}
res.json(projects);
})
})
// login api
api.post('/login', function(req, res) {
User.findOne({
username: req.body.username
}).select(fields).exec(function(err, user) {
if(err) {
throw err;
}
if (!user) {
res.send({ message: "User doesn't exist"});
} else if(user){
var validPassword = user.comparePassword(req.body.password);
if (!validPassword) {
res.send({ message: "Invalid Password"});
} else {
var token = createToken(user);
res.json({
success: true,
message: "Login Successfully !",
token: token
});
}
}
});
});
//middleware
api.use(function(req, res, next) {
console.log("Somebody just logged in!");
var token = req.body.token || req.param('token') || req.headers['x-access-token'];
if (token) {
jsonwebtoken.verify(token, secretKey, function(err, decoded) {
if (err) {
res.status(403).send({success: false, message: "Failed to authenticate user."});
} else {
req.decoded = decoded;
next();
}
});
} else {
res.status(403).send({ success: false, message: "No Token Provided." });
}
});
//api for projects handling
api.route('/')
.post(function(req, res) {
var project = new Project({
creatorID: req.decoded.id,
creator: req.decoded.firstname + " " + req.decoded.lastname,
creator_dept: req.decoded.department,
title: req.body.title,
short_description: req.body.short_description,
description: req.body.description,
priority: req.body.priority,
status: calcStatus(),
assign_dept: req.body.assign_dept,
estimate_cost: req.body.estimate_cost,
actual_cost: req.body.actual_cost,
last_modified_date: req.body.last_modified_date,
due_date: req.body.due_date,
start_date: req.body.start_date,
complete_date: req.body.complete_date,
});
project.save(function(err, newProject) {
if (err) {
res.send(err);
return;
}
io.emit('project', newProject);
res.json({
message: "New Project Created!"
});
});
})
.get(function(req, res) {
Project.find( {creatorID: req.decoded.id}, function(err, project) {
if (err) {
res.send(err);
return;
}
res.json(project);
});
});
//api for tasks handling
api.route('/')
.post(function(req, res) {
var task = new Task({
creatorID: req.decoded.id,
creator: req.decoded.firstname + " " + req.decoded.lastname,
projectID: req.body.taskProjectID,
title: req.body.taskTitle,
description: req.body.taskDescription,
status: calcStatus(),
assigneeName: req.body.assigneeName,
assigneeID: req.body.assigneeID,
assignee_dept: req.body.assignee_dept,
estimate_cost: req.body.taskEstimate_cost,
actual_cost: req.body.TaskActual_cost,
last_modified_date: req.body.taskLast_modified_date,
due_date: req.body.taskDue_date,
start_date: req.body.taskStart_date,
complete_date: req.body.taskComplete_date,
});
task.save(function(err, newTask) {
if (err) {
res.send(err);
return;
}
io.emit('tasks', newTask);
res.json({
message: "New Task Created!"
});
});
})
.get(function(req, res) {
Task.find( {projectID: req.decoded.id}, function(err, task) {
if (err) {
res.send(err);
return;
}
res.json(task);
});
});
// api for angular
api.get('/me', function(req, res) {
res.json(req.decoded);
});
return api;
}
Thanks for the help.
You reference project.projects here:
<div class="panel-group" ng-repeat="eachProject in project.projects | reverse track by $index">
But project is never in ProjectController... I think you may want to set project to refer to your ProjectController, try this:
<div ng-controller="ProjectController as project">
EDIT
A few other problems. You mentioned you wanted to show all Tasks as well, in ProjectController you populate projects and tasks like so:
Project.all()
.success(function(data) {
vm.projects = data;
})
....
Task.all()
.success(function(data) {
vm.tasks = data;
})
That's good, except the definitions for Project.all and Task.all are referencing the same data source:
projectFactory.all = function() {
return $http.get('/api');
}
taskFactory.all = function() {
return $http.get('/api');
}
They both reference /api. How can they both expect different data from the same route? They should likely be two distinct routes.
In addition, your definition in api.js for this end point requires an ID to be passed to get a single project:
.get(function(req, res) {
Task.find( {projectID: req.decoded.id}, function(err, task) {
if (err) {
res.send(err);
return;
}
res.json(task);
});
});
.get(function(req, res) {
// The request must contain a creatorId, otherwise you're not going to find your project
Project.find( {creatorID: req.decoded.id}, function(err, project) {
if (err) {
res.send(err);
return;
}
res.json(project);
});
});
Perhaps your projectService should be pointing to the /all_projects end point instead for .all. So to sum up:
API end points for Project/Task should be different
Be sure to pass the project ID/task if necessary, otherwise you're not going to get the data you're expecting.
Set breakpoints and log output to trace your code path to see where other mistakes may be.

AngularJS - Templates not working

I am using Restangular to create a simple API using MEAN stack.
Here is my code:
index.html
<!DOCTYPE html>
<html data-ng-app="scotchTodo">
<head>
<!-- META -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"><!-- Optimize mobile viewport -->
<title>Node/Angular Todo App</title>
<!-- SCROLLS -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"><!-- load bootstrap -->
<style>
html{
overflow-y:scroll;
}
body{
padding-top:50px;
}
</style>
<!-- SPELLS -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular-route.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/restangular/1.4.0/restangular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<script src="app.js"></script>
</head>
<body>
<div data-ng-view>
</div>
</body>
</html>
app.js
var scotchTodo = angular.module('scotchTodo', ['restangular','ngRoute']);
//config
scotchTodo.config(['$routeProvider','$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider
.when('/',{
templateUrl: 'list.html',
controller: 'ListController'
})
.when('api/todos/:todo_id',{
templateUrl: 'edit.html',
controller: 'EditController'
});
$locationProvider.html5Mode(true);
}]);
//controllers
scotchTodo.controller('ListController', ['$scope', 'Restangular',
function($scope, Restangular) {
//GET ALL
var baseTodo = Restangular.all('api/todos');
baseTodo.getList().then(function(todos) {
$scope.todos = todos;
});
//POST -> Save new
$scope.save = function() {
var baseTodo = Restangular.all('api/todos');
var newTodo = {'text': $scope.text};
baseTodo.post(newTodo).then(function(todos) {
$scope.todos = todos;
$scope.text = '';
});
};
//DELETE
$scope.delete = function(id) {
var baseTodo = Restangular.one('api/todos', id);
baseTodo.remove().then(function(todos) {
$scope.todos = todos;
});
};
}]);
scotchTodo.controller('EditController', ['$scope', 'Restangular','$routeParams',
function($scope, Restangular, $routeParams) {
var baseTodo = Restangular.one('api/todos', id);
baseTodo.getList().then(function(todo) {
$scope.todo = todo[0];
window.test = "dev";
});
//PUT -> Edit
$scope.update = function(id){
var baseTodo = Restangular.one('api/todos', id);
baseTodo.text = "Edited";
baseTodo.put().then(function(todos) {
$scope.todos = todos;
});
};
}]);
list.html
<div>
<div data-ng-repeat="todo in todos">
{{todo.text}}Edit<button data-ng-click="delete(todo._id)">X</button>
</div>
<input type="text" data-ng-model="text"/>
<button data-ng-click="save()">Add</button>
</div>
edit.html
<div>
<input type="text" data-ng-model="text" value="{{todo.text}}" />
<button data-ng-click="update(todo._id)">Save</button>
</div>
server.js
// setup ========================
var express = require('express');
var app = express();
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
//configuration =================
mongoose.connect('mongodb://127.0.0.1:27017/sl', function(err, db) {
if (!err) {
console.log("We are connected to " + db);
}
});
app.use(express.static(__dirname + '/public'));
app.use(bodyParser());
// application -------------------------------------------------------------
app.get('/', function(req, res) {
res.sendfile('./public/index.html'); // load the single view file (angular will handle the page changes on the front-end)
});
//listen ========================
app.listen(8080);
console.log('App started on the port 8080');
//define model ==================
var Todo = mongoose.model('Todo', {
text: String
});
// routes ======================================================================
// api ---------------------------------------------------------------------
//get one todo
app.get('/api/todos/:todo_id', function(req, res) {
// use mongoose to get all todos in the database
Todo.find({
_id: req.params.todo_id
},function(err, todos) {
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err){
res.send(err);
}
res.json(todos); // return all todos in JSON format
});
});
// get all todos
app.get('/api/todos', function(req, res) {
// use mongoose to get all todos in the database
Todo.find(function(err, todos) {
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err){
res.send(err);
}
res.json(todos); // return all todos in JSON format
});
});
// create todo and send back all todos after creation
app.post('/api/todos', function(req, res) {
// create a todo, information comes from AJAX request from Angular
Todo.create({
text: req.body.text,
done: false
}, function(err, todo) {
if (err){
res.send(err);
}
// get and return all the todos after you create another
Todo.find(function(err, todos) {
if (err)
res.send(err);
res.json(todos);
});
});
});
// update todo and send back all todos after creation
app.put('/api/todos/:todo_id', function(req, res) {
// create a todo, information comes from AJAX request from Angular
Todo.update({
_id: req.params.todo_id
}, {
text:req.body.text
}, function(err, todo) {
if (err){
res.send(err);
}
// get and return all the todos after you create another
Todo.find(function(err, todos) {
if (err)
res.send(err);
res.json(todos);
});
});
});
// delete a todo
app.delete('/api/todos/:todo_id', function(req, res) {
Todo.remove({
_id: req.params.todo_id
}, function(err, todo) {
if (err){
res.send(err);
}
// get and return all the todos after you create another
Todo.find(function(err, todos) {
if (err){
res.send(err);
}
res.json(todos);
});
});
});
The first page of my app loads perfectly fine. Here is the screenshot.
But when I click on either of the edit link it is supposed to load edit.html template. But it shows a blank page with no errors in console. Here is the screenshot.
I am unable to figure out what's wrong. Please help. Please ask if any other piece of code is needed. I added almost everything that I did. I know it is annoying and not recommended but I am not sure what part of my code is causing this issue.
EDIT 1:
My farthest guess is that the url for edit.html might not be getting resolved correctly. But I am not sure how to test that! Any help will be appriciated.
EDIT 2: Directory structure
SOLUTION : Courtesy #ashu
The issue was this line in index.html
<script src="app.js"></script>
It should be:
<script src="/app.js"></script>
However, I am not clear why! Page was including app.js either way. It is weird.
You have same routes for angular and express.
.when('api/todos/:todo_id',{
templateUrl: 'edit.html',
controller: 'EditController'
});
and in express
app.get('/api/todos/:todo_id', function(req, res) {
Hence, there is ambiguity. You can remove the 'api' part from angular urls.
.when('/todos/:todo_id', {
templateUrl: 'edit.html',
controller: 'EditController'
})
And in server, you can add a catchall route which will handle all the non-api urls. For doing that you can move your app.get('/', function(req,res) {..}) call at the bottom after defining your api routes.
// < Define APi Routes here >
//catch all route for serving the html template
app.get('/*', function(req, res ) {
res.sendfile('./public/index.html')
});
Also, in your app.js EditController, you forgot to initialise value of id.
var id = $routeParams.todo_id;

Resources