I am creating a simple AngularJS SPA using an API to load data into Mongoose.
My app just adds, displays and edits a list of members. It works when I just store the members in an array in my factory service but now I want to change it to hook up to Mongoose via an API.
Factory
app.factory('SimpleFactory', ['$http', function($http){
var factory = {};
var members = $http.get('/api/members')
factory.getMembers = function ()
{
return members = $http.get('/api/members');
}
factory.getMember = function (index) {
if (index >=0 && index < members.length ) {
return members[index] = $http.get('/api/members/' + member_id )
}
return undefined
}
factory.addMember = function(member) {
return $http.post('/api/members',member)
}
factory.updateMember = function(index,member) {
$http.put('/api/members/' + member_id, member)
}
return factory;
}])
Controller
app.controller('MembersController', ['$scope','SimpleFactory',
function ($scope,SimpleFactory) {
SimpleFactory.getMembers()
.success(function(members) {
$scope.members = members;
});
$scope.addMember = function()
{
var member = {
name: $scope.newMember.name,
address: $scope.newMember.address,
age : $scope.newMember.age,
level : $scope.newMember.level,
swimmer : $scope.newMember.swimmer,
email : $scope.newMember.email,
regdate : $scope.newMember.regdate,
}
SimpleFactory.addMember(member)
.success(function(added_member)
{
$scope.members.push(added_member);
$scope.newMember = { }
} );
}
}])
But I am not sure how to change my controller for updating a member, it is coded as follows to pick up the members from an array in my factory setting, how do I code it to pick up members from Mongoose via API:
app.controller('MemberDetailController', ['$scope', '$location', '$routeParams', 'SimpleFactory',
function($scope, $location, $routeParams, SimpleFactory) {
$scope.member = {
index: $routeParams.member_index,
detail: SimpleFactory.getMember($routeParams.member_index)
}
$scope.updateMember = function() {
SimpleFactory.updateMember($scope.member.index,
$scope.member.detail)
$location.path('/members')
}
}
])
Can anyone help, its not a complicated app but I'm only learning and I am stuck here!
Thanks
You $scope.member object should set after getMember promise success.
Code
SimpleFactory.getMember($routeParams.member_index).then(function(data){
$scope.member = {
index : $routeParams.member_index,
detail : data.user
};
});
Apart from that you need to make sure getMember method should always return a promise while index is 0
factory.getMember = function (index) {
var deferred = $q.defer();
if (index >=0 && index < members.length ) {
return members[index] = $http.get('/api/members/' + member_id )
}
deferred.resolve;
}
Update
For calling update method you need to do change service first which would return a promise
factory.updateMember = function(index,member) {
return $http.put('/api/members/' + member_id, member)
}
Then call factory.updateMember resolve that promise and then do $location.path
$scope.updateMember = function() {
SimpleFactory.updateMember($scope.member.index, $scope.member.detail)
.then(function(data) {
$location.path('/members')
});
};
Related
I've been trying to code up a search engine using angular js, but I can't copy one array to another. When I initiate the the code (in the service.FoundItems in the q.all then function) new array(foundArray) shows up as an empty array. I searched up how to copy one array to another and tried that method as you can see, but it isn't working. Please help, here is the code, and thank you.
P.S. if you need the html please tell me.
(function () {
'use strict';
angular.module('narrowDownMenuApp', [])
.controller('narrowItDownController', narrowItDownController)
.service('MenuSearchService', MenuSearchService)
.directive('searchResult', searchResultDirective);
function searchResultDirective() {
var ddo = {
templateUrl: 'searchResult.html',
scope: {
items: '<'
},
};
return ddo
}
narrowItDownController.$inject = ['MenuSearchService'];
function narrowItDownController(MenuSearchService) {
var menu = this;
menu.input = "";
menu.displayResult = [];
menu.searchX = function(name) {
menu.displayResult = MenuSearchService.FoundItems(menu.input, name);
console.log(menu.displayResult);
};
}
MenuSearchService.$inject = ['$http', '$q'];
function MenuSearchService($http, $q) {
var service = this;
service.getMatchedMenuItems = function(name, searchTerm) {
var deferred = $q.defer();
var foundItems = [];
var result = $http({
method: "GET",
url: ('https://davids-restaurant.herokuapp.com/menu_items.json'),
params: {
category: name
}
}).then(function (result) {
var items = result.data;
for (var i = 0; i < items.menu_items.length; i++) {
if (searchTerm === ""){
deferred.reject("Please enter search term");
i = items.menu_items.length;
}
else if (items.menu_items[i].name.toLowerCase().indexOf(searchTerm.toLowerCase()) ==! -1){
foundItems.push(items.menu_items[i].name)
deferred.resolve(foundItems);
}else {
console.log("doesn't match search");
}
}
});
return deferred.promise;
};
service.FoundItems = function (searchTerm, name) {
var searchResult = service.getMatchedMenuItems(name, searchTerm);
var foundArray = [];
$q.all([searchResult])
.then(function (foundItems) {
foundArray = foundItems[0].slice(0);
foundArray.reverse();
})
.catch(function (errorResponse) {
foundArray.push(errorResponse);
});
console.log(foundArray);
return foundArray;
};
};
})();
If the goal of the service.FoundItems function is to return a reference to an array that is later populated with results from the server, use angular.copy to copy the new array from the server to the existing array:
service.FoundItems = function (searchTerm, name) {
var foundArray = [];
var searchPromise = service.getMatchedMenuItems(name, searchTerm);
foundArray.$promise = searchPromise
.then(function (foundItems) {
angular.copy(foundItems, foundArray);
foundArray.reverse();
return foundArray;
})
.catch(function (errorResponse) {
return $q.reject(errorResponse);
})
.finally(function() {
console.log(foundArray);
});
return foundArray;
};
I recommend that the promise be attached to the array reference as a property named $promise so that it can be used to chain functions that depend on results from the server.
Frankly I don't recommend designing services that return array references that are later populated with results. If you insist on designing it that way, this is how it is done.
I tried the $promise thing that you recommended. I was wondering how you would get the value from it ie the array.
In the controller, use the .then method of the $promise to see the final value of the array:
narrowItDownController.$inject = ['MenuSearchService'];
function narrowItDownController(MenuSearchService) {
var menu = this;
menu.input = "";
menu.displayResult = [];
menu.searchX = function(name) {
menu.displayResult = MenuSearchService.FoundItems(menu.input, name);
̶c̶o̶n̶s̶o̶l̶e̶.̶l̶o̶g̶(̶m̶e̶n̶u̶.̶d̶i̶s̶p̶l̶a̶y̶R̶e̶s̶u̶l̶t̶)̶;̶
menu.displayResult.$promise
.then(function(foundArray) {
console.log(foundArray);
console.log(menu.displayResult);
}).catch(function(errorResponse) {
console.log("ERROR");
console.log(errorResponse);
});
};
}
To see the final result, the console.log needs to be moved inside the .then block of the promise.
Titus is right. The function always immediately returns the initial value of foundArray which is an empty array. The promise is executed asynchronously so by the time you are trying to change foundArray it is too late. You need to return the promise itself and then using .then() to retrieve the value just like you are currently doing inside the method.
From just quickly looking at your code I think you made have a simple error in there. Are you sure you want
foundArray = foundItems[0].slice(0);
instead of
foundArray = foundItems.slice(0);
I am trying to consume my spring rest service using angularjs client following this link
Create,update and read parts are working. When I try to delete, its showing this error.
Error: [$resource:badcfg] Error in resource configuration for action
get. Expected response to contain an object but got an array
(Request: GET http://localhost:8080/SpringRestExample/employee)
Why i am getting GET request in DELETE method?
employee_service.js
'use strict';
App.factory('Employee', ['$resource', function ($resource) {
return $resource(
'http://localhost:8080/SpringRestExample/employee/:id',
{id: '#employeeId'},
{
update: {
method: 'PUT'
}
}
);
}]);
employee_controller.js
'use strict';
App.controller('EmployeeController', ['$scope', 'Employee', function($scope, Employee) {
var self = this;
self.employee= new Employee();
self.employees=[];
self.fetchAllEmployees = function(){
self.employees = Employee.query();
};
self.createEmployee = function(){
self.employee.$save(function(){
self.fetchAllEmployees();
});
};
self.updateEmployee = function(){
self.employee.$update(function(){
self.fetchAllEmployees();
});
};
self.deleteEmployee = function(identity){
var employee = Employee.get({employeeId:identity}, function() {
employee.$delete(function(){
console.log('Deleting employee with id ', identity);
self.fetchAllEmployees();
});
});
};
self.fetchAllEmployees();
self.submit = function() {
if(self.employee.employeeId==null){
console.log('Saving New Employee', self.employee);
self.createEmployee();
}else{
console.log('Updating employee with id ', self.employee.employeeId);
self.updateEmployee();
console.log('Employee updated with id ', self.employee.employeeId);
}
self.reset();
};
self.edit = function(employeeId){
console.log('id to be edited', employeeId);
for(var i = 0; i < self.employees.length; i++){
if(self.employees[i].employeeId === employeeId) {
self.employee = angular.copy(self.employees[i]);
break;
}
}
};
self.remove = function(employeeId){
console.log('id to be deleted', employeeId);
if(self.employee.employeeId === employeeId) {//If it is the one shown on screen, reset screen
self.reset();
}
self.deleteEmployee(employeeId);
};
self.reset = function(){
self.employee= new Employee();
$scope.myForm.$setPristine(); //reset Form
};
}]);
Your issue could be when you call Employee.get({employeeId:identity}, ...) prior to deleting the employee. This will load the employee before deletion and it will do a GET request on 'http://localhost:8080/SpringRestExample/employee/:id'.
For this query to work properly, you need to provide id, which you haven't done, so it might just be leaving out that part of the URL. You provided employeeId, which is only used for mapping the id parameter to the Employee objects. Try replacing the query above with {id: identity}.
I'm attempting to learn the MEAN stack and learning to use the $http service.
I currently have a global check in place that is suppose to update my Sprints model, which looks like:
var SprintSchema = new Schema({
tasks: [{
type: String,
ref: 'Task'
}],
name: {
type: String
},
start: {
type: Date
},
end: {
type: Date
},
active: Boolean
});
The following controller should update the Sprint model when requested, and when I console.log the variable in my success function, it looks like what I would expect it to pass but it doesn't actually end up updating my model. Below is my code and an example of the console.log.
'use strict';
angular.module('inBucktApp')
.service('VariableService', function () {
// AngularJS will instantiate a singleton by calling "new" on this function
var ticketId = 'noTicketYet';
var ticketAssigneeName = 'noTicketNameYet';
return {
getPropertyId: function () {
return ticketId;
},
getPropertyName: function () {
return ticketAssigneeName;
}
,
setProperty: function(value, valueName) {
ticketId = value;
ticketAssigneeName = valueName;
}
};
})
.run(['$rootScope', '$http', 'socket', 'VariableService', function($rootScope, $http, socket, VariableService) {
$rootScope.sprintStart;
$http.get('/api/sprints').success(function(sprints) {
$rootScope.sprints = sprints.pop();
$rootScope.sprintStart = new Date($rootScope.sprints.start);
$rootScope.sprintEnd = new Date($rootScope.sprints.end);
socket.syncUpdates('sprints', $rootScope.sprints);
$http.get('/api/tasks').success(function(task) {
$rootScope.task = task;
$rootScope.taskPop = _.flatten($rootScope.task);
$rootScope.taskPopAgain = $rootScope.task.pop();
socket.syncUpdates('task', $rootScope.task);
$rootScope.updateTicket = function(){
//Goes through the entire array and check each element based on critera.
var taskIdsToAdd = [];
for(var i = 0; i < $rootScope.taskPop.length; i++){
var taskFind = $rootScope.taskPop[i];
//Logic if ticket is not in the sprint
if ((new Date(taskFind.start) >= $rootScope.sprintStart) && (new Date(taskFind.start) <= $rootScope.sprintEnd)){
taskFind.sprint = true;
taskIdsToAdd.push(taskFind._id);
$rootScope.sprints.tasks.push(taskFind._id);
$http.put("/api/tasks/"+taskFind._id,taskFind).success(function(task){
console.log('Logic 1 Ran!');
console.log($rootScope.sprintStart);
// socket.syncUpdates('taskPopAgain', taskFindPopAgain);
});
$http.put("/api/sprints/"+$rootScope.sprints._id,$rootScope.sprints).success(function(sprints){
console.log('Logic 2 Ran!');
console.log($rootScope.sprintStart);
console.log(sprints)
});
console.log($rootScope.sprints);
} else{
console.log('this doesnt work first');
};
//Logic if ticket is not in the sprint
if (new Date(taskFind.start) < $rootScope.sprintStart || new Date(taskFind.start) > $rootScope.sprintEnd){
taskFind.sprint = false;
$http.put("/api/tasks/"+taskFind._id,taskFind).success(function(task){
console.log(task);
});
}else{
console.log('this doesnt work');
};
}
};
$rootScope.updateTicket();
});
});
}]);
Console.Log of console.log(sprints)
Anyone have any idea what I'm doing incorrect here?
Thanks for the help guys.
I am struggling with some Javascript that I am currently working on. So I have a simple web application and the following is the AngularJS stuff:
app.filter('startFrom', function () {
return function (input, start) {
if (input) {
start = +start;
return input.slice(start);
}
return [];
};
});
app.controller('MainCtrl', ['$scope', 'filterFilter', function ($scope, filterFilter) {
$scope.items = ["name 1", "name 2", "name 3"
];
$scope.addLink = function () {
$scope.errortext = "";
if (!$scope.newItem) {return;}
if ($scope.items.indexOf($scope.newItem) == -1) {
$scope.items.push($scope.newItem);
$scope.errortext = "submitted";
} else {
$scope.errortext = " in list";
}
};
So I have these and I there is html side of it which displays the list of items. Users have options to add and delete these items from items array.
Question. How do I make sure that when user added or deleted items from the array can still see the edited list after reloading the page? Can someone suggest a way of dealing with it? Would it be possible to store in cookies and after each add/delete action update them, if so how?
thanks
UPDATE:
So I changed the script but it still does not seem to be working.
var app = angular.module('App', ['ui.bootstrap']);
app.filter('startFrom', function () {
return function (input, start) {
if (input) {
start = +start;
return input.slice(start);
}
return [];
};
});
app.factory('ItemsService', ['$window', function ($window) {
var storageKey = 'items',
_sessionStorage = $window.sessionStorage;
return {
// Returns stored items array if available or return undefined
getItems: function () {
var itemsStr = _sessionStorage.getItem(storageKey);
if (itemsStr) {
return angular.fromJson(itemsStr);
}
},
// Adds the given item to the stored array and persists the array to sessionStorage
putItem: function (item) {
var itemsStr = _sessionStorage.getItem(storageKey),
items = [];
if (itemStr) {
items = angular.fromJson(itemsStr);
}
items.push(item);
_sessionStorage.setItem(storageKey, angular.toJson(items));
}
}
}]);
app.controller('MainCtrl', ['$scope', 'filterFilter', 'ItemsService', function ($scope, filterFilter, ItemsService) {
$scope.items = ItemsService.get($scope.items)
$scope.addLink = function () {
$scope.errortext = "";
if (!$scope.newItem) {
return;
}
if ($scope.items.indexOf($scope.newItem) == -1) {
$scope.items.push($scope.newItem);
$scope.errortext = "Submitted";
$scope.items = ItemsService.put($scope.items)
} else {
$scope.errortext = "Link in the list";
}
};
$scope.removeItem = function (item) {
$scope.items.splice($scope.items.indexOf(item), 1);
$scope.items = ItemsService.put($scope.items)
$scope.resetFilters;
};
}]);
Any help how to fix it and how to make sure that if user does not have any items it will use the default $scope.items = ["name 1", "name 2", "name 3"]; ?
You could create a simple get/set service that is using $cookies. It could be like this :
angular.module('myApp')
.factory('ItemsService', ['$cookies', function($cookies) {
var cookieName = 'items'
return {
get: function(defaults) {
return $cookies.get(cookieName).split(',') || defaults
},
put: function(items) {
var expireDate = new Date()
expireDate.setDate(expireDate.getDate() + 1);
$cookies.put(cookieName, items.join(','), { expires: expireDate } )
}
}
}]);
Include ItemsService in your controller and in the main function
$scope.items = ItemsService.get($scope.items)
to get the edited list stored in $cookies (if any), and save the list in addLink() by
ItemsService.put($scope.items)
I would like to extend #davidkonrad's answer here, by making his service to use sessionStorage. Since using sessionStorage is most suited for your usecase.
angular.module('myApp')
.factory('ItemsService', ['$window', function($window) {
var storageKey = 'items',
_sessionStorage = $window.sessionStorage;
return {
// Returns stored items array if available or return undefined
getItems: function() {
var itemsStr = _sessionStorage.getItem(storageKey);
if(itemsStr) {
return angular.fromJson(itemsStr);
}
return ['name1', 'name2', 'name3']; // return default value when there is nothing stored in sessionStore
},
// Adds the given item to the stored array and persists the array to sessionStorage
putItem: function(item) {
var itemsStr = _sessionStorage.getItem(storageKey),
items = [];
if(itemStr) {
items = angular.fromJson(itemsStr);
}
items.push(item);
_sessionStorage.setItem(storageKey, angular.toJson(items));
}
}
}]);
I have quite a few scenarios where I need clicks, etc. to trigger behavior in another place on the page (a one-way communication scenario). I now have a need for bi-directional communication, where stuff that happens in element A can modify specific properties in the scope behind element B and vice-versa. Thus far, I've been using $rootScope.$broadcast to facilitate this but it feels like overkill, and winds up creating boilerplate in both places:
$scope.$on('event-name', function(event, someArg) {
if(someArg === $scope.someProperty) return;
$scope.someProperty = someArg;
});
$scope.$watch('someProperty', function(newValue) {
$rootScope.$broadcast('event-name', newValue);
});
Is there a better way? I'd like to tie the two (or three, or N) scopes together via a service, but I don't see a way to do that without magic event names and boilerplate.
I haven't used this myself, but this post explains basically how I would do it. Here's the code which illustrates the idea:
(function() {
var mod = angular.module("App.services", []);
//register other services here...
/* pubsub - based on https://github.com/phiggins42/bloody-jquery-plugins/blob/master/pubsub.js*/
mod.factory('pubsub', function() {
var cache = {};
return {
publish: function(topic, args) {
cache[topic] && $.each(cache[topic], function() {
this.apply(null, args || []);
});
},
subscribe: function(topic, callback) {
if(!cache[topic]) {
cache[topic] = [];
}
cache[topic].push(callback);
return [topic, callback];
},
unsubscribe: function(handle) {
var t = handle[0];
cache[t] && d.each(cache[t], function(idx){
if(this == handle[1]){
cache[t].splice(idx, 1);
}
});
}
}
});
return mod;
})();
Note the memory leak though if controllers are "deleted" without unsubscribing.
I think you can try the following service,
'use strict';
angular.module('test')
.service('messageBus', function($q) {
var subscriptions = {};
var pendingQuestions = [];
this.subscribe = function(name) {
subscriptions[name].requestDefer = $q.defer();
return subscriptions[name].requestDefer.promise; //for outgoing notifications
}
this.unsubscribe = function(name) {
subscriptions[name].requestDefer.resolve();
subscriptions[name].requestDefer = null;
}
function publish(name, data) {
subscriptions[name].requestDefer.notify(data);
}
//name = whom shd answer ?
//code = what is the question ?
//details = details abt question.
this.request = function(name, code, details) {
var defered = null;
if (subscriptions[name].requestDefer) {
if (pendingQuestions[code]) {
//means this question is already been waiting for answer.
//hence return the same promise. A promise with multiple handler will get
//same data.
defered = pendingQuestions[code];
} else {
defered = $q.defer();
//this will be resolved by response method.
pendingQuestions[code] = defered;
//asking question to relevant controller
publish(name, {
code: code,
details: details
});
}
} else {
//means that one is not currently in hand shaked with service.
defered = $q.defer();
defered.resolve({
code: "not subscribed"
});
}
return defered.promise;
}
//data = code + details
//responder does not know the destination. This will be handled by the service using
//pendingQuestions[] array. or it is preemptive, so decide by code.
this.response = function(data) {
var defered = pendingQuestions[data.code];
if (defered) {
defered.resolve(data);
} else {
//means nobody requested for this.
handlePreemptiveNotifications(data);
}
}
function handlePreemptiveNotifications() {
switch (data.code) {
//handle them case by case
}
}
});
This can be used as a message bus in multi controller communication. It is making use of the angular notify() callback of promise API.All the participating controllers should subscribe the service as follows,
angular.module('test')
.controller('Controller1', function($scope, messageBus) {
var name = "controller1";
function load() {
var subscriber = messageBus.subscribe(name);
subscriber.then(null, null, function(data) {
handleRequestFromService(data);
});
}
function handleRequestFromService(data) {
//process according to data content
if (data.code == 1) {
data.count = 10;
messageBus.respond(data);
}
}
$scope.$on("$destroy", function(event) {
//before do any pending updates
messageBus.unsubscribe(name);
});
load();
});
angular.module('test')
.controller('Controller2', function($scope, messageBus) {
var name = "controller2";
function load() {
var subscriber = messageBus.subscribe(name);
subscriber.then(null, null, function(data) {
handleRequestFromService(data);
});
}
function handleRequestFromService(data) {
//process according to data content
}
$scope.getHorseCount = function() {
var promise = messageBus.request("controller1", 1, {});
promise.then(function(data) {
console.log(data.count);
});
}
$scope.$on("$destroy", function(event) {
//before do any pending updates
messageBus.unsubscribe(name);
});
load();
});