How to limit the number of dropzone.js files uploaded? - file

Depending on the use case, how do I constrain the number of files that dropzone.js will allow?
For example, I might need to only allow 1, 2, or 4 files uploaded.
It's not uploadMultiple. Unfortunately, uploadMultiple only applies to the number of files handled per request.

I achieved this a slightly different way. I just remove the old dropped file any time a new file is added. It acts as overwriting the file which was the user experience I was going for here.
Dropzone.options.myAwesomeDropzone = {
accept: function(file, done) {
console.log("uploaded");
done();
},
init: function() {
this.on("addedfile", function() {
if (this.files[1]!=null){
this.removeFile(this.files[0]);
}
});
}
};

Nowell pointed it out that this has been addressed as of August 6th, 2013. A working example using this form might be:
<form class="dropzone" id="my-awesome-dropzone"></form>
You could use this JavaScript:
Dropzone.options.myAwesomeDropzone = {
maxFiles: 1,
accept: function(file, done) {
console.log("uploaded");
done();
},
init: function() {
this.on("maxfilesexceeded", function(file){
alert("No more files please!");
});
}
};
The dropzone element even gets a special style, so you can do things like:
<style>
.dz-max-files-reached {background-color: red};
</style>

I thought that the most intuitive single file upload process was to replace the previous file upon a new entry.
$(".drop-image").dropzone({
url: '/cart?upload-engraving=true',
maxFiles: 1,
maxfilesexceeded: function(file) {
this.removeAllFiles();
this.addFile(file);
}
})

maxFiles: 1 does the job but if you also want to remove the additional files you can use this sample code taken from the Wiki page:
How can I limit the number of files?
You're in luck! Starting with 3.7.0 Dropzone supports the maxFiles
option. Simply set it to the desired quantity and you're good to go.
If you don't want the rejected files to be viewed, simply register for
the maxfilesexceeded event, and remove the file immediately:
myDropzone.on("maxfilesexceeded", function(file)
{
this.removeFile(file);
});

Alternative solution that worked really well for me:
init: function() {
this.on("addedfile", function(event) {
while (this.files.length > this.options.maxFiles) {
this.removeFile(this.files[0]);
}
});
}

You can limit the number of files uploaded by changing in dropezone.js
Dropzone.prototype.defaultOptions = {
maxFiles: 10,
}

Set maxFiles Count: maxFiles: 1
In maxfilesexceeded event, clear all files and add a new file:
event: Called for each file that has been rejected because the number
of files exceeds the maxFiles limit.
var myDropzone = new Dropzone("div#yourDropzoneID", { url: "/file/post",
uploadMultiple: false, maxFiles: 1 });
myDropzone.on("maxfilesexceeded", function (file) {
myDropzone.removeAllFiles();
myDropzone.addFile(file);
});

it looks like maxFiles is the parameter you are looking for.
https://github.com/enyo/dropzone/blob/master/src/dropzone.coffee#L667

Why do not you just use CSS to disable the click event. When max files is reached, Dropzone will automatically add a class of dz-max-files-reached.
Use css to disable click on dropzone:
.dz-max-files-reached {
pointer-events: none;
cursor: default;
}
Credit: this answer

The problem with the solutions provided is that you can only upload 1 file ever. In my case I needed to upload only 1 file at a time (on click or on drop).
This was my solution..
Dropzone.options.myDropzone = {
maxFiles: 2,
init: function() {
this.handleFiles = function(files) {
var file, _i, _len, _results;
_results = [];
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
_results.push(this.addFile(file));
// Make sure we don't handle more files than requested
if (this.options.maxFiles != null && this.options.maxFiles > 0 && _i >= (this.options.maxFiles - 1)) {
break;
}
}
return _results;
};
this._addFilesFromItems = function(items) {
var entry, item, _i, _len, _results;
_results = [];
for (_i = 0, _len = items.length; _i < _len; _i++) {
item = items[_i];
if ((item.webkitGetAsEntry != null) && (entry = item.webkitGetAsEntry())) {
if (entry.isFile) {
_results.push(this.addFile(item.getAsFile()));
} else if (entry.isDirectory) {
_results.push(this._addFilesFromDirectory(entry, entry.name));
} else {
_results.push(void 0);
}
} else if (item.getAsFile != null) {
if ((item.kind == null) || item.kind === "file") {
_results.push(this.addFile(item.getAsFile()));
} else {
_results.push(void 0);
}
} else {
_results.push(void 0);
}
// Make sure we don't handle more files than requested
if (this.options.maxFiles != null && this.options.maxFiles > 0 && _i >= (this.options.maxFiles - 1)) {
break;
}
}
return _results;
};
}
};
Hope this helps ;)

I'd like to point out. maybe this just happens to me, HOWEVER, when I use this.removeAllFiles() in dropzone, it fires the event COMPLETE and this blows, what I did was check if the fileData was empty or not so I could actually submit the form.

You can also add in callbacks - here I'm using Dropzone for Angular
dzCallbacks = {
'addedfile' : function(file){
$scope.btSend = false;
$scope.form.logoFile = file;
},
'success' : function(file, xhr){
$scope.btSend = true;
console.log(file, xhr);
},
'maxfilesexceeded': function(file) {
$timeout(function() {
file._removeLink.click();
}, 2000);
}
}

Dropzone.options.dpzSingleFile = {
paramName: "file", // The name that will be used to transfer the file
maxFiles: 1,
init: function() {
this.on("maxfilesexceeded", function(file) {
this.removeAllFiles();
this.addFile(file);
});
}
};

Related

Qooxdoo Remote table getRowCount() return 0

qx.Class.define("webApp.backendjs.tables.RegionesModel", {
extend: qx.ui.table.model.Remote,
members: {
_loadRowCount: function () {
var params = {};
params.action = "getCount";
var rpc = new qx.io.remote.Rpc("http://qx.alpali.cl/svc/svc.php");
rpc.setProtocol("2.0");
rpc.setCrossDomain(true);
rpc.callAsync(qx.lang.Function.bind(this._onRowCountCompleted, this), "regiones.regiones.getNominaRegiones", params);
},
_onRowCountCompleted: function (result, exc) {
if (result !== null) {
this._onRowCountLoaded(result.count);
}
},
_loadRowData: function (firstRow, lastRow) {
var params = {};
params.action = "getData";
var rpc = new qx.io.remote.Rpc("http://qx.alpali.cl/svc/svc.php");
rpc.setProtocol("2.0");
rpc.setCrossDomain(true);
rpc.callAsync(qx.lang.Function.bind(this._onLoadRowDataCompleted, this), "regiones.regiones.getNominaRegiones", params);
},
_onLoadRowDataCompleted: function (result, exc) {
if (result !== null) {
this._onRowDataLoaded(result);
}
}
}
});
var RTRegionesModel = new webApp.backendjs.tables.RegionesModel();
RTRegionesModel.setColumns(["ID", "Cè´¸digo", "Nombre"], ["id", "region_id", "region_nombre"]);
var TableRegiones = new qx.ui.table.Table(RTRegionesModel);
TableRegiones.setTableModel(RTRegionesModel);
// THIS don't work, return 0
TableRegiones.addListener('appear', function () {
console.log("RTRegionesModel.getRowCount(): %s", RTRegionesModel.getRowCount());
}, RTRegionesModel);
// THIS don't work, return 0
TableRegiones.addListener('appear', function () {
console.log("RTRegionesModel.getRowCount(): %s", RTRegionesModel.getRowCount());
}, this);
this.getRoot().add(TableRegiones);
var button1 = new qx.ui.form.Button("How many record...", "icon/22/apps/internet-web-browser.png");
this.getRoot().add(button1,{right:50,top:50});
// this is ok, return teh value
button1.addListener("execute", function(e) {
console.log("RTRegionesModel.getRowCount(): %s", RTRegionesModel.getRowCount());
});
url for testing playground
i need the valor when remote table is loaded
what is the problem..???
thank.
PD: sorry for my bad and ugly english, my native language is spanish (chile), my best friend in this moment is googol
At the time that you are looking for the row count with your "THIS don't work" comment, the row count is not yet available because the network operation to retrieve the row count from the server has not yet been issued.
You probably want to be listening for the model's dataChanged event which is fired when a row count is loaded, or when the model data changes, such as this:
TableRegiones.getTableModel().addListener(
'dataChanged',
function ()
{
console.log(
"dataChanged: RTRegionesModel.getRowCount(): %s",
RTRegionesModel.getRowCount());
},
RTRegionesModel);

Nodejs async data duplication

I'm having some problems with one async process on nodejs.
I'm getting some data from a remote JSON and adding it in my array, this JSON have some duplicated values, and I need check if it already exists on my array before add it to avoid data duplication.
My problem is when I start the loop between the JSON values, the loop call the next value before the latest one be process be finished, so, my array is filled with duplicated data instead of maintain only one item per type.
Look my current code:
BookRegistration.prototype.process_new_books_list = function(data, callback) {
var i = 0,
self = this;
_.each(data, function(book) {
i++;
console.log('\n\n ------------------------------------------------------------ \n\n');
console.log('BOOK: ' + book.volumeInfo.title);
self.process_author(book, function() { console.log('in author'); });
console.log('\n\n ------------------------------------------------------------');
if(i == data.length) callback();
})
}
BookRegistration.prototype.process_author = function(book, callback) {
if(book.volumeInfo.authors) {
var author = { name: book.volumeInfo.authors[0].toLowerCase() };
if(!this.in_array(this.authors, author)) {
this.authors.push(author);
callback();
}
}
}
BookRegistration.prototype.in_array = function(list, obj) {
for(i in list) { if(list[i] === obj) return true; }
return false;
}
The result is:
[{name: author1 }, {name: author2}, {name: author1}]
And I need:
[{name: author1 }, {name: author2}]
UPDATED:
The solution suggested by #Zub works fine with arrays, but not with sequelize and mysql database.
When I try to save my authors list on the database, the data is duplicated, because the system started to save another array element before finish to save the last one.
What is the correct pattern on this case?
My code using database is:
BookRegistration.prototype.process_author = function(book, callback) {
if(book.volumeInfo.authors) {
var author = { name: book.volumeInfo.authors[0].toLowerCase() };
var self = this;
models.Author.count({ where: { name: book.volumeInfo.authors[0].toLowerCase() }}).success(function(count) {
if(count < 1) {
models.Author.create(author).success(function(author) {
console.log('SALVANDO AUTHOR');
self.process_publisher({ book:book, author:author }, callback);
});
} else {
models.Author.find({where: { name: book.volumeInfo.authors[0].toLowerCase() }}).success(function(author) {
console.log('FIND AUTHOR');
self.process_publisher({ book:book, author:author }, callback);
});
}
});
// if(!this.in_array(this.authors, 'name', author)) {
// this.authors.push(author);
// console.log('AQUI NO AUTHOR');
// this.process_publisher(book, callback);
// }
}
}
How can I avoid data duplication in an async process?
This is because you are comparing different objects and result is always false.
Just for experiment type in the console:
var obj1 = {a:1};
var obj2 = {a:1};
obj1 == obj2; //false
When comparing objects (as well as arrays) it only results true when obj1 links to obj2:
var obj1 = {a:1};
var obj2 = obj1;
obj1 == obj2; //true
Since you create new author objects in each process_author call you always get false when comparing.
In your case the solution would be to compare name property for each book:
BookRegistration.prototype.in_array = function(list, obj) {
for(i in list) { if(list[i].name === obj.name) return true; }
return false;
}
EDIT (related to your comment question):
I would rewrite process_new_books_list method as follows:
BookRegistration.prototype.process_new_books_list = function(data, callback) {
var i = 0,
self = this;
(function nextBook() {
var book = data[i];
if (!book) {
callback();
return;
}
self.process_author(book, function() {
i++;
nextBook();
});
})();
}
In this case next process_author is being called not immediately (like with _.each), but after callback is executed, so you have consequence in your program.
Not sure is this works though.
Sorry for my English, I'm not a native English speaker

Get Count of selected files in Telerik Upload

I need to get a count of all selected files using Telerik MVC Upload Control. Can anyone tell me how can i do that. I tried code like this.
var count = e.files.length;
But its counting always as one.
try this code
[function onSelect(e) {
var selectedFiles = e.files.length;
totalFiles += selectedFiles;
}
function UploadRemove(e) {
totalFiles--;
if (totalFiles > 0) {
// Write true block code here.
}
else if (totalFiles == 0) {
// Write your false block code here.
}
}]
You could do this in your upload event handler:
upload: function (e) {
// File Count:
var fileCount = this.wrapper.find(".k-file").length;
// File Index:
var uid = e.files[0].uid;
var file = this.wrapper.find(".k-file[data-uid='" + uid + "']");
var fileIndex = file.index();
}

Backbone. Form with file upload, how to handle?

I want to organize the workflow only through the REST API. I have a form that allows to upload image (enctype="multipart/form-data"). How do I handle this form via backbone? Help me please, how I can to serialize it into JSON with a file field.
Thanks.
Vitaliy
If you are using HTML5, you can use the readAsDataURL method from the file api to read and store it on your models.
Here's the code i use to read and store.
var Image = Backbone.Model.extend({
readFile: function(file) {
var reader = new FileReader();
// closure to capture the file information.
reader.onload = (function(theFile,that) {
return function(e) {
//set model
that.set({filename: theFile.name, data: e.target.result});
};
})(file,this);
// Read in the image file as a data URL.
reader.readAsDataURL(file);
}
});
You could try the jquery.iframe.transport plugin.
IMHO, you cannot serialize a file into JSON.
If you need to send some data along with the file you can send them as query params with POST method.
www.example.com/upload?param1=value1&param2=value2
There's no good way to submit a file via AJAX. So I wrote a function to fake it--it inserts a secret iframe into your DOM that is never visible but still works as a target to submit your form on, and it installs a function for your response to call that cleans house when the file is uploaded.
Have your upload form's submit button fire this function I wrote. It uses jQuery because it's easy and nice, but in principle that shouldn't be strictly necessary:
function backgroundUpload(form, container) {
$(container).append('<iframe name="targetFrame" id="targetFrame" style="display: none; height: 0px; width:0px;" ></iframe>');
$(form).attr('target', 'targetFrame');
window.backgroundUploadComplete = function() {
//clear your form:
$(form).find(':file').val('');
$(form).find(':text').val('');
//do whatever you do to reload your screenful of data (I'm in Backbone.js, so:)
window.Docs.fetch().complete( function() { populateDocs(); });
//get rid of the target iframe
$('#targetFrame').remove();
};
$(form).submit();
}
Then have your form handler that does your file parsing and saving return the string:
<script>window.parent.backgroundUploadComplete();</script>
Your form can look like:
<form id="uploadForm" method="POST" action="/your/form/processor/url" enctype="multipart/form-data">
<input type="file" name="file"/>
<!-- and other fields as needed -->
<input type="button" onClick="backgroundUpload(this.form, $('#documents'));" value="Upload" />
</form>
(#documents is the div that this form lives in. Could probably be any DOM element, it just needs a home.)
events : {
"click #uploadDocument" : "showUploadDocumentDetails",
"change #documents" : "documentsSelected",
"click .cancel-document" : "cancelDocument"
},
showUploadDocumentDetails : function(event) {
$('#id-gen-form').attr("enctype","multipart/form-data");
$('#id-gen-form').attr("action",this.model.url);
var config = {
support : "image/jpg,image/png,image/bmp,image/jpeg,image/gif", // Valid file formats
form: "id-gen-form", // Form ID
dragArea: "dragAndDropFiles", // Upload Area ID
uploadUrl: this.model.url // Server side upload url
};
initMultiUploader(config);
if($('#uploadDocument').attr("checked")){
$('#id-documentCategory-div').show();
$('#id-documentName-div').show();
this.model.set({"uploadDocument": "YES"},{silent: true});
}
else{
$('#id-documentCategory-div').hide();
$('#id-documentName-div').hide();
this.model.set({"uploadDocument": "NO"},{silent: true});
}
},
cancelDocument : function(event) {
var targ;
if (!event) event = window.event;
if (event.target) targ = event.target;
else if (event.srcElement) targ = event.srcElement;
$('#' + event.target.id).parent().parent().remove();
var documentDetails = this.model.get("documentDetails");
documentDetails = _.without(documentDetails, _(documentDetails).find(function(x) {return x.seqNum == event.target.id;}));
this.model.set({
"documentDetails" : documentDetails
}, {
silent : true
});
},
documentsSelected : function(event) {
/*var targ;
if (!event) event = window.event;
if (event.target) targ = event.target;
else if (event.srcElement) targ = event.srcElement;
if (targ.nodeType == 3) // defeat Safari bug
targ = targ.parentNode;
var files = event.target.files; // FileList object
var html = [];
var documentDetails = [];
$(".files").html(html.join(''));
var _this = this;
_this.model.set({
"documentDetails" : documentDetails
}, {
silent : true
});
var seqNum = 0;
for(var i=0; i< files.length; i++){
(function(file) {
html.push("<tr class='template-upload' style='font-size: 10px;'>");
html.push("<td class='name'><span>"+file.name+"</span></td>");
html.push("<td class='size'><span>"+file.size+" KB <br/>"+file.type+"</span></td>");
//html.push("<td><div class='progress progress-success progress-striped active'style='width: 100px;' role='progressbar' aria-valuemin='0' aria-valuemax='100' aria-valuenow='0'><div class='bar' style='width:0%;'></div></div></td>");
if(LNS.MyesqNG.isMimeTypeSupported(file.type)){
if(!LNS.MyesqNG.isFileSizeExceeded(file.size)){
html.push("<td class='error' colspan='2'></td>");
var reader = new FileReader();
console.log(reader);
reader.onload = function(e) {
var targ;
if (!e) e = window.event;
if (e.target) targ = e.target;
else if (e.srcElement) targ = e.srcElement;
if (targ.nodeType == 3) // defeat Safari bug
targ = targ.parentNode;
console.log(e.target.result);
var content = e.target.result;
var document = new Object();
document.name = file.name;
document.type = file.type;
document.content = content;
document.seqNum = "document"+seqNum;
seqNum++;
documentDetails.push(document);
// _this.model.set({"documentDetails" : documentDetails},{silent:true});
};
reader.readAsDataURL(file, "UTF-8");
}else{
seqNum++;
html.push("<td class='error' colspan='2'><span class='label label-important'>Error</span> Too long</td>");
}
}else{
seqNum++;
html.push("<td class='error' colspan='2'><span class='label label-important'>Error</span> Not suported</td>");
}
html.push("<td><a id='document"+i+"' class='btn btn-warning btn-mini cancel-document'>Cancel</a></td>");
html.push("</tr>");
})(files[i]);
}
$(".files").html(html.join(''));*/
}
LNS.MyesqNG.isMimeTypeSupported = function(mimeType){
var mimeTypes = ['text/plain','application/zip','application/x-rar-compressed','application/pdf'];
if($.inArray(mimeType.toLowerCase(), mimeTypes) == -1) {
return false;
}else{
return true;
}
};
LNS.MyesqNG.isFileSizeExceeded = function(fileSize) {
var size = 2000000000000000000000000000;
if(Number(fileSize) > Number(size)){
return true;
}else{
return false;
}
};
Use this, it can work but not more than 5 MB file
Based on Anthony answer (https://stackoverflow.com/a/10916733/2750451), I've written a solution in coffeescript based on a defer object.
readFile: (file) =>
def = $.Deferred()
reader = new FileReader()
reader.onload = (ev) =>
def.resolve
name: file.name
binary: ev.target.result
reader.onerror = ->
def.reject()
reader.readAsDataURL(file)
def.promise()
Then, you could use it this way
readFile(file)
.done (parsedFile) =>
# do whatever you want with parsedFile
#model.set
image_name: parsedFile.name
image: parsedFile.binary
#model.save
.fail ->
console.log "readFile has failed"
To handle it on the server side (because it's Base64 encoded), here the solution in RoR (based on https://stackoverflow.com/a/16310953/2750451)
my_object.image = decode_image(params[:image])
my_object.image.name = params[:image_name]
def decode_image(encoded_file)
require 'base64'
image_data_string = split_base64(encoded_file)[:data]
Base64.decode64(image_data_string)
end
def split_base64(uri)
if uri.match(%r{^data:(.*?);(.*?),(.*)$})
return {
type: $1, # "image/png"
encoder: $2, # "base64"
data: $3, # data string
extension: $1.split('/')[1] # "png"
}
end
end
To ellaborate on Anthony Chua's answer. You need to add Image handling to Backbone.Form.editors like
Backbone.Form.editors.Image = Backbone.Form.editors.Text.extend({
tagName: 'div',
events: {
'change input[type=file]': 'uploadFile',
'click .remove': 'removeFile'
},
initialize: function(options) {
_.bindAll(this, 'filepickerSuccess', 'filepickerError', 'filepickerProgress');
Backbone.Form.editors.Text.prototype.initialize.call(this, options);
this.$input = $('<input type="hidden" name="'+this.key+'" />');
this.$uploadInput = $('<input type="file" name="'+this.key+'" accept="image/*" />');
this.$loader = $('<p class="upload-status"><span class="loader"></span> please wait..</p>');
this.$error = $('<p class="upload-error error">Error</p>');
this.$list = $('<ul class="file-list">');
},
// return an array of file dicts
getValue: function() {
var val = this.$input.val();
return (val ? JSON.parse(val) : [])[0].value;
},
setValue: function(value) {
var str, files = value;
if (_(value).isObject()) {
str = JSON.stringify(value);
} else {
files = value ? JSON.parse(value) : [];
}
this.$input.val(str);
this.updateList(files);
},
render: function(options) {
Backbone.Form.editors.Text.prototype.render.apply(this, arguments);
this.$el.append(this.$input);
this.$el.append(this.$uploadInput);
this.$el.append(this.$loader.hide());
this.$el.append(this.$error.hide());
this.$el.append(this.$list);
return this;
},
uploadFile: function() {
var fileInput = this.$uploadInput.get(0);
var fileObj = fileInput.files[0]
var reader = new FileReader();
var that = this;
// closure to capture the file information.
reader.onload = function(file){
var dataURL = reader.result;
var fileValue = {
value: dataURL,
name: fileObj.name,
content_type: fileObj.type
}
that.filepickerSuccess(fileValue);
};
// Read in the image file as a data URL.
reader.readAsDataURL(fileObj);
},
filepickerSuccess: function(files) {
console.log('File (raw)', files);
this.$loader.hide();
this.$error.hide();
this.$uploadInput.val('');
// when uploading one file, it returns just an object
if (!_(files).isArray()) { files = [files]; }
// turn response array into a flatter array of objects
var newFiles = _(files).map(function(file, index) {
return {
url: "#",
value: file.value,
filename: file.name,
key: index,
content_type: file.type
};
});
console.log('File (processed)', newFiles);
this.setValue(newFiles);
},
filepickerError: function(msg) {
console.debug('File error', msg);
this.$loader.hide();
this.$error.show();
},
filepickerProgress: function(percent) {
this.$loader.show();
this.$error.hide();
},
updateList: function(files) {
// this code is currently duplicated as a handlebar helper (I wanted to let this
// backbone-forms field stand on its own)
this.$list.empty();
_(files).each(function(file) {
var a = $('<a>', {
target: '_blank',
href: file.url,
text: file.filename + ' (' + file.content_type + ') '
});
var li = $('<li>').append(a);
li.append(a, ' ', $('<i class="icon-remove"></i>').data('key', file.key));
this.$list.append(li);
}, this);
this.$list[files.length ? 'show' : 'hide']();
},
removeFile: function(ev) {
if (ev) ev.preventDefault();
var files = this.getValue();
this.setValue([]);
}
});
You can use above code as follows
var ImgSlot = Backbone.Model.extend({
defaults: {
},
schema: {
imageField: {
type: "Image"
}
}
})
Render form using:
this.form = new Backbone.Form({
model: new ImgSlot(),
submitButton: "Example Image file input handling"
}).render();
var errors = that.form.commit({validate: true})
if(errors != null)
{
return false;
}
var data = that.form.model.attributes;
console.debug(data.imageField); // Will return base64 of image selected.
It is not possible to submit a file over AJAX before HTML5 (including IE9).
You need to sync the model attributes over ajax, and then send another html form post with the file, and then sync them up somehow. Generally, save the model over ajax, get an id back, add the id to the other form, and then post the file.
The jQuery plug in "jquery.form" may help you to construct a form to post the file. It manages the "hidden iframe trick" so that it looks like AJAX to the end user.
You might just need to spend some time googling "hidden iframe trick" ...

Loading content dynamically (panels) in an Ext Js Viewport

Well basically im looking on this problem, i have many components with dinamic stuff that is written in the server side with PHP.
Depending on the user my components will change, based on the role of the user.
So i need to know any ways/examples/info on how to do this.
1- I used the load function EXTJS has, but it clearly says i wont load script only plain text.
2- i used eval() but im a bit scared o this approach, like this example crate layout component (static)
var contentPanel = new Ext.Panel({
frame: true,
style: {marginTop: '10px'},
height: 315,
border: true,
bodyBorder: false,
layout: 'fit',
id: 'contentPanel'
});
var mainPanel = new Ext.Panel({
title: 'Panel Principal',
id: 'mainPanel',
border: true,
frame: true,
width: '50%',
style: {margin: '50px auto 0 auto'},
height: 400,
renderTo: Ext.getBody(),
items: [
{
html: 'Panel 1'
},
{
html: 'Panel 2'
},
contentPanel
]
})
and update the content of the layout with js files written on the server
function receiveContent(options, success, response)
{
var respuesta = response.responseText;
//console.log(respuesta);
eval(respuesta);
//console.log(options.url);
url = options.url;
url = url.substring(0,(url.search(/(\.)/)));
var contenedor = Ext.getCmp('contentPanel');
contenedor.removeAll();
var contenido = Ext.getCmp(url);
contenedor.add(contenido);
contenedor.doLayout();
}
function requestContent(panel)
{
//panel es el nombre del archivo que quiero
Ext.Ajax.request({
url: panel+'.js',
callback: receiveContent
});
}
any other way for this to be done, what i DONT want to do is making a million different components and load them ALL at login time like many people seem to say
To address your questions:
The .load method WILL load script and evaluate it once the content has finished loading, however to accomplish this you will need to set the scripts:true option, an example may be:
my_panel.load({
url: 'url_to_load.php/hmt/html/asp...',
params: {param1: param1value, param2: param2value...etc},
nocache: true,
timeout: 30,
scripts: true
});
Using eval() is fine...but seeing as the scripts:true config option above accomplishes this for javascript in the source file, you shouldnt need to use this.
Hope this helps
You might load JavaScript dynamically using something like like below - there are a hundred variations on the web. In this way, you would avoid the AJAX call and handling the response (and subsequent eval).
var aHeadNode = document.getElementById('head')[0];
var aScript = document.createElement('script');
aScript.type = 'text/javascript';
aScript.src = "someFile.js";
aHeadNode.appendChild(oScript);
What I understood from your question is that, you are looking for dynamic JS file loader with a callback handler i.e. the callback function will be called only when the file is loaded fully. I also faced similar problems at start and after searching a lot and doing some research, I developed the following code, it provides absolute Dynamic JS and CSS file loading functionality :
Class ScriptLoader: (Put it in a separate file and load it at first)
ScriptLoader = function() {
this.timeout = 30;
this.scripts = [];
this.disableCaching = false;
};
ScriptLoader.prototype = {
processSuccess : function(response) {
this.scripts[response.argument.url] = true;
window.execScript ? window.execScript(response.responseText) : window
.eval(response.responseText);
if (response.argument.options.scripts.length == 0) {
}
if (typeof response.argument.callback == 'function') {
response.argument.callback.call(response.argument.scope);
}
},
processFailure : function(response) {
Ext.MessageBox.show({
title : 'Application Error',
msg : 'Script library could not be loaded.',
closable : false,
icon : Ext.MessageBox.ERROR,
minWidth : 200
});
setTimeout(function() {
Ext.MessageBox.hide();
}, 3000);
},
load : function(url, callback) {
var cfg, callerScope;
if (typeof url == 'object') { // must be config object
cfg = url;
url = cfg.url;
callback = callback || cfg.callback;
callerScope = cfg.scope;
if (typeof cfg.timeout != 'undefined') {
this.timeout = cfg.timeout;
}
if (typeof cfg.disableCaching != 'undefined') {
this.disableCaching = cfg.disableCaching;
}
}
if (this.scripts[url]) {
if (typeof callback == 'function') {
callback.call(callerScope || window);
}
return null;
}
Ext.Ajax.request({
url : url,
success : this.processSuccess,
failure : this.processFailure,
scope : this,
timeout : (this.timeout * 1000),
disableCaching : this.disableCaching,
argument : {
'url' : url,
'scope' : callerScope || window,
'callback' : callback,
'options' : cfg
}
});
}
};
ScriptLoaderMgr = function() {
this.loader = new ScriptLoader();
this.load = function(o) {
if (!Ext.isArray(o.scripts)) {
o.scripts = [o.scripts];
}
o.url = o.scripts.shift();
if (o.scripts.length == 0) {
this.loader.load(o);
} else {
o.scope = this;
this.loader.load(o, function() {
this.load(o);
});
}
};
this.loadCss = function(scripts) {
var id = '';
var file;
if (!Ext.isArray(scripts)) {
scripts = [scripts];
}
for (var i = 0; i < scripts.length; i++) {
file = scripts[i];
id = '' + Math.floor(Math.random() * 100);
Ext.util.CSS.createStyleSheet('', id);
Ext.util.CSS.swapStyleSheet(id, file);
}
};
this.addAsScript = function(o) {
var count = 0;
var script;
var files = o.scripts;
if (!Ext.isArray(files)) {
files = [files];
}
var head = document.getElementsByTagName('head')[0];
Ext.each(files, function(file) {
script = document.createElement('script');
script.type = 'text/javascript';
if (Ext.isFunction(o.callback)) {
script.onload = function() {
count++;
if (count == files.length) {
o.callback.call();
}
}
}
script.src = file;
head.appendChild(script);
});
}
};
ScriptMgr = new ScriptLoaderMgr();
Now it can be used this way:
For CSS files loading :
ScriptMgr.loadCss([first.css', 'second.css']);
That is you just need to provide css files path in an array and pass that array to loadCss() function as an argument. No callback is required for CSS files.
For JS file loading :
ScriptMgr.load({
scripts : ['lib/jquery-1.4.2.min.js','lib/jquery.touch-gallery-1.0.0.min.js'],
callback : function() {
//Here you will do those staff needed after the files get loaded
},
scope : this
});
In this case, the same way you entered CSS files, here you just need to put that array of JS files in scripts option. The callback function is called only when all the JS files are loaded successfully. Also, if in any case, the JS files are already loaded in the browser (i.e. already this code is run once), then the control will automatically go to the callback function.

Resources