Creating fields in CRUD model MEAN.js - angularjs

I'm starting with MEAN.js framework and I´m trying to create my first app. I'm creating the App from a CRUD module generated with Yeoman (yo meanjs:crud-module).
The app is a simple form with 2 fields. "name" and "image". Name is the title of the image, and image the URL of image.
My problem now is that I can´t insert in the Mongo collection "photos" both fields from the form. Only the field "name" generated by default with the CRUD module generator is inserted correctly in the collection.
What am I doing wrong?
photo.server.model.js
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
/**
* Photo Schema
*/
var PhotoSchema = new Schema({
name: {
type: String,
default: '',
required: 'Introduce el nombre de la imagen',
trim: true
},
image: {
type: String,
default: '',
required: 'Introduce la url de la imagen',
trim: true
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Photo', PhotoSchema);
photo.server.controller.js
'use strict';
/**
* Module dependencies.
*/
var path = require('path'),
mongoose = require('mongoose'),
Photo = mongoose.model('Photo'),
errorHandler = require(path.resolve('./modules/core/server/controllers/errors.server.controller')),
_ = require('lodash');
/**
* Create a Photo
*/
exports.create = function(req, res) {
var photo = new Photo(req.body);
photo.user = req.user;
photo.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(photo);
}
});
};
/**
* Show the current Photo
*/
exports.read = function(req, res) {
// convert mongoose document to JSON
var photo = req.photo ? req.photo.toJSON() : {};
// Add a custom field to the Article, for determining if the current User is the "owner".
// NOTE: This field is NOT persisted to the database, since it doesn't exist in the Article model.
photo.isCurrentUserOwner = req.user && photo.user && photo.user._id.toString() === req.user._id.toString();
res.jsonp(photo);
};
/**
* Update a Photo
*/
exports.update = function(req, res) {
var photo = req.photo;
photo = _.extend(photo, req.body);
photo.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(photo);
}
});
};
/**
* Delete an Photo
*/
exports.delete = function(req, res) {
var photo = req.photo;
photo.remove(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(photo);
}
});
};
/**
* List of Photos
*/
exports.list = function(req, res) {
Photo.find().sort('-created').populate('user', 'displayName').exec(function(err, photos) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(photos);
}
});
};
/**
* Photo middleware
*/
exports.photoByID = function(req, res, next, id) {
if (!mongoose.Types.ObjectId.isValid(id)) {
return res.status(400).send({
message: 'Photo is invalid'
});
}
Photo.findById(id).populate('user', 'displayName').exec(function (err, photo) {
if (err) {
return next(err);
} else if (!photo) {
return res.status(404).send({
message: 'No Photo with that identifier has been found'
});
}
req.photo = photo;
next();
});
};
photo.client.controller.js
(function () {
'use strict';
// Photos controller
angular
.module('photos')
.controller('PhotosController', PhotosController);
PhotosController.$inject = ['$scope', '$state', '$window', 'Authentication', 'photoResolve'];
function PhotosController ($scope, $state, $window, Authentication, photo) {
var vm = this;
vm.authentication = Authentication;
vm.photo = photo;
vm.error = null;
vm.form = {};
vm.remove = remove;
vm.save = save;
// Remove existing Photo
function remove() {
if ($window.confirm('Are you sure you want to delete?')) {
vm.photo.$remove($state.go('photos.list'));
}
}
// Save Photo
function save(isValid) {
if (!isValid) {
$scope.$broadcast('show-errors-check-validity', 'vm.form.photoForm');
return false;
}
// TODO: move create/update logic to service
if (vm.photo._id) {
vm.photo.$update(successCallback, errorCallback);
} else {
vm.photo.$save(successCallback, errorCallback);
//console.log(vm.photo.image);
}
function successCallback(res) {
$state.go('photos.view', {
photoId: res._id
});
}
function errorCallback(res) {
vm.error = res.data.message;
}
}
}
}());
form-photo.client.view.html
<section>
<div class="page-header">
<h1>{{vm.photo._id ? 'Edit Photo' : 'Nueva imagen'}}</h1>
</div>
<div class="col-md-12">
<form name="vm.form.photoForm" class="form-horizontal" ng-submit="vm.save(vm.form.photoForm.$valid)" novalidate>
<fieldset>
<div class="form-group" show-errors>
<label class="control-label" for="name">Nombre</label>
<input name="name" type="text" ng-model="vm.photo.name" id="name" class="form-control" placeholder="Name" required>
<div ng-messages="vm.form.photoForm.name.$error" role="alert">
<p class="help-block error-text" ng-message="required">El nombre de la foto es requerido</p>
</div>
</div>
<div class="form-group" show-errors>
<label class="control-label" for="image">URL</label>
<input name="image" type="text" ng-model="vm.photo.image" id="image" class="form-control" placeholder="Url" required>
<div ng-messages="vm.form.photoForm.image.$error" role="alert">
<p class="help-block error-text" ng-message="required">La URL de la foto es requerido</p>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default">{{vm.photo._id ? 'Update' : 'Crear'}}</button>
</div>
<div ng-show="vm.error" class="text-danger">
<strong ng-bind="vm.error"></strong>
</div>
</fieldset>
</form>
</div>
</section>

Use this link and see example, since
mean 'js'
uses
multer
to upload in 0.4.2 is already integrated so if you want u can also generate and copy from there and edit little bit since they are not compatible

Related

Clearing emails after sending in angular

I'm trying to figure out how I can clear the emails in the list. I've managed to create a function where I can remove emails one by one. But I can't seem to find a way on how to clear them all after sending clicking the send invite button.
I'm working on project using angular.js and below is a sample of the code that is a work in progress
<div class="content referral-sender-page">
<div class="wrapper">
<div class="main" ng-controller="ReferralDispatchController">
<h1 class="h large">Send Invites</h1>
<!-- TODO: Explanatory text -->
<div>
<section class="grid__item bp-md-one-third">
<h5 class="h medium gray">To</h5>
<span ng-repeat="e in emails" class="h small email-list">
<span remove-on-click ng-click="removeEmail(e)" class="email-item">{{ e }} <small class="close">X</small></span>
</span>
<div class="col-2">
<input class="input" type="email"
ng-model="email"
placeholder="Email">
<a ng-click="addEmail()" class="button pads primary" >+</a>
</div>
</section>
<section class="grid__item bp-md-two-thirds">
<h5 class="h medium gray">Message</h5>
<p>Write a message to send to your homies with your invite</p>
<textarea class="text-input" ng-model="message" rows="5">
This is awesome and you should try it!
</textarea>
</section>
</div>
<div class="space--bottom one-whole">
<a ng-click="sendReferral()" class="button pads primary">Send Email Invite</a>
</div>
</div>
</div>
</div>
var ctrls = angular.module('elstudio.controllers.site');
//Removes Element only
// ctrls.directive('removeOnClick', function() {
// return {
// link: function(scope, elt, attrs) {
// scope.removeEmail = function() {
// elt.remove();
// };
// }
// }
// });
ctrls.controller('ReferralDispatchController', function ($scope, UserService,
ReferralService) {
$scope.emails = [];
$scope.message = '';
$scope.removeEmail = function(e) {
var index = $scope.emails.indexOf(e);
$scope.emails.splice(index, 1);
};
$scope.addEmail = function() {
if (!$scope.email) {
$scope.$emit('notify', { message: 'Please provide a valid email address' });
return;
}
// If email already in list, ignore
// FIXME: Provide feedback
if ($scope.emails.indexOf($scope.email) != -1) {
$scope.email = '';
return;
}
$scope.emails.push($scope.email);
$scope.email = '';
};
$scope.sendReferral = function() {
if (!$scope.loginUser) {
$scope.$emit('notify', { message: 'Please sign up or log in to your Electric account.',
duration: 3000 });
angular.element('html, body').animate({ scrollTop: 0 }, 'slow');
angular.element('.login-toggle').click();
return;
}
if ($scope.email != '') {
$scope.emails.push($scope.email);
}
if (!$scope.emails) {
$scope.$emit('notify', { message: 'Please provide at least one email address' });
return;
}
var refer = {
emails: $scope.emails,
message: $scope.message
};
var sendSuccess = function() {
$scope.$emit('notify', { message: 'An invitation has been sent!',
duration: 4000 });
};
var sendFailed = function(error) {
// Retry?
$scope.$emit('notify', { message: "Couldn't send invitation",
duration: 4000 });
};
ReferralService.email(refer).$promise.then(sendSuccess, sendFailed);
};
});
$scope.email = []; used to clear the array.
var sendSuccess = function() {
$scope.$emit('notify', { message: 'An invitation has been sent!',
duration: 4000 });
$scope.email = [];
};
Please Use
var refer = {
emails: angular.copy($scope.emails),
message: angular.copy($scope.message)
};
Please view the detail
It will do deep copy. So when we change the value in $scope.emails and $scope.message copied value will not change
also clear the $Scope.email value in the sendSuccess function

Thinkster MEAN stack tutorial, deletion of element

I've been following the Thinkster Mean Stack tutorial, and it works wonderfully. So I have created my own project, and so far everything works fine.
However, they didn't cover how to delete posts.
And I can for the life for me not figure out how to delete an element from the data.
AngularApp.js
var app = angular.module('KOL', ['ui.router'])
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('home', {
url: '/home',
templateUrl: "/views/home.ejs",
controller: 'kolCtrl',
resolve: {
patientPromise: ['patients', function(patients) {
return patients.getAll();
}]
}
})
.state('details', {
url: '/details/{id}',
templateUrl: './views/details.html',
controller: 'detailsCtrl'
});
$urlRouterProvider.otherwise('home');
}])
.factory('patients', ['$http', function($http){
var object = {
patients: []
};
object.getAll = function() {
return $http.get('/patients').success(function(data) {
angular.copy(data, object.patients);
});
}
object.create = function(patient) {
return $http.post('/patients', patient).success(function(data){
object.patients.push(data);
});
}
};
return object;
}])
.controller('kolCtrl', ['$scope', 'patients',
function($scope, patients){
$scope.patients = patients.patients;
$scope.selectedItem = $scope.patients[0];
$scope.addPost = function() {
if(!$scope.title || $scope.title === '') { return; }
if(!$scope.age || $scope.age === '') { return; }
patients.create({
name: $scope.title,
age: $scope.age,
})
$scope.title = '';
$scope.age = '';
};
object.delete = function(patient)
}])
.controller('detailsCtrl', [
'$scope',
'$stateParams',
'patients',
function($scope, $stateParams, patients){
$scope.patient = patients.patients[$stateParams.id];
}])
;
Home.ejs
<div class="container">
<div clas="row">
<div style="width: 200px; margin-top: 100px">
<select ng-model="selectedItem" ng-options="patients.name for patients in patients" class="pull-left form-control" name="Vælg"></select>
</div>
<div class="viewbox pull-right">
<h3>Patient: {{selectedItem.name}}</h3>
<p>Age: {{selectedItem.age}} </p>
<p>index: {{patients.indexOf(selectedItem)}}</p>
<button>Rediger</button>
<button ng-click="deleteItem(patients.indexOf(selectedItem))">Delete</button>
</div>
</div>
<div class="row" class="pull-left">
<div style="width: 200px; margin-top: 100px">
<form role="form" class="form-group" ng-submit="addPost()">
<input class="form-control" type="text" ng-model="title" />
<input class="form-control" type="text" ng-model="age" />
<button type="submit">Add</button>
</form>
</form>
</div>
</div>
</div>
index.js (route get and post)
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
var mongoose = require('mongoose');
var Patient = mongoose.model('Patient');
router.get('/patients', function(req, res, next) {
Patient.find(function(err, patients){
if(err){ return next(err); }
res.json(patients);
});
});
router.post('/patients', function(req, res, next) {
var patient = new Patient(req.body);
patient.save(function(err, post){
if(err){ return next(err); }
res.json(patient);
});
});
router.delete('/patients/:patient', function(req, res, next) {
req.patient.remove(function(err, patient){
if (err) { return next(err); }
res.json(patient);
});
});
router.param('patient', function(req, res, next, id) {
var query = Patient.findById(id);
query.exec(function (err, patient){
if (err) { return next(err); }
if (!post) { return next(new Error('can\'t find patient')); }
req.patient = patient;
return next();
});
});
router.get('/details/:patient', function(req, res) {
res.json(req.patient);
});
module.exports = router;
I suspect the answer is quite straightforward, given the other code, but maybe not?
Thanks.
According to your function that you are passing to the router.delete in your index.js file:
router.delete('/patients/:patient', function(req, res, next) {
req.patient.remove(function(err, patient){
if (err) { return next(err); }
res.json(patient);
});
});
You'll have to append the patients.id to the url when using the delete verb with the $http service. So you can add a delete method to the object in your patients factory:
.factory('patients', ['$http', function($http){
var object = {
patients: []
};
object.getAll = function() {
return $http.get('/patients').success(function(data) {
angular.copy(data, object.patients);
});
}
object.create = function(patient) {
return $http.post('/patients', patient).success(function(data){
object.patients.push(data);
});
}
//add patient id to the url
object.delete = function(patient) {
return $http.delete('/patients/',patient).success(function(data){
console.log(data);
});
}
}
};
return object;
}])
object.delete = function(patient) {
return $http.delete('/patients', patient).success(function(data){
for(var i = 0; i < object.patients.length; i++) {
if(object.patients[i].id == patient.id) {
object.patients.splice(i, 1);
}
});
Something like that maybe, hard to say without knowing the response. If the response (data) is the deleted patient then you can use "if(object.patients[i].id == data.id)" instead

Angular (1.4) Design and layout

I couldn't think of a good title for this as I'm not 100% sure what to ask for plus this might be tricky without posting my whole application.
I'm new to angular, and want to create a site for holding recipes.
I'm using MVC and Web API and I have the basics together.
I want to make the recipe editing process a good user experience so want the user to be able to add an ingredient and edit and save it all without leaving the page.
At the moment I have an angular module and controller for the recipe editing, I want to have a sub-application to allow them to add and edit ingredients. I don't want them to have to navigate away.
What I need to know is a) the terminology. I'm thinking of the ingredient editor as a sub app, but is it just a sub controller?
and b) what is the pattern called? Master Detail View?
The following is my current "EditRecipeViewModel"
At the moment my newAddIngredient pushes an ingredient object into an array, this can then be edited and saved with saveNewIngredient, however this is nasty and doesn't work for editing the ingredient. I think I want the functionality for editing an ingredient encapsulated in a separate something, but is that a module a controller or what? maybe I'm trying to run before I can walk??
//edit recipe
//add ingredients
//add picture(s)
//confirm
//save
var editRecipeModule = angular.module('editRecipe', ['common'])
.config(function ($routeProvider, $locationProvider) {
$routeProvider.when(CookBook.rootPath + 'recipe/edit', { templateUrl: CookBook.rootPath + 'Templates/EditRecipe/EditRecipe.html', controller: 'EditRecipeViewModel' });
$routeProvider.when(CookBook.rootPath + 'recipe/edit/confirm', { templateUrl: CookBook.rootPath + 'Templates/EditRecipe/ConfirmRecipe.html', controller: 'ConfirmRecipeViewModel' });
$routeProvider.otherwise({ redirectTo: CookBook.rootPath + 'recipe/edit/' });
$locationProvider.html5Mode({ enabled: true });
});
editRecipeModule.controller("EditRecipeViewModel", function ($scope, $window, viewModelHelper, validator) {
$scope.viewModelHelper = viewModelHelper;
$scope.editRecipeModel = new CookBook.EditRecipeModel();
$scope.newIngredient = new CookBook.IngredientModel();
$scope.ingredients = [];
var editRecipeRules = [];
var setupRules = function () {
editRecipeRules.push(new validator.PropertyRule("Name", {
required: { message: "Recipe name is required." }
}));
editRecipeRules.push(new validator.PropertyRule("Description", {
required: { message: "Please add a description." }
}));
editRecipeRules.push(new validator.PropertyRule("Method", {
required: { message: "All good recipes need a method." }
}));
}
var editIngredientRules = [];
var setupIngredientRules = function () {
editIngredientRules.push(new validator.PropertyRule("Name", {
required: { message: "Name is required" }
}));
editIngredientRules.push(new validator.PropertyRule("Amount", {
required: { message: "Amount is required" }
}));
editIngredientRules.push(new validator.PropertyRule("Unit", {
required: { message: "Unit is required" }
}));
}
$scope.confirm = function () {
validator.ValidateModel($scope.editRecipeModel, editRecipeRules);
viewModelHelper.modelIsValid = $scope.editRecipeModel.isValid;
viewModelHelper.modelErrors = $scope.editRecipeModel.errors;
if (viewModelHelper.modelIsValid) {
//save via web api
}
}
$scope.newAddIngredient = function () {
$scope.newIngredient = new CookBook.IngredientModel();
$scope.newIngredient.EditMode = true;
$scope.ingredients.push($scope.newIngredient);
}
$scope.saveNewIngredient = function() {
$scope.newIngredient.EditMode = false;
}
$scope.addIngredient = function () {
validator.ValidateModel($scope.newIngredient, editIngredientRules);
viewModelHelper.modelIsValid = $scope.newIngredient.isValid;
viewModelHelper.modelErrors = $scope.newIngredient.errors;
if (viewModelHelper.modelIsValid) {
$scope.ingredients.push($scope.newIngredient);
$scope.newIngredient = new CookBook.IngredientModel();
}
}
setupRules();
setupIngredientRules();
});
editRecipeModule.controller("ConfirmRecipeViewModel", function ($scope, $window, viewModelHelper) {
$scope.viewModelHelper = viewModelHelper;
//$scope.editRecipeModel = new CookBook.editRecipeModel();
});
Having done some more reading
I now know the answer is to use an additional controller to deal with that small part of the UI.
The key part i was missing was to put the controller to use in the HTML.
ng-controller="IngredientViewModel as ivm"
My HTML.
<div class="row" ng-controller="IngredientViewModel as ivm" ng-repeat="ingredient in ingredients">
<span class="col-lg-3 col-md-3 col-sm-3 col-xs-3">
<input ng-show="ivm.ingredient.EditMode" ng-model="ivm.ingredient.Name" ng-change="newIngredientChanged()"></input>
<span ng-hide="ivm.ingredient.EditMode">{{ivm.ingredient.Name}}</span>
</span>
<span class="col-lg-3 col-md-3 col-sm-3 col-xs-3">
<input ng-show="ivm.ingredient.EditMode" ng-model="ivm.ingredient.Amount"></input>
<span ng-hide="ivm.ingredient.EditMode">{{ivm.ingredient.Amount}}</span>
</span>
<span class="col-lg-3 col-md-3 col-sm-3 col-xs-3">
<input ng-show="ivm.ingredient.EditMode" ng-model="ivm.ingredient.Unit"></input>
<span ng-hide="ivm.ingredient.EditMode">{{ivm.ingredient.Unit}}</span>
</span>
<span class="col-lg-3 col-md-3 col-sm-3 col-xs-3">
<button ng-show="ivm.ingredient.EditMode" ng-click="ivm.save()">OK</button>
<button ng-hide="ivm.ingredient.EditMode" ng-click="ivm.edit()">Edit</button>
</span>
</div>
The controller..
editRecipeModule.controller("IngredientViewModel", function ($scope, $window, viewModelHelper, validator) {
var local = this;
local.ingredient = new CookBook.IngredientModel();
local.ingredient.Name = "bob";
local.ingredient.Amount = "100";
local.ingredient.Unit = "g";
local.ingredient.EditMode = true;
var editRules = [];
var setupRules = function () {
editRules.push(new validator.PropertyRule("Name", {
required: { message: "Name is required" }
}));
editRules.push(new validator.PropertyRule("Amount", {
required: { message: "Amount is required" }
}));
editRules.push(new validator.PropertyRule("Unit", {
required: { message: "Unit is required" }
}));
}
local.save = function () {
validator.ValidateModel(local.ingredient, editRules);
viewModelHelper.modelIsValid = local.ingredient.isValid;
viewModelHelper.modelErrors = local.ingredient.errors;
if (viewModelHelper.modelIsValid) {
local.ingredient.EditMode = false;
}
}
local.edit = function () {
local.ingredient.EditMode = true;
}
setupRules();
});

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 model not updated when switching between users at login

I need the following functionality: When a user goes to the login form, the browser should auto fill the username and password.
My implementation works (on FF and Chrome) but, there is this bug (not consistent) where the model data does not get updated correctly when switching between users. This means that I log in with user ONE, then log out, and enter the credentials for user TWO, but after I click the login button, I'm still logged in with user ONE.
The login form looks like this:
<form class="form-horizontal" ng-submit="login(credentials)">
<fieldset>
<div class="form-group" ng-class="{'has-error' : validationErrors.email}">
<div class="btn-icon-lined btn-icon-round btn-icon-sm btn-default-light">
<span class="glyphicon glyphicon-envelope"></span>
</div>
<input type="email" name="email" autocomplete="on" ng-model="credentials.email" class="form-control input-lg input-round text-center" placeholder="Email" >
<span class="help-block" ng-if="validationErrors.email">{{ validationErrors.email.0 }}</span>
</div>
<div class="form-group" ng-class="{'has-error' : validationErrors.password}">
<div class="btn-icon-lined btn-icon-round btn-icon-sm btn-default-light">
<span class="glyphicon glyphicon-lock"></span>
</div>
<input type="password" name="password" autocomplete="on" ng-model="credentials.password" class="form-control input-lg input-round text-center" placeholder="Password" >
<span class="help-block" ng-if="validationErrors.password">{{ validationErrors.password.0 }}</span>
</div>
<div class="form-group">
<input type="submit" value="Sign in" class="btn btn-primary btn-lg btn-round btn-block text-center">
</div>
</fieldset>
</form>
The login controller contains something like:
// Login Controller
app.controller( 'LoginCtrl', ['$rootScope','$scope', '$state', 'AppUser', 'Auth',
function($rootScope, $scope, $state, AppUser, Auth){
console.log("LoginCtrl");
$scope.credentials = {
"email" : "",
"password": ""
};
$scope.redirectAfterLogin = function() {
// set user data
AppUser.setUserData($scope.user);
...
}
// Login attempt handler
$scope.login = function(data) {
data = {'user':data};
Auth.login(data,
function(response) {
$scope.user = response.data;
...
},function(response){
$scope.validationErrors = {
"email" : [],
"password": []
};
...
}
);
};
}]);
Logout:
// logout
$scope.logout = function() {
// remove attempted URL, if any
AppUser.removeAttemptUrl();
data = {'user':
{
'email': $scope.user.email
}
};
Auth.logout(data,
function(){
AppUser.unSetUserData($scope.user); // se method below
$state.go(ApplicationState.LOGIN);
},
function(){
console.log("Logout failed");
});
}
angular.module('app.service').factory('AppUser', [
'$window', '$rootScope', 'LocalStorage', 'appConfig', '$injector', '$location',
function($window, $rootScope, localStorage, appConfig, $injector, $location){
// Redirect to the original requested page after login
var redirectToUrlAfterLogin = { url: '' };
var userKey = "AppUser";
var userData = {};
angular.element($window).on('storage', function(event) {
if (event.key === userKey) {
$rootScope.$apply();
}
});
return {
/**
* Redirect to the original requested page after login
* - we need to be able to save the intended URL, request it, remove it and redirect to it
*/
saveAttemptUrl: function() {
if ($location.path().toLowerCase() != ApplicationState.LOGIN) {
redirectToUrlAfterLogin.url = $location.path();
}
else {
redirectToUrlAfterLogin.url = '/';
}
},
getAttemptedUrl: function() {
return redirectToUrlAfterLogin.url;
},
removeAttemptUrl: function() {
// re-initialize URL
redirectToUrlAfterLogin = { url: '' };
},
redirectToAttemptedUrl: function() {
$location.path(redirectToUrlAfterLogin.url);
},
/**
* Returns the current user's state
* #returns {boolean}
*/
isAuthenticated: function() {
userData = JSON.parse(localStorage.get(userKey) || '{}').userData;
if (!this._isSessionExpired()) {
if (userData !== undefined){
return !!(userData.id !== null && userData.email);
}
else{
return false;
}
}
else{
if (userData !== undefined){
var data = {
'user':{
'email': userData.email
}
}
// we use $injector to avoid Circular Dependency which is thrown by injecting the $api service
$injector.invoke(['$api', function($api){
$api.auth.logout(data).success(function(result){
userData = {};
localStorage.remove(userKey);
});
}]);
return false;
}
}
},
getUserData: function() {
return userData;
},
setUserData: function(data) {
userData = data;
localStorage.set(userKey, JSON.stringify({
userData: data,
stamp: Date.now()
}));
},
unSetUserData: function() {
userData = {};
localStorage.remove(userKey);
},
_isSessionExpired: function() {
var session = JSON.parse(localStorage.get(userKey) || '{}');
return (Date.now() - (session.stamp || 0)) > appConfig.sessionTimeout;
},
userData : userData
};
}] );
Any ideas on why this is happening?
After you logout check the localStorage with the browser inspector.
Probably you will find some variable that you didn't clear.
So just clear the storage and it should be fine.
To clear the storage use:
localStorage.clear();
One additional problem it could be you didn't clean the $rootScope if you didn't refresh all the data are still in there.
Is this the problem? userKey doesn't seem to be defined in the code you've showed.
// add userKey param
unSetUserData: function(userKey) {
userData = {};
localStorage.remove(userKey);
},

Resources