Angular promise in async loop function - angularjs

I have an upload function which loops through the selected files and adds them on the servers file system.
Upload factory
app.factory('uploadFactory', function ($upload, $q) {
var uploadFactory = {};
var image = {
Models: [],
Images: [],
uploadImages: function () {
var defer = $q.defer();
for (var i = 0; i < this.Models.length; i++) {
var $file = this.Models[i].file;
(function (index) {
$upload
.upload({
url: "/api/upload/",
method: "POST",
file: $file
})
.success(function (data, result) {
// Add returned file data to model
var imageObject = {
Path: data.Path,
Description: image.Models[index].Description,
Photographer: image.Models[index].Photographer
};
image.Images.push(imageObject);
defer.resolve(result);
});
})(i);
}
return defer.promise;
}
};
uploadFactory.image = function () {
return image;
};
return uploadFactory;
});
In my controller
$scope.imageUpload = new uploadFactory.image;
$scope.create = function () {
var uploadImages = $scope.imageUpload.uploadImages();
uploadImages.then(function ()
$scope.ship.Images = $scope.imageUpload.Images;
shipFactory.create($scope.ship).success(successPostCallback).error(errorCallback);
});
};
My problem is that the promise only holds the promise for the first upload through the looping. I have read something about $q.all() but I'm not sure how to implement it to work.
How can I make it to hold through the whole loop? Thanks!
Solution
var image = {
Models: [],
Images: [],
uploadImages: function () {
for (var i = 0; i < this.Models.length; i++) {
var $file = this.Models[i].file;
var defer = $q.defer();
(function (index) {
var promise = $upload
.upload({
url: "/api/upload/",
method: "POST",
file: $file
})
.success(function (data, result) {
// Add returned file data to model
var imageObject = {
Path: data.Path,
Description: image.Models[index].Description,
Photographer: image.Models[index].Photographer
};
image.Images.push(imageObject);
defer.resolve(result);
});
promises.push(promise);
})(i);
}
return $q.all(promises);
}
};

You're right $q.all() is the way to go here (totally untested -- but i think this is at least the right direction..):
app.factory('uploadFactory', function ($upload, $q) {
var uploadFactory = {};
var image = {
Models: [],
Images: [],
uploadImages: function () {
var promises = [];
for (var i = 0; i < this.Models.length; i++) {
var $file = this.Models[i].file;
var response = $upload
.upload({
url: "/api/upload/",
method: "POST",
file: $file
})
.success(function (data, result) {
// Add returned file data to model
var imageObject = {
Path: data.Path,
Description: $file.Description,
Photographer: $file.Photographer
};
image.Images.push(imageObject);
});
promises.push(response);
}
return $q.all(promises);
}
};
uploadFactory.image = function () {
return image;
};
return uploadFactory;
});

Related

how i can upload file using custom keys

I have to create a quiz system for the website. I have to upload image type question and also image type options on single page using angularjs and webapi. I want to check in api that which image is for option and which image is for question image while array of image is with name visible I can show images in array here is my code:
app.directive('uploadFiles', function () {
return {
scope: true, //create a new scope
link: function (scope, el, attrs) {
el.bind('change', function (event) {
var files = event.target.files;
console.info(files);
for (var i = 0; i < files.length; i++) {
scope.$emit("seletedFile", { file: files[i] });
}
});
}
};
});
app.controller('questionairController', function ($scope, $http) {
$scope.files = [];
$scope.$on("seletedFile", function (event, args) {
$scope.fileList = true;
$scope.$apply(function () {
$scope.files.push(args.file);
});
});
$scope.addQuestionair = function () {
var cID = document.getElementById("CustomerID").value;
var pID = document.getElementById("ProjectID").value;
$scope.question.CustomerID = cID;
$scope.question.ProjectID = pID;
console.info($scope.question);
console.info($scope.OptionsList);
var viewObj = {
questionair: $scope.question,
options: $scope.OptionsList
};
console.info(viewObj);
$http({
method: 'POST',
url: 'api/Questions/AddQuestion',
headers: { 'Content-Type': undefined },
uploadEventHandlers: {
progress: function (e) {
if (e.lengthComputable) {
var progressValue = (e.loaded / e.total) * 100;
$scope.ProgressWidth = 0;
$scope.ProgressWidth = { 'width': progressValue + '%' };
}
}
},
transformRequest: function (data) {
console.info("in transform");
var formData = new FormData();
formData.append("model", angular.toJson(data.model));
for (var i = 0; i < data.files.length; i++) {
formData.append(data.files[i].name, data.files[i]);
}
return formData;
},
data: { model: viewObj, files: $scope.files }
}).then(function (response) {
console.info(response);
$scope.SaveMessage = response.data;
GetQuestionair();
$scope.question = null;
}, function (error) {
});
};
Here is my API function
[HttpPost]
public HttpResponseMessage AddQuestion()
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
HttpFileCollection hfc = HttpContext.Current.Request.Files;
var model = HttpContext.Current.Request["model"];
ViewModel viewModel = Newtonsoft.Json.JsonConvert.DeserializeObject<ViewModel>(model);
if (viewModel == null)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
var root = HttpContext.Current.Server.MapPath("~/SiteImages/");
if (!Directory.Exists(root))
{
Directory.CreateDirectory(root);
}
HttpPostedFile hpf = hfc[0];
if (hpf.ContentLength > 0)
{
hpf.SaveAs(root + "/" + Path.GetFileName(hpf.FileName));
}
{
db.Tbl_Questionair.Add(viewModel.questionair);
db.SaveChanges();
foreach (var item in viewModel.options)
{
item.CreatedOn = DateTime.Now;
item.QID = viewModel.questionair.ID;
item.CustomerID = viewModel.questionair.CustomerID;
item.ProjectID = viewModel.questionair.ProjectID;
db.Options.Add(item);
db.SaveChanges();
}
return Request.CreateResponse(HttpStatusCode.OK, "Data successfully saved!");
}
}

GET call to REST API returns null

I am trying to make a GET call to test a REST API but it keeps returning null, is there anything I am doing wrong:
Making the call in controller.js
function ConsumerSearchCtrl($scope, BusinessCategoryService) {
console.log(BusinessCategoryService);
}
127.0.0.1:8000/api/category/ works perfectly fine
Code in services.js for API
/**
*
*/
function BusinessCategoryService(WenzeyAPI, $q) {
var scope = this;
scope.categories = categories;
function categories() {
var q = $q.defer();
WenzeyAPI.get('http://127.0.0.1:8000/api/category/').then(function success (res) {
q.resolve(res.data);
}, function failure (err) {
q.reject(err);
})
return q.promise;
}
}
/**
*
*/
function WenzeyAPI() {
var scope = this,
ip = "http://127.0.0.1:8000";
scope.get = get;
scope.post = post;
function get(url, data) {
data = data || {};
var req = {
method: 'GET',
url: url,
data: data
}
var q = $q.defer();
$http(req).then(function success(response) {
q.resolve(response);
}, function failure(err) {
q.reject(err);
});
return q.promise;
}
function post(url, data) {
data = data || {};
var req = {
method: 'POST',
url: url,
data: data
}
var q = $q.defer();
$http(req).then(function success(response) {
q.resolve(response);
}, function failure(err) {
q.reject(err);
});
return q.promise;
}
}
Removing WenzeyAPI and using $http resolved it.
function BusinessCategoryService($http) {
this.getAllData = function () {
return $http({
method: 'GET',
url: 'http://127.0.0.1:8000/api/category/',
});
}
}

Webservice Call using Angular in MVC

Can some one please explain how can I call an action using Angular in MVC project?
I managed to call action using Ajax like this:
var app = angular.module('toprightSec', ['ng']);
app.controller('NationalityCtrl', ['$scope', function ($scope, $http) {
$scope.items = [];
var items = populateListFromLocalStorage("offices", "Login/GetOffices", 24);
var officelist = "";
for (var i = 0; i < items.length; i++)
{
$scope.items[i] = { "name": read_prop(items[i], 'office_desc'), "guid": read_prop(items[i], 'office_guid') };
}
$scope.reloadPage = function () { window.location.reload(); }
$scope.getResult = function ($index, item) {
$.ajax({
type: 'GET',
async: true,
url: 'Login/ChangeOffice',
contentType: "application/json; charset=utf-8",
dataType: "json",
data: {
officeID: $scope.items[$index].guid,
officeName: $scope.items[$index].name,
},
success: function (msg) {
}
});
};
}]);
I tried changing it to Angular like this:
var AngularModule = angular.module('toprightSec', ['ng']);
AngularModule.service('ApiCall', ['http', function ($http) {
var result;
this.PostApiCall = function (controllerName, methodName, obj) {
debugger;
result = $http.post('api/' + controllerName + '/' + methodName,obj).success(function (data, success) {
result = (data);
}).error(function () {
("Something went wrong");
});
return result;
};
}]);
AngularModule.controller('NationalityCtrl', ['$scope', function ($scope, $http, ApiCall) {
$scope.items = [];
var items = populateListFromLocalStorage("offices", "Login/GetOffices", 24);
var officelist = "";
for (var i = 0; i < items.length; i++)
{
$scope.items[i] = { "name": read_prop(items[i], 'office_desc'), "guid": read_prop(items[i], 'office_guid') };
}
$scope.reloadPage = function () { window.location.reload(); }
$scope.getResult = function ($index, item) {
var obj = {
'officeID' : '123',
'officeName' : 'Sample'
}
var result = ApiCall.PostApiCall("Login", "ChangeOffice", obj).success(function (data) {
var data = $.parseJSON(JSON.parse(data));
$scope.message = data;
});
};
}]);
I keep getting this error "PostApiCall" is not defined on browser console.
Any idea what am I doing wrong here?
Thanks.
User promise, return when $http are done:
this.PostApiCall = function (controllerName, methodName, obj) {
debugger;
var deferred = $q.defer();
$http.post('api/' + controllerName + '/' + methodName,obj).success(function (data) {
deferred.resolve(data);
});
return deferred.promise;
};
var result = ApiCall.PostApiCall("Login", "ChangeOffice", obj).then(function (data) {
var data = $.parseJSON(JSON.parse(data));
$scope.message = data;
});
Well, I managed to fix it, I noticed two issues with my code:
I was using $http in two different places, and I was using success at both the spots. (solution suggested by charlietfl)
The other issue was that the parameters that I was passing, they were not right.
This is my updated working code:
var app = angular.module('BMSApp', []);
app.factory('ApiCall', ['$http', function ($http) {
var PostApiCall = function (controllerName, methodName) {
return $http.post(controllerName + '/' + methodName);
};
var GetApiCall = function (controllerName, methodName) {
return $http.get(controllerName + '/' + methodName);
};
return {
PostApiCall: PostApiCall,
GetApiCall: GetApiCall
};
}]);
app.controller('NationalityCtrl', ['ApiCall', '$scope', function (ApiCall,$scope) {
$scope.items = [];
var items = populateListFromLocalStorage("offices", "Login/GetOffices", 24);
var officelist = "";
for (var i = 0; i < items.length; i++)
{
$scope.items[i] = { "name": read_prop(items[i], 'office_desc'), "guid": read_prop(items[i], 'office_guid') };
}
$scope.getResult = function ($index, item) {
var result = ApiCall.PostApiCall("Login", "ChangeOffice/?officeID=" + $scope.items[$index].guid + "&officeName="+$scope.items[$index].name).then(function (data) {
$scope.reloadPage();
});
};
}]);
Thanks everyone for the help.

AngularJS $resource Custom Action

I have just started using $resource to retrieve data and I'm having problems implementing a custom action. The standard GET is using an object ID as follows;
pageContentService.get({ pageId: 1 }, function (data) {
vm.pageContent = data.content;
});
I also want to be able to retrieve the same data using a string as follows;
pageContentService.getByPageName({ pageName: "Home" }, function (data) {
vm.pageContent = data.content;
});
My $resource service is;
(function () {
"use strict";
angular
.module("common.services")
.factory("pageContentService", ["$resource", pageContentService]);
function pageContentService($resource) {
return $resource("/api/pageContent/:pageId", null,
{
"getByPageName": { method: "GET", url: "/api/pageContent/:pageName", isArray: false }
});
};
})();
And I have mocked the backend as follows;
(function (undefined) {
"use strict";
var app = angular.module("pageContentServiceMock", ["ngMockE2E"]);
app.run(function ($httpBackend) {
var content = [
{ "pageId": 0, "pageName": "Unknown", "content": "<h1>Page not found</h1>" },
{ "pageId": 1, "pageName": "Home", "content": '<h1>Home Page</h1>' },
{ "pageId": 2, "pageName": "Programs", "content": '<h1>Programs Page</h1>' }
];
var contentUrl = "/api/pageContent";
$httpBackend.whenGET(contentUrl).respond(content);
var contentById = new RegExp(contentUrl + "/[0-9][0-9]*", '');
$httpBackend.whenGET(contentById).respond(function (method, url, data) {
var pageContent = content[0];
var parameters = url.split("/");
var length = parameters.length;
var pageId = parameters[length - 1];
if (pageId > 0) {
for (var i = 0; i < content.length; i++) {
if (content[i].pageId == pageId) {
pageContent = content[i];
break;
}
}
}
return [200, pageContent, {}];
});
var contentByName = new RegExp(contentUrl + "/[a-z][A-Z]*", '');
$httpBackend.whenGET(contentByName).respond(function (method, url, data) {
var pageContent = content[0];
var parameters = url.split("/");
var length = parameters.length;
var pageName = parameters[length - 1];
if (pageName.length > 0) {
for (var i = 0; i < content.length; i++) {
if (content[i].pageName == pageName) {
pageContent = content[i];
break;
}
}
}
return [200, pageContent, {}];
});
});
})();
The code works as expected when using the "pageId" to return the data however it doesn't appear to execute the "getByPageName" action of the service in the latter code.
From my understanding the custom action is used to extend the existing functionality of the standard $resource methods so I presume I am not implementing it correctly.
You can try with something like this:
angular.module('common.services').factory('pageContentService', ['$resource',
function($resource) {
return {
getByPageName: $resource('/api/pageContent/:pageName', {pageName: '#pageName'}, { method: "GET", isArray: false }),
getByPageId: $resource('/api/pageContent/:id', {id: '#id'}, { method: "GET", isArray: false })
};
}
]);
And after injecting pageContentService in a controller you can retrieve your data by:
pageContentService.getByPageName({ pageName: "Home" }, function (data) {
vm.pageContent = data.content;
});
Or:
pageContentService.getByPageId({ id: 1 }, function (data) {
vm.pageContent = data.content;
});
The problem turned out to be in the mocking. I was using;
var contentByName = new RegExp(contentUrl + "/[a-z][A-Z]*", '');
When I should have been using;
var contentByName = new RegExp(contentUrl + "/[a-zA-Z]+", '');
Once I fixed that I could use either;
pageContentService.get({ pageId: 1 }, function (data) {
vm.pageContent = data.content;
});
To retrieve the data using the default "GET" or;
pageContentService.getByPageName({ pageName: "Home" }, function (data) {
vm.pageContent = data.content;
});
To use the custom action. I also tried Michelem's suggestion using;
function pageContentService($resource) {
return {
"getByPageId": $resource('/api/pageContent/:pageId', null, { method: "GET", isArray: false }),
"getByPageName": $resource('/api/pageContent/:pageName', null, { method: "GET", isArray: false })
};
}
But I couldn't get this to work.

$resource service and controller

I have a code like this:
angular.module( 'APPLICATION', ['ngResource'] )
.service( 'Service', ['$resource', function ($resource) {
var Model = $resource(
'/:node/:key.json',
{ node: '#node', key: '#key' },
{
query: {method: 'GET', isArray: false}
}
);
var responseMessage, responseData;
var successHandler = function (resource) {
responseMessage = { success: resource.result.message };
responseData = resource.result.data;
};
var errorHandler = function (resource) {
responseMessage = { error: resource.headers().message };
};
this.model = null;
this.fetch1 = function () {
var result = Model.query( {node: this.model.node} );
return result;
};
this.fetch2 = function () {
var result = Model.query( {node: this.model.node} );
result.$promise.then( successHandler, errorHandler );
return responseData;
};
}] )
.controller( 'Controller', ['$scope', 'Service', function ($scope, Service) {
Service.model = $scope.model;
// works
var response = Service.fetch1();
response.$promise.then( function (response) {
$scope.items = response.result.data;
} );
// doesn't work
$scope.items = Service.fetch2();
}] );
How can I make Service.fetch2 work?
I want to process data inside of the Service, not in the Controller, but I can't figure out how to send the data to the Controller once it's processed...
Since your fetch2 call also is async in nature you cannot do a direct assignment, as the results may not be available at that time. Return a promise from fetch2 as well. Like this
this.fetch2 = function () {
var result = Model.query( {node: this.model.node} );
return result.$promise.then( successHandler, errorHandler );
};
var successHandler = function (resource) {
responseMessage = { success: resource.result.message };
responseData = resource.result.data;
return responseData;
};
var errorHandler = function (resource) {
responseMessage = { error: resource.headers().message };
return responseMessage;
};
In your controller
Service.fetch2().then(function(data) {
$scope.items=data;
});

Resources