Ajax in AngularJS Service - angularjs

Created an Angular Service:
calculator_app.service('FillOpportunity', function () {
this.fill_opportunity = function (path,scope) {
$.ajax({
url: 'opportunitycalculator/calculator/GetProducts?idstring=' + path,
type: "GET",
cache: false,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data)
{
scope.opportunity_data = data;
scope.$apply();
},
error: function () {
}
});
};
});
Called the service on ng-change of a dropdown:
FillOpportunity.fill_opportunity($scope.result_path,$scope);
The scope.opportunity_data is binded to the select in UI:
<select id="seloppurtunityproducts" name="selproducttype" multiple="" style="height:300px" ng-model="opportunity_products" ng-options="a for a in opportunity_data"></select>
On ng-Change, Ajax is called when I check in Network of Chrome, but the value is not updated in the select box.
Any inputs?

Don't use jQuery's ajax. Use the built in $http. Using $http automatically begins the digest cycle of angular's builtin compiler. If you must use jquery... then you'd have to call $scope.$apply(); every time there is a data change.
Service:
calculator_app.factory("calcService", ["$http", function($http) {
return {
getItem: function(url, items) {
return $http.get(url,
// query string like { userId: user.id } -> ?userId=value
{ params: items });
}
}
}]);
Inject the service into your controller and use:
calculator_app.controller("MainCtrl", ["calcService", "$scope", function(calcService, $scope) {
$scope.opportunity_data = [];
var payload = {
idstring: path
};
//call the service
calcService.getItem('path/to/calc/api', payload).then(function(response) {
$scope.opportunity_data = response.data;
}).catch(function(response) {
alert('error' + response.data);
});
}]);

Related

AngularJS, reinit (update page) on external event

I have angularjs mobile app, after opening chat page messages being loaded:
myApp.controller('ChatControllerGlobal',function($scope,global) {
$http({
method: 'POST',
url: serverURL+'&act=get_chat',
withCredentials: true,
}).then(function successCallback(data) {
$scope.messages = data.data;
$scope.loader = false;
$scope.content = true;
});
});
and now by external event i need somehow to reinit controller (load new messages):
window.FirebasePlugin.onNotificationOpen(function(data) {
// some code here to reload new chat messages
});
Is there any not dirty way to reinit controller or call controller function?
You could create an injectable service from your Firebase plugin. Then inject that service into your controller, and call a function that gets your messages:
myApp
.factory('firebase', function ($window) {
return $window.FirebasePlugin;
});
myApp.controller('ChatControllerGlobal', function ($scope, $http, firebase) {
getMessages();
firebase.onNotificationOpen(function (data) {
getMessages();
});
function getMessages() {
$http({
method: 'POST',
url: serverURL + '&act=get_chat',
withCredentials: true,
}).then(function successCallback(data) {
$scope.messages = data.data;
$scope.loader = false;
$scope.content = true;
});
}
});
This way you can mock the firebase service in unit tests.

Data bind works only on second click event

I have a click event to call the AngularJS function which retrieves the data from SQL and return to the ajax call.
My problem is the data which is retrieved binds with ng-repeat only on the second click event.Here is my code,
`
(function (app) {
app.controller("OnvioController", function ($scope,$http, OnvioService) {
$scope.retData = [];
$scope.getResult = function () {
var serviceURL =window.location.origin+ '/Datafetching/dataFetch';
$.ajax({
type: "POST",
url: serviceURL,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(data, status) {
$scope.retData = data;
},
error: function (status) {
}
});
}
});
}(angular.module("OnvioModule")));
`
Do not use $.ajax in angularJS.
You can do this by $http.get
I haven't tried it my self right now. but it should work in your case.
(function (app) {
app.controller("OnvioController", function ($scope,$http, OnvioService) {
$scope.retData = [];
$scope.getResult = function () {
var serviceURL =window.location.origin+ '/Datafetching/dataFetch';
$http.get(serviceURL).success( function(response) {
$scope.retData = response;
});
}
});
}(angular.module("OnvioModule")));
refer this document for more details
Use $http.post instead of $.ajax.
If you still want to use $.ajax add a $scope.$apply() in your success function.
This will trigger angular loop to search for changes outside of his context. This is because $.ajax is executing outside of angular stuff.
Add a $scope.$apply() in your success function.

How to call service function in a controller with AngularJS?

I'm trying to call a function from a service in a controller but I get an error saying that the thing I'm calling isn't a function. I'm new to AngularJS so I'm not exactly sure what I'm doing wrong. So, what's the correct way to call a service function in a controller?
I'm trying to call getCurrentUserInfo in the ProfileCtrl
.service('AuthService', function($http, Backand){
function getCurrentUserInfo() {
return $http({
method: 'GET',
url: baseUrl + "users",
params: {
filter: JSON.stringify([{
fieldName: "email",
operator: "contains",
value: self.currentUser.name
}])
}
}).then(function (response) {
if (response.data && response.data.data && response.data.data.length == 1)
return response.data.data[0];
});
}
})
.controller('ProfileCtrl', ['$scope', '$ionicSideMenuDelegate', 'AuthService', function($scope, $ionicSideMenuDelegate, AuthService) {
AuthService.getCurrentUserInfo().then(function(response){
$scope.user = response.data.data;
});
// function getCurrentUserInfo() {
// AuthService.getCurrentUserInfo()
// .then(function (result) {
// $scope.user = result.data;
// });
// }
}])
You need to make it a property of this.
.service('AuthService', function($http, Backand){
this.getCurrentUserInfo = function() {
return $http({
method: 'GET',
url: baseUrl + "users",
params: {
filter: JSON.stringify([{
fieldName: "email",
operator: "contains",
value: self.currentUser.name
}])
}
}).then(function (response) {
if (response.data && response.data.data && response.data.data.length == 1)
return response.data.data[0];
});
}
})
Then in your controller
AuthService.getCurrentUserInfo(whatEverYourParamsAre)
Edit: Actually, let me provide a little bit of context. Angular applies the new function to every .service(....) you include in your controller. As we know, calling this.aFunction in a constructor function causes the new operator in javascript to treat the aFunction function as a property on the object your constructor returns.

DELETE Restful method with angularjs $resource

I'm attemping to call a RESTful method via $resource as following:
Resource:
angular.module('secure',['ngResource']).factory('Vehicle', function ($resource) {
return $resource('/secure/vehicle/index', { id: '#id' }, {
query: {
method: 'GET',
isArray: true
},
delete: {
method: 'DELETE',
isArray: false,
url: '/secure/vehicle/delete/:id'
}
});
});
Then, from other service I inject the above factory and I call DELETE method in this way:
factory.delete = function (procedureId) {
var vehicle = new Vehicle();
vehicle.$delete({id: procedureId}, function () {
//success
deferred.resolve();
}, function (errResponse) {
// fail
console.log(errResponse);
});
return deferred.promise;
};
(Don't pay attention to deferred things, it doesn't work with or without it)
Unfortunately, I always get the same answer:
Remote Address:127.0.0.1:8080
Request URL:http://localhost:8080/secure/vehicle/delete/21
Request Method:DELETE
Status Code:422 Unprocessable Entity
The call itself is set up properly (secure/vehicle/delete/21). In fact, if I do the same, but instead of using $resource variable, using $http, everything works!
$http({
'method': 'DELETE',
'url': '/secure/vehicle/delete/' + procedureId,
'headers': {
'Content-Type': 'application/json'
},
'data': ""
})
.success(function () {
// success
})
.error(function (data, status) {
console.log(data.errors);
});
So, I guess something is missing in $resource-way, but what? Any help, it will be appreciated!
EDIT:
It seems it's a backend problem when it reads the entire url call. If I call DELETE resource, using $http, adding the data: "" as I show above, the backend initializes itself properly.
But if I try the $resource-way, the required params are pre-configured and that doesn't like to the backend, so I need to find the way to say to $resource how to add something like data: "", any ideas?
Test proof that it works:
angular.module('secure', ['ngResource']).factory('Vehicle', function($resource) {
return $resource('/secure/vehicle/index', {
id: '#id'
}, {
query: {
method: 'GET',
isArray: true
},
delete: {
method: 'DELETE',
isArray: false,
url: '/secure/vehicle/delete/:id'
}
});
});
angular.module('secure').factory('VehicleFactory', function(Vehicle, $q) {
var factory = {}
factory.delete = function(procedureId) {
var deferred = $q.defer();
var vehicle = new Vehicle();
vehicle.$delete({
id: procedureId
}, function(r) {
deferred.resolve(r);
}, function(errResponse) {
console.log(errResponse);
});
return deferred.promise;
};
return factory;
});
describe('VehicleFactory', function() {
var $httpBackend, VehicleFactory
beforeEach(module('secure'));
beforeEach(inject(function(_$httpBackend_, _VehicleFactory_) {
$httpBackend = _$httpBackend_
VehicleFactory = _VehicleFactory_
}))
it('deletes vehicle - vehicle.$delete()', function() {
var r = {
data: 'correct response'
}
$httpBackend.when('DELETE', '/secure/vehicle/delete/123').respond(r)
VehicleFactory.delete(123).then(function(response) {
expect(response.data).toBe(r.data)
})
$httpBackend.flush();
});
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation()
$httpBackend.verifyNoOutstandingRequest()
})
})
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-resource.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>
Some much cleaner way to write delete functionality without $q:
angular.module('secure').factory('VehicleFactory', function(Vehicle) {
var factory = {}
factory.delete = function(procedureId) {
return (new Vehicle()).$delete({
id: procedureId
})
}
return factory;
});

After I call a service my view does not get updated in AngularJS

I have the following service:
angular.module('adminApp')
.factory('subjectService', function ($http) {
return {
get: function (testAccountId) {
return $http({
method: 'GET',
url: '/api/Subjects/GetSelect',
params: { testAccountId: testAccountId }
});
}
}
});
The following code works when I call http directly but now when I use the service:
$scope.$watch('selectedTestAccount', function () {
if ($scope.selectedTestAccount != null) {
$scope.myData = null;
$http({
method: 'GET',
url: '/api/Subjects/GetSelect',
params: { testAccountId: $scope.selectedTestAccount }
}).success(function (result) {
$scope.subjects = result;
$scope.myData = null;
});
//subjectService.get($scope.selectedTestAccount)
// .then(function (result) {
// $scope.subjects = result;
// $scope.myData = null;
// alert($scope.subjects);
// }, function (result) {
// alert("Error: No data returned");
// });
}
});
Related to this question. Is this the correct way to call the service. I saw another suggestion that looked like the following and used promises. Should I be doing this:
services.factory('MultiRecipeLoader', ['Recipe', '$q',
function(Recipe, $q) {
return function() {
var delay = $q.defer();
Recipe.query(function(recipes) {
delay.resolve(recipes);
}, function() {
delay.reject('Unable to fetch recipes');
});
return delay.promise;
};
}]);
I tried to reproduce your exemple in a plunker. Let me know if it is what you are look for:
http://plnkr.co/edit/6s5utccjgHTnrrokM1u8?p=preview
In this demo, there is a dropdown that changes the value of the account variable that is $watched in the controller. If the account value changes, it calls the SubjectsServices and updates the unordered list with the names of the subjects. As an addition, there is also a dropdown filled with the same values.
If the only way to change the value is through the dropdown, maybe you can only use the ng-change directive on the select to call an update function that will do the same work.
$scope.update = function() {
SubjectService.get($scope.account).then(function(result) {
$scope.subjects = result.data;
}, function(result) {
// error handling...
});
};
<select ng-change="update()"></select>
The object received in parameter from the method then on an $http promise has a data property containing the requested data.
$http.get(...).then(function(result) {
var subjects = result.data;
});

Resources