I have a model that I am using to hold my data in angular:
var FuelProcessingModel = function (carrierService) {
this.myArray = [];
};
That model has an array of MyObjects that I get from the DB:
var MyObject = function () {
//stuff
}
I update this using a REST call:
$scope.add = function () {
var myObject = new MyObject();
$scope.model.MyObjects.push(myObject);
service.add(myObject);
};
Which I use a service to hit the Server:
this.add = function (myObject) {
$http({
method: "POST",
url: "theServer",
data: myObject
});
}
The REST service just adds to the database, It doesn't return anything.
I need to reload the data from the database after the update is finished, so that my records now have all newly associated ID's and pertinent data.
I cannot just do:
window.location.reload();
The user starts by selecting a value from a drop down list to decide which list of data they start off seeing. I cannot / do not want to pass the value to it, mainly because it is in its own partial view, with its own controller, because it is used on many pages.
I tried doing:
$scope.add = function () {
//same as above
//this
service.get().then(function(result) { $scope.model.myArray = result.data; });
};
Obviously the problem here is the promise isn't complete before the DOM reloads the page. So the user saw themself add an item to the array and it vanished.
Do I want to load the page after the promise is complete? (How would I do that?)
should I return the updated data from the REST service and reset the current value? (seems like the same promise issue)
Is there a better practice that I do not know about?
UPDATE
For Bergi:
this.get = function (key) {
return $http({
method: "GET",
url: "theServer" + key
})
.success(function (data) {
return data;
});
}
I think you want to chain your two promises:
$scope.add = function () {
var myObject = new MyObject();
$scope.model.MyObjects.push(myObject);
return service.add(myObject).then(function() {
return service.get();
}).then(function(result) {
$scope.model.myArray = result.data;
});
};
and
this.add = function(myObject) {
return $http({
// ^^^^^^ return a promise here
method: "POST",
url: "theServer",
data: myObject
});
};
You can wrap your service call in a deferred promise, and on return success re-init your data from the controller..
$scope.add = function () {
var myObject = new MyObject();
$scope.model.MyObjects.push(myObject);
service.add(myObject).then(function (response) {
// here's where you'd do whatever you want to refresh your model
}),
function (err) {console.log(err);};
};
And the service:
this.add = function (myObject) {
var deferred = $q.defer();
$http({
method: "POST",
url: "theServer",
data: myObject,
success: function (response) {
deferred.resolve(err);
},
error: function (err) {
deferred.reject(err);
}
});
return deferred.promise;
}
Related
In the following code I want to execute a series of $http requests that modify a list. When all the responses are received, I want to process the list and remove part of the content.
The problem is that when I print the list after $q.all, the Chrome console shows a length of 3, but when I expand it to read the content only 2 elements are shown. On JSFiddle I have no issues, though.
var app = angular.module('MyApp',[]);
app.controller('MyController',['$scope','$q',"$http", function($scope,$q,$http){
var loopPromises = [];
var workorders = null;
$scope.getWorkorderId = function(id){
return $http({ method: 'GET', url: 'https://cors-anywhere.herokuapp.com/https://blk.clojure.xyz/interdiv/api/v1/service/' + id })
.then(function success(response) {
return response.data;
}, function error(response) {
console.log(response);
});
}
$http({ method: 'GET', url: 'https://cors-anywhere.herokuapp.com/https://blk.clojure.xyz/interdiv/api/v1/workorder' })
.then(function success(response) {
workorders = response.data;
}, function error(response) {
console.log(response);
})
.then(function() {
if (workorders == null) {
return;
}
angular.forEach(workorders, function(value, index, obj) {
var deferred = $q.defer();
loopPromises.push(deferred.promise);
var waitResponse = $scope.getWorkorderId(value.id);
waitResponse
.then(function(res) {
obj[index].services = res;
deferred.resolve();
})
});
$q.all(loopPromises)
.then(function() {
// Should contain 3 elements, only 2 are shown
console.log(workorders);
});
});
}]);
see better in the screenshots. Console Requests
The problem was in the second part of the code not copied in the question: I was using .splice() inside angular.forEach() which changes the indices of the elements within the array.
I have a service that do two $http.get to get data from two source and concat into an array and return it to controller.
angular.module('starter.controllers').factory('GetDataList', function ($http) {
var arrDataList = [];
var postData1 = {
"param": "1"
};
var postData2 = {
"param": "2"
};
$http({
method: 'GET',
url: 'https://localhost/search',
data: postData1
})
.then(function (items) {
debugger
arrDataList = arrDataList.concat(items.data.list);
});
$http({
method: 'GET',
url: 'https://localhost/locate',
data: postData2
})
.then(function (items) {
debugger
arrDataList = arrDataList.concat(items.data.list);
});
return {
getAPIData: function () {
debugger
return arrDataList;
}
};
});
In my controller, I call it like this:
$scope.GetList = function () {
debugger
$scope.item = GetDataList.getAPIData();
$scope.$broadcast('scroll.infiniteScrollComplete');
}
When I use the debugger in console, I notice that
1) getAPIData() will be called first but it has data in it
2) Next debugger triggered at the controller which GetDataList.getAPIData(); does not return any data for $scope.Item
3) The last debugger reach $http call which return the data correctly as I observed in the console. But it never reach the controller side afterwards so no data is being displayed in the mobile app
I read about the natural behavior of angular async call so this seems to be normal. But in my case, what should I do to ensure that the data could reach the controller?
Many thanks
To achieve that without loosing performance, you should use $q.all(), so it will keep your request async and it will return the data once all the promises are resolved. Don't try a synchronic approach because that will reduce your performance.
You can use it like this:
Your factory:
app.factory('GetDataList', function($q, $http) {
var promises = [];
var arrDataList = [];
var requests = [{
url: 'https://localhost/search',
postData: {
"param": "1"
}
}, {
url: 'https://localhost/locate',
postData: {
"param": "2"
}
}];
angular.forEach(requests, function(req) {
executeRequest(req);
})
function resolveData(data) {
debugger
if (arrDataList.length === 0) {
arrDataList = data.data;
} else {
arrDataList = arrDataList.concat(data.data);
}
}
function executeRequest(req) {
var promise = $http({
url: req.url,
method: 'GET',
data: req.postData
})
.then(resolveData);
promises.push(promise)
}
return {
getAPIData: function() {
debugger
return $q.all(promises).then(function() {
return arrDataList
});
}
}
});
And your controller:
$scope.GetList = function() {
debugger
GetDataList.getAPIData().then(function(item) {
$scope.item = item
});
$scope.$broadcast('scroll.infiniteScrollComplete');
}
What we are doing here is executing each request inside the requests array (using its url and postData) asynchronously and saving the promises inside an array. When getApiData is called, it returns a function that will be called after $q.all(promises), that means it will return the data after all those promises are finished (the promises ask if the arrDataList is empty and concats the new data if it's not).
This way you get to keep your async calls! And inside the controller you receive a promise instead of the data itself.
You should make it to be synchronized as in the below
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope,serviceDemo) {
$scope.name = 'World';
});
app.factory('serviceDemo', function ($http) {
var arrDataList = [];
var postData1 = []; var postData2 =[]
var firstMethod=function(){
$http({
method: 'GET',
url: 'a.json'
})
.then(function (response) {
console.log(response);
postData1=response.data;
arrDataList.push(postData1);
// console.log(postData1);
secondMethod(); //put the second method call here
});
}
var secondMethod=function(){
$http({
method: 'GET',
url: 'b.json'
})
.then(function (response) {
postData2=response.data;
arrDataList.push(postData2);
console.log(arrDataList);
});
}
var getAPIData= function () {
firstMethod();
return arrDataList;
}
return {
getAPIData: getAPIData
};
});
Modification Made:
You need to call the second method inside the success block of your first method. By this way your first method gets executed, when the result is fetched your second method gets executed and then only control will come out of the first method block.
LIVE DEMO
here is my javascript code
$scope.addUser = function () {
debugger;
url = baseURL + "AddUser";
$scope.objUser = [];
$scope.objUser.push( {
"ID": '0',
"UserName": $scope.txtUserName,
"Password": $scope.txtPassword,
"Role":"Non-Admin"
});
$http.post(url,$scope.objUser[0])
.success(function (data) {
debugger;
alert("S");
window.location = "../View/Login.html";
}).error(function () {
debugger;
alert("e");
});
}
here is my server method code
[HttpPost]
public int AddUser(UserModel user)
{
//_entity.Configuration.ProxyCreationEnabled = false;
tblUser objUser = new tblUser();
objUser.UserName = user.UserName;
objUser.Password = user.Password;
objUser.Role = user.Role;
_entity.tblUsers.Add(objUser);
_entity.SaveChanges();
return objUser.ID;
}
You can use promises to get the response. this can be inside into a service and call it whenever you want to use it.
this.addUser = function (obj) {
var datosRecu = null;
var deferred = $q.defer();
var uri = baseUrl + 'addUser';
$http({
url: uri,
method: 'post',
data: angular.toJson(obj)
}).then(function successCallback(response) {
datosRecu = response;
deferred.resolve(datosRecu);
}, function errorCallback(response) {
datosRecu = response;
deferred.resolve(datosRecu);
});
return deferred.promise;
};
Also .error and .success are deprecated.
PD: the parameter data: inside the $http correspond to the body. if you want to send parameters you should use params:{}
EDIT:
Here i leave you a link how promises work. Angular promises
Basically this helps to process data asynchronously
the example above can be used inside a service like this
myApp.service('myService', function($q, $http){
// here your services....
});
the service can be injected inside to any controller to provide the data that what you want, inside of your functions
myApp.controller('myController', function($scope, myService){
$scope.list = function(){
$promise = myService.getAll(); // this will be the name of your function inside your servive
$promise.then(function(data){
console.log(data); //here you can se your promise with data like status and messages from the server.
});
};
});
Hope it helps.
I have this service:
angular.module('autotestApp').factory('GroupService', ['$http', function ($http) {
var groups = [];
return{
list: function () {
return groups;
},
retrieve: function () {
$http({
method: "get",
url: "/enterprises/_groups"
}).success(function (response) {
groups = response;
}).error(function () {
console.log("failed")
});
}
}
}]);
and this is my controller:
angular.module('autotestApp').controller('GroupsController', function($scope, $http, GroupService) {
GroupService.retrieve();
$scope.items = GroupService.list();
});
So, in my controller, I am first getting the result from the API so that the variable groups(in the service) gets assigned, then I am using the list() method to assign the values to $scope.items.
I am sure that API is returning results. But the
groups = response
part is not working correctly. When I changed it to
groups.push(response)
it is working but the result is a list inside a list, which I dont't want: [[ Object, Object, Object ]]
I want this: [ Object, Object, Object ]
How to fix this?
The reason
groups = response
is not working is because you're sending an async request that will replace the groups reference after you've already retrieved the old reference via the list function. The reason it works with the push modification is because Angular creates a watch that notices that the collection has changed and updates your view. However, your code is now working, but you don't understand why it works, and it's prone to breaking unexpectedly.
When dealing with asynchronous code, the best way to deal with it is to write your own code to take that into account. Here's a version that does the whole thing in an async fashion:
angular.module('autotestApp').factory('GroupService', ['$http', function ($http) {
var groupsResult;
return{
retrieve: function () {
if (groupsResult) { return groupsResult; }
return groupsResult = $http({
method: "get",
url: "/enterprises/_groups"
}).then(function (response) {
return response.data;
}, function () {
console.log("failed")
});
}
}
}]);
angular.module('autotestApp').controller('GroupsController',
['$scope', 'GroupService', function($scope, GroupService) {
GroupService.retrieve().then(function (groups) {
$scope.items = groups;
});
}]);
One of the fixes you could use is:
for (var i = 0; i < response.length; i++) {
groups.push(response[i]);
});
That way you would have [ Object, Object, Object ]
EDIT:
One thing you could try is the following, change your retrieve function to return your promise:
return{
list: function () {
return groups;
},
retrieve: function () {
var promise = $http({
method: "get",
url: "/enterprises/_groups"
}).success(function (response) {
groups = response;
}).error(function () {
console.log("failed")
});
return promise;
}
}
and then in your controller:
angular.module('autotestApp').controller('GroupsController', function($scope, $http, GroupService) {
GroupService.retrieve().finally(function () {
$scope.items = GroupService.list();
});
});
I think your groups = response is working, but when you do $scope.items = GroupService.list() the request isn't finished yet.
do groups.concat(response)
this will flatten the items & add it to parent list rather than appending a single element.
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;
});