In my Angular / Rails app, I am uploading a spreadsheet using the angular-file-upload gem. https://github.com/danialfarid/angular-file-upload
I pass the spreadsheet to the Rails backend, and it processes the data and returns a JSON array.
The Angular code that passes the file up is this:
$scope.submitForm = function () {
console.log('submit upload2');
return $scope.upload = $upload.upload({
url: '/api/batches/spreadsheet_upload.json',
data: {
id: 99,
assembly: "xy"
},
file: $scope.file
}).success(data, status, headers, config)(function () {
debugger
$scope.selector.tabledata = data;
$scope.uploaded = true;
});
};
Rails accepts the file and processes it. I am trying to return it with render json: {data: spreadsheet_data}
def spreadsheet_upload
Rails.logger.debug("here params: #{params}")
spreadsheet = nil
case File.extname(params[:file].original_filename)
when '.csv' then spreadsheet = Roo::CSV.new(params[:file].path)
when '.xls' then spreadsheet = Roo::Excel.new(params[:file].path, nil, :ignore)
when '.xlsx' then spreadsheet = Roo::Excelx.new(params[:file].path, nil, :ignore)
else raise "Unknown file type: #{file.original_filename}"
end
spreadsheet_header = spreadsheet.row(1)
spreadsheet_data = []
(2..spreadsheet.last_row).each do |i|
row = Hash[[spreadsheet_header, spreadsheet.row(i)].transpose]
spreadsheet_data.push(row)
end
render json: {data: spreadsheet_data}
end
The network tab in Chrome shows me the data has been returned from the Rails server, but Angular doesn't seem to know what to do with it.
ReferenceError: data is not defined
at Object.$scope.submitForm (http://0.0.0.0:3000/assets/controllers/DesignViewCtrl.js?body=1:203:18)
at http://0.0.0.0:3000/assets/lib/angular.min.js?body=1:1936:30
at http://0.0.0.0:3000/assets/lib/angular.min.js?body=1:4127:15
at Object.e.$eval (http://0.0.0.0:3000/assets/lib/angular.min.js?body=1:2432:24)
at Object.e.$apply (http://0.0.0.0:3000/assets/lib/angular.min.js?body=1:2439:40)
at HTMLFormElement.<anonymous> (http://0.0.0.0:3000/assets/lib/angular.min.js?body=1:4126:15)
at HTMLFormElement.jQuery.event.dispatch (http://0.0.0.0:3000/assets/jquery.js?body=1:2940:98)
at HTMLFormElement.elemData.handle (http://0.0.0.0:3000/assets/jquery.js?body=1:2750:123)
Returned data in the network tab
{"data":[{"chrom":4.0,"chrom_start":55593607.0,"chrom_end":55593607.0}, {"chrom":"4","chrom_start":55593609.0,"chrom_end":55593617.0},{"chrom":"6","chrom_start":133105152.0,"chrom_end":133105152.0}]}
I have also tried changing to a response, but this also did not work.
}).success(resp)(function () {
$scope.selector.tabledata = resp.data;
$scope.uploaded = true;
});
Not sure but the structure on the success that i have seen looks like
.success(function(data, status, headers, config) {
$scope.selector.tabledata = data;
$scope.uploaded = true;
});
Related
I have a AngularJS web application, I'm trying to upload a file to a server and while the upload is complete, I have to update ng-grid with the last uploaded file's entry. The following is my grid html,
<div class="gridholder" data-ng-grid="viewmodel.gridOptions">
</div>
The following is my controller logic.
vm.gridOptions = {
data: 'gridData',
enableColumnResize: true,
enablePaging: true,
columnDefs: [
{ field: 'FileName', displayName: 'File Name', width: 250 }
{ field: 'UploadedDate', displayName: 'Uploaded Date'}
],
multiSelect: false,
enableSorting: true,
showFooter: true,
};
The requirement is that I show the progress of file upload and the entire application to be responsive when upload is in progress, I have achieved this but my ng-grid not is updating in a particular scenario.
If I remain in the same page until the file is uploaded and the response comes, the grid is refreshing but when I move to another page of my application and come back to the file upload page, and the response comes after, my grid is not getting refreshed.
This is my file upload js code,
var data = new FormData();
data.append('file', file);
var xhrRequest = Factory.uploadFileRequest('UploadFile');
xhrRequest.upload.addEventListener("progress", progressHandler, false);
xhrRequest.onreadystatechange = function (e) {
};
xhrRequest.onload = function (e) {
if (JSON.parse(e.currentTarget.responseText).Success == true) {
$timeout(function () {
$scope.LoadGrid();
//showing success message here
}, 2000);
}
else
{
//showing error message here
}
};
xhrRequest.onerror = function (e) {
//showing error message here
};
xhrRequest.send(data);
$scope.LoadGrid = function () {
Factory.callGet("Files").then(function (d) {
$scope.gridData = d.data;
}
$scope.totalItems = $scope.gridData.length;
}, function error(err) {
//Error Message
});
}
gridData is my data-ng-grid value. I'm calling my LoadGrid method inside a $timeout already but still the grid is not refreshing with latest data. Any help would be much appreciated.
Possible Problem
You implemented upload logic inside the controller. When you switch to another view, angularjs destroys your controller and therefore no one listens on file upload response.
Possible solution:
1) Use a service (or Factory) kind of singleton to manage upload process there.
For example MyService.upload(data).then(function (response) {/**/});
2) By default MyService.upload(data) returns a promise on a regular basis but also stores the result inside the Service, for example, upload_results:
app.service('MyService',['$q',function ($q) {
var self = this;
var upload_results = [];
self.upload = function (_data) {
return // <YOUR_PROMISE>
.then(function (response) {
upload_results.push({
id: new Date().getTime(),
data: response.data
})
}
, function (error) {
console.error(error);
return $q.reject(error);
});
};
self.getResults() = function(){
return upload_results;
}
self.resetResults() = function(){
upload_results = [];
}
}
When you initialize the controller on start or go back to the previous controller, you ask the service if it has something for you:
var results = MyService.getResults();
if(results.length > 0){
$scope.gridData = results[0].data; // or use timestamp to manage it
MyService.resetResults();
}
Hope it will give you some insight,
I’m having an issue with my project. In my angularjs controller a function is being executed and then my function to make a call to my database to update a record is executing without waiting for the first function to complete and therefore sending over an undefined result variable.
Below you can find my code snippets with my attempts so far.
Submit button function:
$scope.submitNewStarters = function () {
// result is returning as undefined <<<<< Issue
var result = $scope.sendNewStarterDetailsToApi();
$scope.updateArchivedImportFlag(result);
};
Controller function handling the logic:
$scope.sendNewStarterDetailsToApi = function () {
swal({
title: "Confirmation",
text: "Are you sure you want to import the new starter details?",
icon: "info",
dangerMode: true,
buttons: ["No", "Yes"]
}).then(function (approve) {
if (approve) {
// Get each of the new starter details that have been set to true for import.
var newStartsToImport = $scope.tableParams.data.filter(x => x.imported == true);
for (let i = 0; i < newStartsToImport.length; i++) {
// Parses the current new starter object into a stringified object to be sent to the api.
$scope.newStartsToImport = $scope.createApiObject(newStartsToImport[i]);
// A check to ensure that nothing has went wrong and that the stringify object has worked.
if ($scope.newStartsToImport !== "") {
apiFactory.postNewStarterDetailsToApi($scope.newStartsToImport).then(function (response) {
var isSuccessful = response.data.d.WasSuccessful;
if (isSuccessful)
toastr.success("New starter details successfully sent to API.", "Success!");
else {
var errorMessage = response.data.d.ErrorMessage;
toastr.error("New starter details were unsuccessfully sent to API. Please try again. \n" + errorMessage, "Error!");
}
});
}
else {
toastr("An error has occurred when attempting to create the data object to be sent to API. The process has stopped!", "Error!");
break;
}
}
return newStartsToImport;
}
else
toastr.info("No new starter details were sent to API", "Information!");
});
};
Factory function for API call:
postNewStarterDetailsToApi: function (data) {
return $http({
url: "https://www.example.com/services/service.svc/Import",
method: "POST",
data: data,
headers: {
'Content-Type': 'application/json; charset=utf-8',
}
}).then(function successCallbwack(response) {
// this callback will be called asynchronously
// when the response is available
return response;
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
console.log('An error has occured during the function call postNewStarterDetailsToApi(): ', response);
});
}
So with the concept of promises how am I able to execute the sendNewStarterDetailsToApi function, wait for it to complete and then return the populated array? Once the populated array (result) is returned then execute the updateArchivedImportFlag function.
Below I've added an illustration of what I'd like to achieve:
The approach I am using is , save all the promises in an array .
Use any promise library or es6 Promise, and use .all function to wait for all promises to execute
The syntax i wrote is not totally correct. Since you are using angular js , you can use $q.all
$scope.sendNewStarterDetailsToApi = function () {
swal({
title: "Confirmation",
text: "Are you sure you want to import the new starter details?",
icon: "info",
dangerMode: true,
buttons: ["No", "Yes"]
}).then(function (approve) {
var res = [];
if (approve) {
// Get each of the new starter details that have been set to true for import.
var newStartsToImport = $scope.tableParams.data.filter(x => x.imported == true);
for (let i = 0; i < newStartsToImport.length; i++) {
// Parses the current new starter object into a stringified object to be sent to the api.
$scope.newStartsToImport = $scope.createApiObject(newStartsToImport[i]);
// A check to ensure that nothing has went wrong and that the stringify object has worked.
if ($scope.newStartsToImport !== "") {
res.push(apiFactory.postNewStarterDetailsToApi($scope.newStartsToImport))
}
else {
toastr("An error has occurred when attempting to create the data object to be sent to API. The process has stopped!", "Error!");
break;
}
}
return Promise.all(res);
}
else
toastr.info("No new starter details were sent to API", "Information!");
}).then(function (data) {
data.forEach((response) => {
var isSuccessful = response.data.d.WasSuccessful;
if (isSuccessful)
toastr.success("New starter details successfully sent to API.", "Success!");
else {
var errorMessage = response.data.d.ErrorMessage;
toastr.error("New starter details were unsuccessfully sent to API. Please try again. \n" + errorMessage, "Error!");
}
})
}).then((res) => {
//call Submit new starters
})
};
I am trying to use json using API to retrieve data on a Google Map in AngularJS.
This is my code in Angular:
$scope.loadData = function () {
var map_info = {
'ApiKey': '1iVuQy3FGK39d51',
'ProjectId': '11'
};
var url = "http://localhost:63411/api/clientportal/?action=mapjson";
return $http.post(url, map_info).then(function (response) {
return response.data.MapData;
});
};
But, when I run the code, it shown 'Cross-Origin Reuest Block' error.
Then, I search from the internet about this error, I come out with a solution to change $http.post to $http.jsonp.
$scope.loadData = function () {
var map_info = {
'ApiKey': '1iVuQy3FGK39d51',
'ProjectId': '11'
};
var url = "http://localhost:63411/api/clientportal/?action=mapjson";
return $http.jsonp(url, map_info).then(function (response) {
return response.data.MapData;
});
};
It does go to the URL, however the Request Body is empty.
Since I created the API using C#, the 'Request.InputStream' is null.
string jsonPosted = new StreamReader(Request.InputStream).ReadToEnd();
So the 'jsonPosted' is null.
I did try on Postman, and it works. But not in Angular.
How do I send the Request Body properly?
I want the 'ApiKey' and 'ProjectId' to be included on Request Body.
Thank You.
I'm not sure why when i print the json file on a html page works, also from a button calling a function, but not inside of the javascript file.
This a problem because i need to sort the data in the json file before displaying it from in the web page, i can't make it work. i tried using this solution https://stackoverflow.com/a/15463124/2796268,
but the console say
jsonDat is not defined
my code:
$scope.init = function () {
console.log("init");
$http.get('json/file.json') .success(function(data) {
$scope.jsonDat = res.data;
})
.error(function(data,status,error,config){
$scope.jsonDat = [{heading:"Error",description:"Could not load json data"}];
});
console.log(jsonDat);
};
How i can process the json data before the page loads or when the page is loading?
You can process the data when it is returned from $http, like so:
$scope.init = function () {
$http.get('json/file.json').success(function(data) {
//---- SORT HERE ----
$scope.jsonDat = mySortFunction(data);
});
};
Try this :
$scope.init = function() {
console.log("init");
$http.get('json/file.json').success(function(data) {
$scope.jsonDat = data;
console.log($scope.jsonDat);
})
.error(function(data, status, error, config) {
$scope.jsonDat = [{
heading: "Error",
description: "Could not load json data"
}];
console.log($scope.jsonDat);
});
};
In success you have data but you try get from res.data. When you use success then it is not response with data but only your data.
I thought you wanna sort some JSON file then display it in HTML page . So My Idea is get that JSON file (you tried)
$http.get('json/file.json') .success(function(data) {
$scope.jsonDat = data.res;
console.log('Checking the result',angular.toJson($scope.jsonDat));
})
But putting your result in
$scope.jsonDat = data.res;
eg,
sortService.sortJsn = data.res;
$scope.jsonDat instead create angular service pour your data there then you can access those data any where in your controller sort it also show it in HTML.
I'm about to make a web app which will have a pretty heavy client end. I'm not sure about the way to organize my javascript code, but here is a basic idea :
// the namespace for the application
var app = {};
// ajax middle layer
app.products = {
add : function(){
// send ajax request
// if response is successful
// do some ui manipulation
app.ui.products.add( json.data );
},
remove : function(){},
...
};
app.categories = {
add : function(){},
....
};
// the ui interface which will be called based on ajax responses
app.ui = {};
app.ui.products = {
add : function( product_obj ){
$('#products').append( "<div id='"+product_obj.id+"'>"+product_obj.title+"</div>" );
}
};
app.ui.categories = {};
Anybody got similar experiences to tell me the pros and cons of this approach? What's your way of designing client side javascript code architecture? Thanks.
[update] : This web app, as you see from the above, deals with products CRUD, categories CRUD only in a ajax fashion. I'm only showing an snippet here, so you guys know what I'm trying to achieve and what my question is. Again, I'm asking for inputs for my approach to organize the code of this app.
That is similar to the way I do my JavaScript projects. Here are some tricks I have used:
Create one file for each singleton object. In your code, store ajax, middle layer and ui interface in separate files
Create a global singleton object for the 3 layers usually in the project; GUI, Backend and App
Never use pure ajax from anywhere outside the Backend object. Store the URL to the serverside page in the Backend object and create one function that uses that URL to contact the server.
Have a JSON class on the server that can report errors and exceptions to the client. In the Backend object, check if the returned JSON object contains an error, and call the serverError function in the GUI class to present the error to the user (or developer).
Here is an example of a Backend object:
var Backend = {};
Backend.url = "/ajax/myApp.php";
Backend.postJSON = function(data, callback){
var json = JSON.stringify(data);
$.ajax({
type: "POST",
url: Backend.url,
data: "json="+json,
dataType: "json",
success: function(response){
if(response){
if(response.task){
return callback(response);
}else if(response.error){
return Backend.error(response);
}
}
return Backend.error(response);
},
error: function(response){
Backend.error({error:"network error", message:response.responseText});
},
});
};
Backend.error = function(error){
if(error.message){
Client.showError(error.message, error.file, error.line, error.trace);
}
};
This can be improved by storing the ajax object somewher in the Backend object, but it's not necessary.
When you build something non trivial, encapsulation is important to make things maintainable in long run. For example, JS UI is not just simple JS methods. A UI components consists of css, template, logic, localization, assets(images, etc).
It is same for a product module, it may need its own settings, event bus, routing. It is important to do some basic architectural code in integrating your chosen set of libraries. This had been a challenge for me when I started large scale JS development. I compiled some best practices in to a reference architecture at http://boilerplatejs.org for someone to use the experience I gained.
For client-side ajax handling I have a URL object that contains all my urls and than I have an ajax object that handles the ajax. This is not a centric approach.In my case I have I have different urls handling different tasks. I also pass a callback function to be executed into the ajax object as well.
var controller_wrapper = {
controller: {
domain: "MYDOMAIN.com",
assets: "/assets",
prefix: "",
api: {
domainer: "http://domai.nr/api/json/info",
tk_check: "https://api.domainshare.tk/availability_check"
},
"perpage": "/listings/ajax",
"save_image": "/members/saveImage",
"update": "/members/update",
"check_domain": "/registrar/domaincheck",
"add_domain": "/registrar/domainadd",
"delete_listing": "/members/deactivateProfile",
"save_listing": "/members/saveProfile",
"get_images": "/images/get",
"delete_image": "/images/delete",
"load_listing": "/members/getProfile",
"load_listings": "/members/getListings",
"loggedin": "/members/loggedin",
"login": "/members/login",
"add_listing": "/members/add",
"remove": "/members/remove",
"get": "/members/get",
"add_comment": "/members/addComment",
"load_status": "/api/loadStatus"
}
}
var common = {
pager: 1,
page: 0,
data: {
saved: {},
save: function (k, v) {
this.saved[k] = v;
}
},
ajax: {
callback: '',
type: 'POST',
url: '',
dataType: '',
data: {},
add: function (k, val) {
this.data[k] = val;
},
clear: function () {
this.data = {};
},
send: function () {
var ret;
$.ajax({
type: this.type,
url: this.url,
data: this.data,
dataType: this.dataType !== '' ? this.dataType : "json",
success: function (msg) {
if (common.ajax.callback !== '') {
ret = msg;
common.ajax.callback(ret);
} else {
ret = msg;
return ret;
}
return;
},
error: function (response) {
console.log(response);
alert("Error");
}
})
}
}
var callback = function (results) {
console.log(results
}
common.ajax.callback = callback;
common.ajax.type = "jsonp";
common.ajax.type = "POST";
common.ajax.url = controller_wrapper.controller.perpage;
common.ajax.add("offset", common.page);
common.ajax.add("type", $("select[name='query[type]']").val());
common.ajax.add("max", $("select[name='query[max]']").val());
common.ajax.add("min", $("select[name='query[min]']").val());
common.ajax.add("bedrooms", $("select[name='query[bedrooms]']").val());
common.ajax.add("sort", $("select[name='query[sort]']").val());
common.ajax.send();