Extjs 4 (with a code for 3.4 below) downloading a file returned from a post request - ajax

I have seen questions slightly related to this, but none that answer my problem. I have set up an Ext.Ajax.request as follows:
var paramsStringVar = 'param1=1&param2=two&param3=something&param4=etc';
Ext.Ajax.request({
url: '/cgi-bin/url.pl',
method:'POST',
params:paramsStringVar,
timeout:120000,
success: function(response, opts){
var objhtml = response.responseText; //content returned from server side
console.log(objhtml);
}
});
This request retrieves the appropriate content from the backend. One parameter is outputType, which can take values {html, excel, csv}. When returning html to display I am able to handle and display it correctly. Now on to the problem...
When I set the outputType parameter to csv or excel, I get back the appropriate content as csv or tsv(excel) as requested. BUT, I don't want the content, I want a prompt to download the file(csv or excel). How can I have the browser auto prompt the user to download the file instead of just retrieving the text content within extjs?
Version 4.07 so I can't use any 4.1 only features

There seems to be no bulletproof solution but there are several approaches I would try:
1) Use an iframe instead of real XHR to POST data to the server, e.g. <form action="/something" target="myiframe"> where myiframe is the name of your hidden iframe. That way your form would use the iframe (not your main window) to submit data to the configured URL. Your server should set response header as application/octet-stream (or some ither MIME type for binary data) so the browser triggers download. Otherwise (if html returned in your case) you can just retrieve iframe's body innerHTML and display it to the user in UI. While using an iframe (or a new window) instead of XHR doesn't sound like the best idea, this solution seems to be the most reliable so far (and with best browser support).
Here is a slightly modified example from Ext.form.Basic docs page:
Ext.create('Ext.form.Panel', {
title: 'Basic Form',
renderTo: Ext.getBody(),
width: 350,
// Any configuration items here will be automatically passed along to
// the Ext.form.Basic instance when it gets created.
// *THIS* makes the form use a standard submit mechanism, not XHR
/**/standardSubmit: true,
// URL to submit to
url: 'save-form.php',
items: [{
fieldLabel: 'Field',
xtype: 'textfield',
name: 'theField'
}],
buttons: [{
text: 'Submit',
handler: function() {
// The getForm() method returns the Ext.form.Basic instance:
var form = this.up('form').getForm();
if (form.isValid()) {
// Submit the Ajax request and handle the response
form.submit({
success: function(form, action) {
Ext.Msg.alert('Success', action.result.msg);
},
failure: function(form, action) {
Ext.Msg.alert('Failed', action.result.msg);
},
// You can put the name of your iframe here instead of _blank
// this parameter makes its way to Ext.form.Basic.doAction()
// and further leads to creation of StandardSubmit action instance
/**/ target: '_blank'
});
}
}
}]
});
There are two key parameters here (lines marked with /**/):
standardSubmit: true config that you pass to your form will make it do a standard submit instead of XHR.
Passing a target parameter to the form's submit action. This feature is not documented but you can see it being used in Ext.form.action.Submit source code (all options that you pass to Ext.form.Basic.submit() method end up as parameters of Ext.form.action.* instance.
In the example code I put target: '_blank' to demonstrate that it works right away (will create a new browser window). You can replace it with the name of your iframe later but I suggest that you first test how your form submits data to a regular new window and then develop logic that creates and processes an iframe. You will have to process the result inside iframe yourself, thought. It's not that difficult, see Ext.data.Connection.upload() implementation as an example of iframe processing.
ExtJS actually already uses the iframe technique for file uploads. See Ext.data.Connection and Ext.form.field.Field.isFileUpload() for an idea of how it can work.
2) Suggested here: Using HTML5/Javascript to generate and save a file.
If you don't want to go the iframe way, you can try generate data URI from response data and navigate to that URI triggering download:
content = "Hello world!";
uriContent = "data:application/octet-stream," + encodeURIComponent(content);
window.location.href = uriContent;
Again, mimetype is essential here. This worked for me, you should note, however, that browsers impose a size limit to data URIs (256Kb is a safe bet).
3) Another answer in the mentioned thread links to FileSaver.js library the implements the (abandoned?) w3 spec. Usage and demo here. It uses [BlobBuilder] to generate a blob of binary data that is further used to initialize downloads using one of several methods. While this solution seems to work, it uses deprecated APIs and may not be future-proof.

Below is my solution. This is how I have it currently working. The response generates a download/open prompt, based on a response type of text/csv. Note that no iFrame or reference to an iframe are needed. I spent a lot of time hung up on the need for an iFrame, which actually broke my solution. An iFrame is not needed to generate a download prompt. What is needed is a request(submittal) similar to this one, along with a backend generating the appropriate csv with text/csv response header.
var hiddenForm = Ext.create('Ext.form.Panel', {
title:'hiddenForm',
standardSubmit: true,
url: /cgi-bin/url.pl
timeout: 120000,
height:0,
width: 0,
hidden:true,
items:[
{xtype:'hiddenField', name:'field1', value:'field1Value'},
// additional fields
]
})
hiddenForm.getForm().submit()
The standardSubmit line is vital

You don't need to create a form panel and make it hidden in your extjs file. We can add a html form and on click of button in extjs file we can submit the form using the url. This will work both in IE as well as chrome browsers. Below is my code i tried and its working fine,
<form action="<%=fullURL%>/DownloadServlet.do" method="get" id="downloadForm" name="downloadForm" target="_self">
</form>
click:
{
fn: function()
{
document.getElementById('downloadForm').submit();
}
}

To get it working on ExtJS 3.4:
var hiddenForm = new Ext.FormPanel({
id:'hiddenForm',
region: 'south',
method: 'POST',
url: "/cgi/test.wsgi",
height: 0,
standardSubmit: true,
hidden:true,
items:[
{xtype:'hidden', name:'p', value:p},
{xtype:'hidden', name:'g', value:g},
// ...
],
});
linkThis = new Ext.Button({
text: 'Download this CSV',
handler: function() {
hiddenForm.getForm().submit();
},
maxHeight: 30,
});
Remember that in order to make it working, you should put the hiddenForm in any container (i.e. in the same Ext.Window of the button), for example:
risultatiWindow = new Ext.Window({
title: 'CSV Export',
height: 400,
width: 500,
....
items: [...., hiddenForm]
});

Related

openseadragon background request's accept header

in a Linked Data context, I am pointing my openseadragon client to a resource that will respond with a 303 redirection. If OSD's request has a header "Accept: image/*" (with virtually anything for '*'), the redirection will point to a jpeg and everything is fine. This is the way it works in google chrome.
However, in Firefox the request seems somehow to have a header "Accept: */*". At least that's how I am interpreting the output of firefox's developer's tools' network panel. This leads the resource at the end to redirect to a html page which, of course, OSD cannot render as an image.
How could this happen?
The way I embed OSD in my webpage is like this:
var Imageviewer = OpenSeadragon({
debugMode: false,
id: "contentDiv",
prefixUrl: "resources/img/SeaDragonImages/",
showNavigator: true,
autoHideControls: false,
autoResize: true,
springStiffness: 10,
preserveViewport: true,
// ajaxHeaders: {'Accept: image/jpeg'},
tileSources: null // sources are (re-)loaded when pageNumbers are clicked
});
$(function(){
$(".pageNo").click(function(event){
event.preventDefault();
Imageviewer.open({
type: 'legacy-image-pyramid',
levels: [{ url: $(this).attr('href'), <span data-template="app:scaleImg"/> }]
});
});
});
Note that if I enable the ajaxHeaders: {'Accept: image/jpeg'}, that is presently commented out, I do get an Accept header but it contains all sorts of text/hmtl; application/xhtml and the like.

I can't seem to generate content for dynamic content load

I am using wordpress and php along with ajax to create a random loading of customer reviews on our main page
function loadContent() {
$.ajax({
type: "GET",
url: 'http://skillsetsonline.ssosv.com/contentLoader.php',
data: {
company: 1
},
success: function(data) {
alert(data);
var currReview = document.getElementById('reviewRand');
currReview.innerHTML = data;
}
});
}
setTimeout(loadContent, 10000); // milliseconds, so 10 seconds = 10000ms
<div id="reviewRand" class="elementToFadeInAndOut" style="font-color:#FFF;">Hi how are you</div>
I pasted the ajax command in from a stackoverflow posting that was an accepted answer but may not have it exactly right this does not include the fading CSS code I use but that is working I just need to change the content.
Currently "Hi how are you" fades in every 10 seconds. One thing I have not learned about yet with this ajax command is the
data:{company:1}
I know it simply passes &company=1 to the GET URL but in my case I do not need to send anything and since it should not break anything if it is sent I left it alone not sure if
data:{}
would work and be cleaner
I have verified that the url used does get a random review
formatted like this
I love this program.blah blah.<br>
A USER<br>
A location<br>
June 2016<br>
Each line is formatted in CSS via a class tag
Any ideas would be greatly appreciated
Since the domain you're making the AJAX request to is on a different domain/origin, what you're running in to is a CORS issue. By default, the client will not allow you to update the page with data from an AJAX request that is served on a different origin than the site where the request originated. You can read about making CORS changes here https://enable-cors.org/
A common way around this is to serve the response via JSONP. You can do this in your script at http://skillsetsonline.ssosv.com/contentLoader.php if you have access to change that file. There are also third-party sites that will request that URL for you and create a proxy that serves the response via JSONP, then you can use it on your website.
Here's an example utilizing a JSONP proxy on https://crossorigin.me
function loadContent() {
$.ajax({
type: "GET",
url: 'https://crossorigin.me/http://skillsetsonline.ssosv.com/contentLoader.php',
success: function(data) {
var currReview = document.getElementById('reviewRand');
currReview.classList.add('ready');
currReview.innerHTML = data;
}
});
}
setTimeout(loadContent, 0); /* changed this for the demo */
#reviewRand:not(.ready) {
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="reviewRand"><img src="http://thinkfuture.com/wp-content/uploads/2013/10/loading_spinner.gif"></div>

Error uploading photo to Appcelerator ACS using Trigger.io

I'm trying to upload a photo to Appcelerator Cloud Services (ACS) storage using Trigger.io.
I can't figure out the correct syntax to use for the file object.
I'm getting an error "Error: Syntax error, unrecognized expression: #[object Object]"
Here's my relevant code:
$("#photograph-record").on("click", function(){
forge.file.getImage({source:"camera", width: 280, height: 280},function(file) {
var data = {
photo: file //the ID of file input control
};
sdk.sendRequest('photos/create.json', 'POST', data, callback);
});
});
Here's the Docs for the ACS Photo class - http://cloud.appcelerator.com/docs/api/v1/photos/create
Required Parameters - photo: the attached binary file
Since it needs to be a binary I tried "photo: forge.file.string(file)" (http://docs.trigger.io/en/v1.4/modules/file.html#modules-file), but got an error on the Appcelerator side "Photo parameter required for photo upload".
I have no problem passing the image into my App page views using forge.file.url, so I know there's no problems with the file object, it's just figuring out the correct syntax to pass it as a binary to the sdk.sendRequest call.
Any ideas on what I need to be passing in the data variable to get this to work?
The Appcelerator docs are pretty good here - it looks like they're expecting a POST parameter called photo which contains the binary image data.
To do that using our request module:
$("#photograph-record").on("click", function(){
forge.file.getImage({source:"camera", width: 280, height: 280},function(file) {
file.name = 'photo'; // the magic
forge.request.ajax({
url: 'https://api.cloud.appcelerator.com/v1/photos/create.json',
files: [file],
success: function () { ... },
error: function () { ... }
});
});
});
I don't see a way to use their JS library here, because they're expecting you to pass in the id of a HTML form element to get data from, but we're interacting with the camera or gallery directly...

sencha touch - ajax call to server (url , extra parameter formation)

I am using this to get the result from server
controller.allVisitStore = new Ext.data.Store({
model: 'allVisit',
autoLoad : true,
proxy: {
type: 'ajax',
id: 'allvisit_app_localstore',
url: '/RadMobApp/api',
extraParams:{
action:'query',
queryName:'GET_ALL_VISIT',
authToken: localStorage.getItem("auth_token"),
patTicketId: localStorage.getItem("patientId"),
retFormat:'XML',
keyValuePair:'yes'
},
// the return will be XML, so lets set up a reader
reader: new Ext.data.XmlReader({
// records will have an "T4" tag
record: 'data'
})
}
});
but i am not getting any thing.But i formed this url in browser and checked this i got the correct result. now here i want to check is there any problem in the url formation.How to check the url formation with extra parameter which is pass through ajax. I have checked in Inspect element-> network -> api there is no any api request found there.Is anything wrong in my code. Thanks in advance...
Use Firebug for Firefox or Chrome's developer tools to see what's going on when that store attempts to load itself. My hunch is that your url is incorrect and should be url: '/api' because RadMobApp is probably your app root.

Sencha Error XMLHttpRequest cannot load http://localhost.... Origin null is not allowed by Access-Control-Allow-Origin

I'm having a problem with loading a Sencha store. My store declaration is like this:
Ext.regModel('Car',{
idProperty: 'id',
fields: [
//'id', 'company', 'driver', 'carType','xCoordinate','yCoordinate'
{name: 'id', type: 'int'},
{name:'company', type:'string'} ,
{name:'driver', type:'string'},
{name:'carType', type:'string'},
{name:'xCoordinate', type:'int'},
{name:'yCoordinate', type:'int'}
]
});
var strr= new Ext.regStore({
id:'carStore',
model:'Car', //configuration option is the cars
proxy: {
type: 'ajax',
url: 'http://localhost:8080/A/carStore.html&callback=?',
reader: {
type: 'json',
root: 'carss'
},
},
autoLoad: true
});
And I'm keeping the store in a list:
CarManagementSystem.views.carList = new Ext.List({
id: 'carList',
store: 'carStore',
onItemDisclosure: function (record) {
var selectedCar = record;
CarManagementSystem.views.addNewCar.load(selectedCar);
CarManagementSystem.views.viewport.setActiveItem('addNewCar', { type: 'slide', direction: 'left' });
},
itemTpl: '<div class="list-item-id">{id}</div>' +'<div class="list-item-driver">{driver}</div>'
});
However, when I try to load the list with my JSON file, I get this error:
XMLHttpRequest cannot load http://localhost:8080/A/carStore.html&callback=?&_dc=1311751412006&limit=25. Origin null is not allowed by Access-Control-Allow-Origin.
sencha-touch-debug.js:7212
Uncaught TypeError: Cannot read property 'length' of undefined
I'm keeping my JSON file in an html format and it is kept in the server. I'd appreciate any help.
Thanks!
You are encountering the classic CORS issue.
It's a browser security, called web-security. If you are in dev mode, you can run Chrome and disabling this flag. But if you run your app in production on a browser, you will need to bypass this ajax specification restriction.
To bypass it, you can use proxys (such as creating a back-end script on the same domain will load for you the resource) or you can use JSON-P. For this you will need to change your store to a script tag BUT ALSO you will need the server to be able to detect a callback param and send it to you as a JS function automatically executed when inserted in the DOM.
I will add that you won't have this issue when running in Phonegap because PhoneGap is not running a web server but serves files with the file:// protocol.
If you want to learn more, and I recommand as it is a common pb when developing mobile web apps, you should learn what JSON-P is, what CORS is, and how it works.
You can't hit a port number (8080) when making an AJAX request. If you end up needing a cross-domain request change your proxy type to 'scripttag'.
You should change the proxy type from 'ajax' to 'type: 'scripttag'

Resources