From the backend I'm getting simple integer:
#PreAuthorize("hasAnyAuthority('WORKER')")
#RequestMapping(value = "/countFiles", method = RequestMethod.GET)
public ResponseEntity<Integer> countFiles(HttpServletRequest request){
return fileService.countFiles(request);
}
UPDATE - Service site:
public ResponseEntity<Integer> countFiles(HttpServletRequest request) {
Principal name = request.getUserPrincipal();
if (name.getName() == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
User userByLogin = userRepository.findUserByLogin(name.getName());
fileDao.countByUser(userByLogin);
return new ResponseEntity<Integer>(fileDao.countByUser(userByLogin), HttpStatus.OK);
}
At frontend site created simple method:
angular.module('sbAdminApp').factory('FileService', function ($resource) {
var service = $resource('api/file', {
id: '#id'
}, {
saveFile: {
method: 'POST',
url: 'api/file',
headers: {'content-Type': undefined}
},
countFiles: {
method: 'GET',
url: 'api/file/countFiles',
responseType: 'text'
}
});
return service;
});
Angular controller:
var app = angular.module('sbAdminApp');
app.controller('GoogleMapCtrl', function ($scope, $log, uiUploader, FileService, $http) {
$scope.name = "Hello";
console.log('count', FileService.countFiles());
});
But the output looks like:
So there is no my clount at all..It should be 27.
================UPDATE=====================
var val;
FileService.countFiles().$promise.then(function(response) {
console.log('respone', response.data);
val = response.value();
});
console.log('val', val)
Abouve code return both for var and when I print undefinded
Your Service/Factory method 'countFiles' returns a promise. So you have to "wait" for the promise to be resolved.
FileService.countFiles().$promise.then(function(response) {
$scope.val = response.value();
}
The console.log('val', val) would get executed first if written outside since we wait for the promise to get resolved. So, it should be,
var val;
FileService.countFiles().$promise.then(function(response) {
console.log('respone', response.data);
//val = response.value;
//console.log('val', val);
});
Related
I would like to use Angular 1.6.5 for a project rebuild, but I'm not sure how to use the $http.get request in a factory when the source returns only a limited number of records at a time (1000 returned per request) and there are over 2000 records that I need to get.
In my current code I use jquery ajax and in the .done method I check for the presence of the value "__next", and if it exists, I recall the function passing the value "__next". When the "__next" value isn't returned, I do something with the data.
function getSpecifiedList(url){
var specUrl = url;
$.ajax({
url: specUrl,
type: "GET",
headers:{"accept":"application/json;odata=verbose",
error: function(xhr){
console.log(xhr.status + " " + xhr.statusText);
}
}
}).done(function (results){
$("#wc_report_holder").text(results.length);
//buildObjects processes the results and adds to an array
buildObject(results);
if(results.d.__next){
getSpecifiedList(results.d.__next);
}else{
buildGridView();
}
}).fail(function(error){
$("#wc_report_holder").text("There was an error: " + error);
});
}
I would like to figure out how to implement that same value check and recursive call in angular 1.6.5 using best practice and most efficient but I haven't had luck figuring it out based on the angular docs and Googling.
Here is a short version of what I currently have using Angular 1.6.5.
<script>
var sitesApp = angular.module("sitesApp", ['ngRoute']);
sitesApp.controller('SitesListCtrl', ['$scope', 'sites',
function ($scope, sites) {
sites.list().then(function (response) {
$scope.sites = response.data.value;
});
}
]);
sitesApp.controller("SiteDetailsCtrl", ['$scope', '$routeParams', 'sites',
function ($scope, $routeParams, sites) {
sites.find($routeParams.SiteCodePc, function (site) {
$scope.site = site;
});
}
]);
sitesApp.config(function ($routeProvider, $locationProvider) {
$locationProvider.hashPrefix('!');
$routeProvider.
when('/', {
templateUrl: 'https://machine/sites/site-list.html',
controller: 'SitesListCtrl'
}).
when('/:SiteCodePc', {
templateUrl: 'https://machine/sites/site-details.html',
controller: 'SiteDetailsCtrl'
}).
otherwise({
redirectTo: '/'
});
});
sitesApp.factory('sites', ['$http', function ($http) {
var urlBase = "https://some-endpoint-for-data";
var cachedData;
function getData(callback) {
if (cachedData) {
callback(cachedData);
} else {
return $http({
method: 'GET',
url: urlBase
})
.then(function (response) {
//HERE IS WHERE I THINK THE SOLUTION NEEDS TO BE IMPLEMENTED
cachedData = response;
return cachedData;
});
}
}
return {
list: getData,
find: function (SiteCodePc, callback) {
getData(function (response) {
var site = response.data.value.filter(function (entry) {
//debugger;
return entry.SiteCodePc === SiteCodePc;
});
callback(site[0]);
});
}
};
}]);
</script>
<div ng-app="sitesApp">
<div ng-view></div>
</div>
Thanks in advance
It looks like you can do a simple recursion where you accept a second (optional) parameter. If you are calling getData() for the first time then you can get your first 1000 results. However if you find __next then you will call it again sending the current 1000 results you have and concat the next 1000 results with the previous 1000.
sitesApp.factory('sites', ['$http', function ($http) {
var urlBase = "https://some-endpoint-for-data";
function getData(callback, results) {
return $http({
method: 'GET',
url: urlBase
})
.then(function (response) {
// If you have found a previous batch of results then concat the two arrays
if(results) {
response = response.concat(results);
}
// If there are more results to be found then recursively call the same function passing the batched results
if(response.__next) {
return getData(callback, response);
}
// If there are no more results to be found then trigger your callback function
else {
callback(response);
}
});
}
return {
list: getData,
find: function (SiteCodePc, callback) {
getData(function (response) {
var site = response.data.value.filter(function (entry) {
//debugger;
return entry.SiteCodePc === SiteCodePc;
});
callback(site[0]);
});
}
};
}]);
I have implemented same kind of scenario with pagination logic and $q. In this sample code I am pulling the records recursively as lazy based on the LazyloadingLimit. You can specify the limit based on your requirement.So it only pulls the records based on the count from the total collection. In this below sample I am not using $http. On your real sample you can use the $http to pull the records from the server. Here I just hard coded the collection initially.
In your case you have to fetch total records count initially and apply some pagination logic or some other parameter to pull the next records.
angular.module('app', []);
angular.module('app').controller('SampleController', function ($scope,$http, $timeout, $q) {
// $scope.initialize();
$scope.mainCount = 0;
$scope.lazyloadingLimit = 2;
$scope.tileDefinitions = null;
$scope.tempList = null;
$scope.totalRecordCollection = [
{ "Name": "Record1" },
{ "Name": "Record2" },
{ "Name": "Record3" },
{ "Name": "Record4" },
{ "Name": "Record5" },
{ "Name": "Record6" },
{ "Name": "Record7" },
];
function getTotalRecordCollection() {
var deferred = $q.defer();
deferred.resolve($scope.totalRecordCollection);
return deferred.promise;
}
$scope.initialize = function () {
debugger;
var currentCount=0;
var pageList = new Array();
var currentPage = 1;
var numberPerPage = 2;
var numberOfPages = 0;
function makeList() {
numberOfPages = getNumberOfPages();
}
function getNumberOfPages() {
return Math.ceil($scope.tempList.length / numberPerPage);
}
function nextPage() {
currentPage += 1;
}
function loadList() {
var deferred = $q.defer();
if (currentCount !== $scope.tempList.length) {
var begin = ((currentPage - 1) * numberPerPage);
var end = begin + numberPerPage;
pageList = $scope.tempList.slice(begin, end);
currentCount = currentCount + pageList.length;
$scope.mainCount = currentCount;
deferred.resolve(true);
} else {
debugger;
return $q.reject();
}
return deferred.promise;
}
function loadNextRecords() {
loadList().then(function (res) {
nextPage();
loadNextRecords();
});
}
getTotalRecordCollection().then(function (response) {
debugger;
$scope.tempList = response;
makeList();
loadNextRecords();
});
}
});
<body ng-controller="SampleController">
<input type="button" value="Click Here" ng-click="initialize()"/>
{{mainCount}}
</body>
Once all the records are loaded , you should reject the promise else the recursive loops never end.
Hope this helps
I am working on an application in which I am calling a webservice and get a response. I am using that response in 2 different modules. In first module I am using as it is and in second module I am doing some formatting and using it.
I created a service for getting data as follows
angular.module('myApp').factory('getData',function($http, $q, restURLS) {
var getData= {};
getData.getTree = function () {
var deferred = $q.defer();
$http.get(restURLS.getTree).
success(function (data) {
deferred.resolve(data);
}).error(deferred.reject);
return deferred.promise;
};
return getData;
});
for Serving response I created another factory as follows
angular.module('myApp').factory('tree', function($http, $q, restURLS, getData, messages) {
var tree= {};
tree.hierarchy = {};
tree.formattedHierarchy = {};
function formatHierarchy(data) {
//some formatting on data.
tree.formattedHierarchy = data;
}
function callTree() {
getData.getTree()
.then(function (data) {
tree.hierarchy = angular.copy(data);
formatHierarchy(data);
}).catch(function () {
//error
});
}
callTree();
return tree;
});
I want to call webservice only once. if data is loaded then factory('tree') should send the data to controller. Otherwise factory('tree') should call webservice and load data.
you need something to know if you got your tree or not... try this:
(UPDATED)
var myApp = angular.module('myApp', ['ngMockE2E'])
// FAKE HTTP CALL JUST FOR EMULATE
.run(function ($httpBackend) {
var tree = [{
node1: 'abcde'
}, {
node2: 'fghi'
}];
$httpBackend.whenGET('/tree').respond(function (method, url, data) {
return [200, tree, {}];
});
})
// YOUR HTTP SERVICE
.factory('getData', function ($http, $q) {
return {
getTree: function () {
var deferred = $q.defer();
$http.get("/tree").
success(function (data) {
deferred.resolve(data);
}).error(deferred.reject);
return deferred.promise;
}
}
})
.factory('TreeFactory', function ($http, $q, getData) {
var tree = {};
var updated = false;
tree.hierarchy = {};
tree.formattedHierarchy = {};
function formatHierarchy(data) {
//some formatting on data.
tree.formattedHierarchy = data;
}
return {
callTree: function() {
if(!updated){
console.log("making http call");
return getData.getTree().then(function (data) {
tree.hierarchy = angular.copy(data);
formatHierarchy(data);
updated = true;
return tree;
}).
catch (function () {
//error
});
}else{
console.log("tree already loaded");
var deferred = $q.defer();
deferred.resolve(tree);
return deferred.promise;
}
}
};
}).controller("MyCtrl", ['$scope', 'TreeFactory', function ($scope, TreeFactory) {
$scope.updateTree = function(){
TreeFactory.callTree().then(function(data){
$scope.tree = data;
});
};
}]);
HTML
<div ng-app="myApp" ng-controller="MyCtrl" ng-init="updateTree()">tree: {{tree}} <br><button ng-click="updateTree()">UPDATE TREE</button></div>
CHECK THE FIDDLE
I found this styleguide for angularjs:
https://github.com/johnpapa/angular-styleguide#factories
I wana now to write my code on that way:
Here is my working factory:
.factory('Noty',function($http){
return{
update:function(NotificationData){
return $http ({
method: "PUT",
url : "/api/notification/" + NotificationData.task,
data : NotificationData
});
}
};
});
How can I rewrite it to look like in document above?
/* recommended */
function dataService() {
var someValue = '';
var service = {
save: save,
someValue: someValue,
validate: validate
};
return service;
////////////
function save() {
/* */
};
function validate() {
/* */
};
}
You'd write it like this:
(function () {
'use strict';
var factoryNotyModule = angular.module('yourApp.factory.Noty', []);
factoryNotyModule.factory('Noty', Noty);
Noty.$inject = ['$http'];
function Noty($http) {
function updateData (NotificationData) {
return $http({
method: 'PUT',
url: '/api/notification/' + NotificationData.task,
data: NotificationData
});
}
return {
updateData: updateData
};
}
})();
So that you can use it after you inject it somewhere:
Noty.updateData(data);
This is pretty simple, as you are already using a factory with the revealing module pattern:
.factory('Noty', function ($http) {
var service = {
update : update
};
return service;
function update(NotificationData) {
return $http({
method : "PUT",
url : "/api/notification/" + NotificationData.task,
data : NotificationData
});
}
});
I have one factory contains save customer function.On success I want to pass its response in controller so that i can update the view.
Factory
sampleApp.factory("authFactory", function($location, $http, transformRequestAsFormPost) {
return {
saveCustomer: function(data) {
var request = $http({
method: "post",
url: "webservice/ws.php?mode=saveCustomer",
transformRequest: transformRequestAsFormPost,
data: data
});
request.success(
function(response) {
console.log(response);
}
);
}
};
});
Controller
sampleApp.controller('customerController', function($scope, testService,authFactory,$http) {
$scope.addCustomer = function() {
var data = {name: $scope.customerName,city: $scope.customerCity};
// Calling Factory Function
authFactory.saveCustomer(data);
// How to fetch response here
}
});
Please help me to solve that problem
Thanks
Various ways, the first one that comes to mind is something like this:
//in your factory
return {
saveCustomer: function(data) {
var request = $http({...});
return request;
}
}
//in your controller
authFactor
.saveCustomer(data)
.success(function() {
//update controller here
})
You are working with "promises" here. You can do a few different things depending on what you return from your service method.
One thing you can do is simply return the promise and handle it in your controller.
service:
return {
saveCustomer: function(data) {
return $http({...});
}
}
contoller:
authFactor.saveCustomer(data).success(function(customer) {
$scope.customer = customer;
})
Another thing you can do is return an object reference and put it on your scope. When the object is filled, it will be updated in your scope.
service:
return {
saveCustomer: function(data) {
var customer = {};
$http({...}).success(function(data){
angular.copy(data, customer);
});
return customer;
}
}
controller:
$scope.customer = authFactor.saveCustomer(data);
The advantage of this second way is that most of your logic stays in your service. You controller stays simple and doesn't have to know about promises or handle them.
This is my html
<form name="change_profile_form" ng-controller="profileController" id="change_profile_form"
ng-submit="changeProfileForm()">
<input id="username" ng-model="data.User.username" name="username" class="profile_input" disabled value="{{ my_profile.User.username }}" required />
This is my js:
var angularApp = angular.module("myApp", ['ngResource', 'myApp.directives']);
// MyProfile constructor function to encapsulate HTTP and pagination logic
angularApp.factory('MyProfile', function($http) {
var MyProfile = function() {
this.user = [];
this.profile = [];
// this.page = 1;
// this.after = '';
// this.perPage = 6;
// this.maxLimit = 100;
// this.rowHeight = 308;
};
MyProfile.prototype.fetch = function() {
var url = "/users/my_profile?callback=JSON_CALLBACK";
$http.defaults.headers.get = { 'Accept' : 'application/json', 'Content-Type' : 'application/json' };
$http.get(url).success(function(data, status, headers, config) {
this.user = data.user;
}.bind(this));
};
return MyProfile;
});
angularApp.controller('profileController', ['$scope', 'MyProfile', '$users', '$parse', function($scope, MyProfile, $users, $parse) {
$scope.my_profile = new MyProfile();
$scope.my_profile.fetch();
$scope.changeProfileForm = function() {
var serverMessage = $parse('change_profile_form.email.$error.serverMessage');
$users.changeProfile(
$scope.data,
function(data, status, headers, config) {
if (typeof data.error === 'undefined' || typeof data.result === 'undefined') {
alert('Server cannot be reached. Please refresh webpage and try again!');
return;
}
if (data.result != null) {
title = "Profile Saved.";
message = "Your <strong>PROFILE</strong> has been<br />successfully changed and saved.";
options = new Object();
options["box-title"] = new Object();
options["box-title"]["padding-left"] = 5;
showOverlayForSuccess(title, message, options);
}
},
function(data, status, headers, config) {
console.log(data);
// error callback
$scope.errors = data.errors;
})
}
}
I checked my network tab in chrome dev tools. The /users/my_profile in the factory is not being triggered.
Where did I get this wrong?
I adopted the logic from http://sravi-kiran.blogspot.com/2013/03/MovingAjaxCallsToACustomServiceInAngularJS.html
My changes are:
a) realize that the input is already using ng-model, hence no point to use value attribute
b) rewrite the factory and use the $q
c) inside the controller, call the factory method directly
Change a)
<form name="change_profile_form" ng-controller="profileController" id="change_profile_form"
ng-submit="changeProfileForm()">
<input id="username" ng-model="data.User.username" name="username" class="profile_input" required style="position: absolute;left:151px;top:<?php echo -138 + $difference; ?>px;"/>
Change b)
// MyProfile constructor function to encapsulate HTTP logic
angularApp.factory('MyProfile', function($http, $q) {
return {
getProfile: function() {
// creating a deferred object
var deferred = $q.defer();
var url = "/users/my_profile?callback=JSON_CALLBACK";
// prepare headers so that CakePHP can accept the call
$http.defaults.headers.get = { 'Accept' : 'application/json', 'Content-Type' : 'application/json' };
// calling the url to fetch profile data
$http.get(url).success(function(data, status, headers, config) {
// passing data to deferred's resolve function on successful completion
deferred.resolve(data);
}).error(function() {
// sending a friendly error message in case of failure
deferred.reject("An error occurred while fetching data");
});
// returning the promise object
return deferred.promise;
}// end getProfile
}// end return
});
Change c) and this is how I call the factory inside the controller
angularApp.controller('profileController', ['$scope', 'MyProfile', '$users', '$parse', function($scope, MyProfile, $users, $parse) {
function getProfile() {
MyProfile.getProfile().then(function(data) {
$scope.data = data.user;
console.log($scope.data);
},
function(errorMessage) {
$scope.error = errorMessage;
});
}
getProfile();