scope passed to service is not updating the view - angularjs

This is an app that I took over from another developer. I have a controller that calls a function inside a service and it passes in the $scope of the controller as a parameter. THis exact same thing is done with another function in the service.
THe problem is that only one of these services seem to update the view.
Here is my code (very condensed to keep it short, or a short as possible)
var KrisisEventsApp = angular.module('KrisisEventsApp', []);
KrisisEventsApp.filter('unsafe', function ($sce) { return $sce.trustAsHtml; });
//Controller for registration
KrisisEventsApp.controller('RegistrationCtrl',
['$scope', 'RegistrationService', '$timeout',
function ($scope, RegistrationService, $timeout) {
$scope.SaveRegistrantData =
function () {
//save user input
RegistrationService.SaveRegistrationForm($scope);
};
$scope.Products_Continue =
function () {
RegistrationService.ListItemsForCart($scope);
};
}
]
);
KrisisEventsApp.service('RegistrationService',
['$http', function ($http) {
var thisService = this;
//injects custom properties into registration form
thisService.SaveRegistrationForm = function ($scope) {
this.count = 0;
this._scope = $scope;
// if no product are found, go straight to payment page
this.count = Object.keys($scope.products).length;
// console.log(this.count);
$http({
method: "POST",
url: v_ModulePath + "/API/Registrants_WebAPI/RegisterUser",
dataType: 'text/plain',
data: data,
headers: {
'Content-Type': 'text/plain',
'ModuleId': v_servicesFramework.getModuleId(),
'TabId': v_servicesFramework.getTabId(),
'RequestVerificationToken': v_servicesFramework.getAntiForgeryValue()
}
})
.then(function (response) {
data = JSON.parse(JSON.parse(response.data))
this.config2 = {
method: "GET",
url: v_ModulePath + "/API/Registrants_WebAPI/ListItemsForCart?idregistrant=" + $("#hid_registrant_id").val() + "&idevent=" + v_EventID,
dataType: 'text/plain',
data: '',
headers: {
'Content-Type': 'text/plain',
'ModuleId': v_servicesFramework.getModuleId(),
'TabId': v_servicesFramework.getTabId(),
'RequestVerificationToken': v_servicesFramework.getAntiForgeryValue()
}
}
return $http.get(v_ModulePath + "/API/Registrants_WebAPI/ListItemsForCart?idregistrant=" + $("#hid_registrant_id").val() + "&idevent=" + v_EventID, this.config2);
})
.then(function (response) {
data = JSON.parse(JSON.parse(response.data));
$scope.Cart = data;
});
}
//list cart items
thisService.ListItemsForCart = function ($scope) {
$http(
{
method: "GET",
url: v_ModulePath + "/API/Registrants_WebAPI/ListItemsForCart?idregistrant=" + $("#hid_registrant_id").val() + "&idevent=" + v_EventID,
dataType: 'text/plain',
data: '',
headers: {
'Content-Type': 'text/plain',
'ModuleId': v_servicesFramework.getModuleId(),
'TabId': v_servicesFramework.getTabId(),
'RequestVerificationToken': v_servicesFramework.getAntiForgeryValue()
}
}).success(function (data) {
data = JSON.parse(JSON.parse(data));
$scope.Cart = data;
}).error(function (data) {
});
}
}
]
);
Here is the view (portion):
...
<a ng-click="SaveRegistrantData()" class="small button success" id="but_InputForm_MoveNext">Continue</a>
...
<a ng-click="Products_Continue()" class="small button success">Continue</a>
...
<tbody>
<tr ng-repeat="CartItem in Cart.Cart_Items_List">
<td>{{ CartItem.Item_Description }}</td>
<td class="text-right">{{ CartItem.Item_Fee }}</td>
</tr>
<tr>
<td>Total to Pay</td>
<td class="text-right">{{ Cart.CartTotal }}</td>
</tr>
</tbody>
You can see that the SaveRegistrationForm and ListItemsForCart functions are called from the controller, which are in turn initiated by button clicks from the view.
NOTE:
the following lines are the important ones in each function:
data = JSON.parse(JSON.parse(response.data));
$scope.Cart = data;
PROBLEM:
Only the ListItemsForCart updates the view, when I run the SaveRegistrationForm function the view does not update.
I have used fiddler to examine the web api calls and both functions successfully return the same data.
I have also used the chrome console to confirm that the $scope.Cart object does in fact get assigned the right data.
QUESTION:
Can someone help me figure out why my view is not updating?

Okay, I inherited this app from another developer. They defined the controller with the same name twice in the html view. arrrrg. So the second set of data that was not updating was in the second instance of the controller.
Once I moved a single controller instance to wrap the whole view the problems all went away.
Thanks to all who helped. You would not have been able to tell this from the abbreviated view code I posted. (I also have many other things to update, like .success to then catch and like Satpal said: just returning promises from my services etc...)

Related

AngularJS pushing/updating new data (likes/dislikes) to API using PUT

I am trying to create an app that counts likes for beer! This would updates the API the beers are stored on and in turn update the number of likes on the API and angularJS view using the PUT method. I am able to get the view to work correctly increasing every time the like is clicked. I am unsure why my PUT method continues to return a 404 and will not update the API. please see code below for my put method. I have also included my JS and HTML for this. I feel like I am close but cannot figure out how to get the "likes" to update on the API. Thank you in advance!! I think i am passing incorrect data to the PUT method.
HTML:
<div ng-app="beerApp" ng-controller="BeerController" class="jumbotron">
<div class="all-beer">
<div class="single-beer" ng-repeat="beer in allBeer">
<div>{{beer.name}}</div>
<div>{{beer.likes}}</div>
<button ng-click="decrease(beer)">X</button>
<button ng-click="increase(beer)">\3</button>
</div>
</div>
</div>
JS:
angular.module('beerApp', []).controller('BeerController', function($scope, $http) {
$scope.allBeer = [];
$scope.beerSum = function() {
$http({
method: 'GET',
url: /api/beers
}).
then( function(response) {
if(typeof response === 'object') {
var dataArr = response.data;
for (var i = 0; i < dataArr.length; i++) {
var beer = dataArr[i];
$scope.allBeer.push(beer);
}
} else {
return;
}
}, function(error) {
console.log('i am an error', error);
})
};
$scope.beerSum();
$scope.increase = function(beer){
var newLikes = beer.likes++;
$http({
method: 'PUT',
url: '/api/beer/',
data: JSON.stringify($scope.allBeer.likes),
}).then(function successCallback(response) {
console.log("Updated!");
}, function errorCallback(response) {
console.log("not updated")
});
};
First things first you are missing some syntax for the http api's. Secondly you are calling a property on an array that doesn't exist. Thirdly your api won't work because of the logic that you have. You have an array of beers and you want to increase the likes on a single beer. Create a method on the server that accepts a beer, the server will take that beer and increase it's likes by 1, then save to the database or whatever.
Depending on the server you are using you have two options.
You can define a command simply at /api/beers and configure the server to accept an object and use that objects id for the server update. If this is the case I recommend creating this endpoint, /api/beers/update and make it a POST, and pass it the object, then within this command do all your update logic.
Or for example the Microsoft Web Api the default put (update) endpoint looks like so, public void Update(int id, object data){} with a url of /api/beers/{id}. To use this method you need to change the code for the updateLikes method I wrote.
See Below:
$scope.updateLikes = function(beer, likeCount){
beer.likes+= likeCount;
$http({
method: 'PUT',
url: '/api/beer/' + beer.id,
data: JSON.stringify(beer),
}).then(function successCallback(response) {
console.log("Updated!");
//Trigger reload of data
$scope.beerSum();
}, function errorCallback(response) {
console.log("not updated")
});
};
Extra Help
If you are still having trouble and are working in a GitHub environment I would gladly help you with your code more directly. Other than that the answer I have posted answer's your question, and does so in what I believe to be good coding practices for AngularJS. With one minor exception there code be a changes to the line that reads, beer.likes += likeCount because this also updates the original beer object. I suppose that is preference, but please contact me if you need more help.
JS:
angular.module('beerApp', []).controller('BeerController', function($scope, $http) {
$scope.allBeer = [];
$scope.beerSum = function() {
$http({
method: 'GET',
url: '/api/beers' //<-- Added string opening and closing tags
}).
then( function(response) {
if(typeof response === 'object') {
var dataArr = response.data;
for (var i = 0; i < dataArr.length; i++) {
var beer = dataArr[i];
$scope.allBeer.push(beer);
}
} else {
return;
}
}, function(error) {
console.log('i am an error', error);
})
};
$scope.beerSum();
$scope.increase = function(beer){
var newLikes = beer.likes++;
//Your code
$http({
method: 'PUT',
url: '/api/beer/', //<-- closing
data: JSON.stringify($scope.allBeer.likes), //<-- Where does likes come from? $scope.allBeer is an array of beer but the array itself doesn't have a property called likes.
}).then(function successCallback(response) {
console.log("Updated!");
}, function errorCallback(response) {
console.log("not updated")
});
//End your code
//My Code
beer.likes+=1; //<-- My bad I fixed this.
$http({
method: 'PUT',
url: '/api/beer/', //<-- closing
data: JSON.stringify(beer), //<-- The object you passed into the function
}).then(function successCallback(response) {
console.log("Updated!");
}, function errorCallback(response) {
console.log("not updated")
});
//End my code
};
Possible problems
Your api doesn't work with put, in which case this question isn't the correct one.
Something else is internally wrong with your program, but from this point on I think you're looking at something wrong with your api, whatever that may be.
angular.module('beerApp', []).controller('BeerController', function($scope, $http) {
$scope.allBeer = [];
$scope.beerSum = function() {
$scope.allBeer.push({
"name": "Miller Lite",
"likes": 0
});
$http({
method: 'GET',
url: '/api/beers' //<-- Added string opening and closing tags
}).
then( function(response) {
if(typeof response === 'object') {
var dataArr = response.data;
for (var i = 0; i < dataArr.length; i++) {
var beer = dataArr[i];
$scope.allBeer.push(beer);
}
}
}, function(error) {
console.log('i am an error', error);
})
};
$scope.beerSum();
$scope.updateLikes = function(beer, likeCount){
beer.likes+= likeCount;
$http({
method: 'PUT',
url: '/api/beer/',
data: JSON.stringify(beer),
}).then(function successCallback(response) {
console.log("Updated!");
//Trigger reload of data
$scope.beerSum();
}, function errorCallback(response) {
console.log("not updated")
});
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.6.2" data-semver="1.6.2" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div ng-app="beerApp" ng-controller="BeerController" class="jumbotron">
<h1>Beers on Tap</h1>
<div class="all-beer">
<div class="single-beer" ng-repeat="beer in allBeer">
<div>{{beer.name}}</div>
<div>{{beer.likes}}</div>
<button ng-click="updateLikes(beer, -1)">Down Vote</button>
<button ng-click="updateLikes(beer, 1)">Up Vote</button>
</div>
</div>
</div>
</body>
</html>

AngularJS: How to store the data from the service so I do not need to query again and again

I have built a service to pass a variables among controllers. I have got in a variable (vm.organizations) all organizations and in vm.contacts all contacts in the Database in the listview.Html (under contactController).
//listview.html
<html ng-app="My-app">
<body ng-controller="contactsController">
<table class="table table-striped" >
<thead>
<tr>
<th>Name </th>
<th>Company</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="contact in vm.contacts>
<td><a ui-sref="contact({Id: contact.id})" ng-click="vm.setCurrentContact(contact)">{{ contact.lastName}}, {{contact.firstName}} </a></td>
<td><a ui-sref="organization({Id: contact.organizationId})"> {{contact.organization.organizationName}} </a></td>
</tr>
</tbody>
</table>
</body>
</html>
I have built a Service (dataservice.js) that get the data from the Database:
data service:
//dataService.js
(function () {
angular.module('appContacts')
.factory('dataService', ['$http', dataService]);
function dataService($http) {
return {
getAllOrganizations: getAllOrganizations,
getAllAvailableOrganizations: getAllAvailableOrganizations,
getAllAvailableContacts: getAllAvailableContacts,
getOrganizationById: getOrganizationById,
GetContactById: GetContactById,
};
function getAllOrganizations() {
return $http({
method: 'GET',
url: 'api/organizations'
});
}
function getAllAvailableOrganizations() {
return $http({
method: 'GET',
url: 'api/organizations'
});
}
function getAllAvailableContacts() {
return $http({
method: 'GET',
url: 'api/contacts'
});
}
function getOrganizationById(id) {
return $http({
method: 'GET',
url: '/api/organizations/{id}'
});
}
function GetContactById(id) {
return $http({
method: 'GET',
url: '/api/contacts/{id}'
});
}
}
})();
And I call it from two controllers: contactsController.js and OrganizationsController.js
My problem is that every time I navigate to another page, the service
has to get all the data that I already have from the Database which
ends in a performance problem. That data would not change inside a
session.
Is there any way to store that info and be available from all controllers?. I have read about local storage, but it is always used with $scope and as you see I am using Controller AS syntax (vm.contact instead $scope.contact)
You can persist data in a service because services in AngularJS are singleton.They don't get destroyed when you navigate to different view.
(function () {
angular.module('appContacts')
.factory('dataService', ['$http', dataService]);
function dataService($http) {
var cachedOrganizations = [];
return {
getAllOrganizations: getAllOrganizations,
getAllAvailableOrganizations: getAllAvailableOrganizations,
getAllAvailableContacts: getAllAvailableContacts,
getOrganizationById: getOrganizationById,
GetContactById: GetContactById,
getCachedOrginations: getCachedOrginations,
setCachedOrginations: setCachedOrginations
};
function getAllOrganizations() {
return $http({
method: 'GET',
url: 'api/organizations'
});
}
function getCachedOrginations(){
return cachedOrganizations;
}
function setCachedOrginations(orginations){
cachedOrganizations = orginations;
}
}
});
From your controller once you get data from backend, call setCachedOrginations to set data in service.
dataService.getAllOrganizations.then(function(data){
dataService.setCachedOrginations(data);
},function(error){
})
Once you navigate to different controller you can get the previously stored data by call the service.
vm.orginations = dataService.getCachedOrginations();
2nd option
Use $cacheFactory service
angular.module('dataCache')
.factory('dataCache', ['$cacheFactory', function($cacheFactory) {
return $cacheFactory('data-cache');
}]);
In your first Controller once you get data
dataService.getAllOrganizations.then(function(data){
// make sure you inject "dataCache" as a dependency
dataCache.put("originations", data);
},function(error){
})
Once you go to second controller get the data from cache
vm.orginations = dataCache.put("originations");

How to perform an ajax lookup within `ng-repeat`

I have an ng-repeat of employees. One of the result fields returned is an employee number e.g. "12345".
How can I perform an ajax lookup and replace the employee number with the corresponding name?
Example: /_api/web/lists/getByTitle('allStaff')/items?$select=fullName&$filter=userid eq '12345'
would return: "Doe, John".
I've tried using a filter but nothing ever gets displayed even though I can see results returned.
<div ng-repeat="emp in employees"">
<i class="fa fa-user"></i> {{emp.id}}
</div>
app.filter('getName', function($http) {
return function(id){
if (id) {
var url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getByTitle('allStaff')/items?$select=fullName&$filter=userid eq '"+id+"'";
$http({
method: 'GET',
url: url,
cache: true,
headers: { "Accept": "application/json;odata=verbose" }
}).success(function (data, status, headers, config) {
userInfo = data.d.results[0].pn;
console.log(userInfo);
}).error(function (data, status, headers, config) {
userInfo = "0";
});
return userInfo;
}
};
});
The filter function is synchronous, while the $http call is asynchronous. The success callback isn't even going to be executed until after the filter function has already returned, so it looks like the return value will be undefined.
An angular filter isn't really appropriate for loading data from an API, and there's an easier approach. Add userInfo to the employees array in the appropriate service/factory/controller (that's up to how you're organizing your code, but the controller where you set $scope.employees is the quick and dirty option). Something like a forEach through the array making an API call for each one and setting employee.userInfo in the success callback:
app.controller('EmployeeController', function($scope, $http) {
// $scope.employees is initialized somehow
$scope.employees.forEach(function (employee) {
if (employee.id) {
var url = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getByTitle('allStaff')/items?$select=fullName&$filter=userid eq '"+employee.id+"'";
$http({
method: 'GET',
url: url,
cache: true,
headers: { "Accept": "application/json;odata=verbose" }
}).success(function (data) {
employee.userInfo = data.d.results[0].pn;
}).error(function () {
employee.userInfo = "0";
});
}
});
});
And in your template:
<div ng-repeat="emp in employees">
<i class="fa fa-user"></i> {{emp.userInfo}}
</div>
It's up to you to figure out what to do before the ajax request is finished, while emp.userInfo is undefined - hide the element, show a placeholder, etc.

Filter Array Using Angular

I am trying to filter an array (courses) by a property, FacilityId.
I am getting a response back for all of my $http calls in my controller which is as follows:
function holeIndexController($scope, $http) {
$scope.facilities = [];
$scope.courses = [];
$scope.holes = [];
getFacilities();
getCourses();
getHoles();
function getFacilities() {
$http({
method: 'GET',
url: '/api/facility'
}).
success(function(result) {
$scope.facilities = result;
}).error(function () {
console.log("Error: " + result.ExceptionMessage);
alert("Could not load facilities");
});
}
$scope.courseByFacility = function (facilities) {
return function(courses) {
return course.facilityId === facility.facilityId;
};
};
function getCourses() {
$http({
method: 'GET',
url: '/api/course'
}).
success(function (result) {
$scope.courses = result;
}).error(function (result) {
console.log("Error: " + result.ExceptionMessage);
alert("Could not load courses");
});
}
function getHoles() {
$http({
method: 'GET',
url: '/api/hole'
}).
success(function(result) {
getFacilities();
$scope.holes = result;
}).error(function(result) {
console.log("Error: " + result.ExceptionMessage);
alert("Could not load courses");
});
}
}
And my HTML is as follows:
<div data-ng-repeat="f in facilities">
Facility: {{f.Name}}
<div data-ng-repeat="c in courses | filter: coursesByFacility">
Course: {{c.Name}}
</div>
</div>
What is the best way to filter courses by their respective FacilityId's?
Pass the facility into the filter function, like:
<div ng-repeat="c in courses | filter:coursesByFacility(f)">
Also, your coursesByFacility function takes a courses parameter, but then you're trying to act upon course (no s). Change this to this:
$scope.coursesByFacility = function(facility) {
return function(course) {
return course.facilityId === facility.facilityId;
}
}
See this jsbin
Edit: Didn't realize the jsbin link was going to strip all the code so you can't see it. Anyways, just view the source, it's minimal and easy to read
You could create a function called getCoursesByFacility() which has a facilityId parameter. The function should iterate through the list of courses and build an array of courses with that facilityId, then return the list. You would then need to call the function from your javascript. Something like this should work:
<div data-ng-repeat="f in facilities">
Facility: {{f.Name}}
<div data-ng-repeat="c in getCoursesByFacility(f.facilityId)">
Course: {{c.Name}}
</div>
</div>

AngularJS Object #<Resource> has no method 'push'

Can someone help me unravel this mystery.
My first $resource return a list.
Then, i loop through the list and call another $resource for each object in the collection.
This code was working using $http, but I was told i should be using the $resource for restful and there u go, I am stuck.
I am getting error : AngularJS Object # has no method 'push'
My restful service returns a JSON object:
In my service/factory :
services.factory('XXXXFactory', function ($resource) {
return $resource('/xxxx-webapp-1.0-SNAPSHOT/restful/services/XXXOption/actions/listAll/invoke', {}, {
query: { method: 'GET', isArray: true },
create: { method: 'POST' }
})
});
In my controller :
app.controller('XXXXListCtrl', ['$scope', 'XXXXsFactory', 'XXXXXFactory', '$location',
function ($scope, XXXXsFactory, XXXXXFactory, $location) {
XXXXsFactory.query(function(data) {
// success handler
var resultType = data.resulttype;
var objects = data.result.value;
$scope.rowList= [];
console.log(objects);
alert('list? = '+ resultType);
if(resultType == "list"){
angular.forEach(objects, function (item) {
alert('item href = '+ item.href);
var InnerXXXXResource = $resource(item.href, {}, { query: { method: 'GET', isArray: true}});
InnerXXXXResource .query(function(rowdata) {
$scope.rowList.push(rowdata);
}, function(error) { });
});
}
}, function(error) {
// error handler
});
}]);
HTML:
<tbody>
<tr ng-repeat="row in rowList">
<td width="70%">{{row.members.XXXDescription.value}}</td>
<td align ="center" width="30%">{{row.members.price.value}}</td>
</tr>
</tbody>
If you want to post data to server and client:
So, in your controller you should get the data using your Factory
$scope.data = XXXXFactory.query();
And if you are dealing with objects change isArray: false
and Posting the data to sever
$scope.createInventory = function(){
XXXXFactory.create($scope.newdata); // posts data to server side
$scope.data.objects.push($scope.newdata); //posts data to client side
$scope.newdata = '';
};

Resources