I'm trying to figure out whether or not this is possible. First off, I should say that I'm cloning the FileReader object and maintaining that throughout my application (so I can allow the user to remove files before they officially upload them).
All that works fine, but I'm hung up trying to figure out the best way to allow a user to remove a member from this cloned object (members are the same as files [images in my case] which would be found within a normal FileReader object).
So basically I've cloned the object, add a pseudoHash to it which allows me to reference the cloned member that the user wants to delete, and upon clicking on a "Remove" icon, I search for the associated pseudoHash which I've tied to that icon, within the cloned object, and delete the member. The would work fine if the object were in its own function, but it's not. So I'm having trouble trying to delete the object's members. I made files global in this case, but it's still not working.
// Our FileList object
files = e.target.files;
// Create a clone since FileList is readonly
files = cloneObject(files);
// If files isn't empty, give the user the option to remove members
if (files.length) {
files = removeSelected(files);
}
Here is my function. Unfortunately when I click to remove an image from the "upload queue", it should go through this function, but it actually doesn't delete the object, as I know Javascript doesn't ever completely delete objects, so if it's the last member, I try setting it to an empty object, but that doesn't work. If it's not the last member, I just want to remove it within its place. Again, because this is in a separate function and not the parent, it's only deleting the local reference to the variable.
// Remove selected file from cloned fileList object
var removeSelected = function(files) {
var dataPseudoHash = $(this).attr('data-pseudo-hash');
$('#js-selected-files').delegate('.js-remove-selected', 'click', function() {
if (files.length == 1) {
$('.js-image-upload').attr('disabled', true).addClass('btn_disabled');
files = {};
delete files;
} else {
// Loop through our files object and if we find a member with the same
// pseudoHash as the attribute from whatever 'X' icon that was clicked, remove it
$.each(files, function(i, dataPseudoHash) {
if (files[i].pseudoHash == dataPseudoHash) {
delete files[i];
files.length -= 1;
}
});
}
// Remove hidden input that prevents duplicate files being uploaded of
$('.js-pseudo-hash[value="' + dataPseudoHash + '"]').remove();
$(this).parent().fadeOut('normal', function() {
$(this).remove();
});
return files;
};
What's the best way to handle this? Maybe setting a flag to true or false, and then delete the object or its members accordingly in the parent function depending on the case? Not quite sure. Other than that, I've got it uploading the files fine, but I need to find out how to remove properties of the object or the object altogether (if all "Remove" icons are clicked).
Firstly you should know that the delete operator deletes only an element oof an array or element of an object not the whole array. In your first if condition
if (files.length == 1) {
$('.js-image-upload').attr('disabled', true).addClass('btn_disabled');
files = {};
delete files;
}
You are deleting the whole object which returns false.
Now it will be better if you can manually check whether the second if condition is true or not in any case.
Related
I wrote a script that allows you to delete a property in the caches of the application, however I need to run this script only once when I install the application.
someone has an idea, thanks
var executed = 0;
if(executed === 0){
Ti.App.Properties.removeProperty("My_Property");
executed++;
}
The only ways you can hold some value across app sessions are Ti.App.Properties or sql database. So you can do it in two ways as below:
Solution 1: Use another property to know that you have deleted the desired property.
// for first time installation, the default value (or 2nd parameter) will be false as you have not deleted the property yet
var isDeleted = Ti.App.Properties.getBool('My_Property_Deleted', false);
if (isDeleted) {
Ti.App.Properties.removeProperty("My_Property");
// since you have deleted it, now set it to true so this 'if' block doesn't runs on any next app session
Ti.App.Properties.setBool('My_Property_Deleted', true);
} else {
// you have already deleted the property, 'if' block won't run now
}
Solution 2: Create a new database or pre-load a shipped db with your app.
// Titanium will create it if it doesn't exists, or return a reference to it if it exists (after first call & after app install)
var db = Ti.Database.open('your_db');
// create a new table to store properties states
db.execute('CREATE TABLE IF NOT EXISTS deletedProperties(id INTEGER PRIMARY KEY, property_name TEXT);');
// query the database to know if it contains any row with the desired property name
var result = db.execute('select * from deletedProperties where name=?', "My_Property");
if (result.rowCount == 0) { // means no property exists with such name
// first delete the desired property
Ti.App.Properties.removeProperty("My_Property");
// insert this property name in table so it can be available to let you know you have deleted the desired property
db.execute('insert into deletedProperties(name) values(?)', "My_Property");
} else {
// you have already deleted the property, 'if' block won't run now
}
// never forget to close the database after no use of it
db.close();
There can be other ways as well, but these 2 will work for what you want. Read more about Ti.Database here
I'm trying to save an object with rhaboo in javascript. The first time after initialising it is working but when I'm trying to save it again it gives me the
rhaboo.min.js:1 Uncaught TypeError: Cannot read property 'refs' of undefined error. I pinned down the error to the line where I save the keyArray with notes.write('presentationNotes', keyArray);
How I get the error in detail:
I open my webapplication with a clean localStorage (nothing is saved) and rhaboo gets initialised. After that I navigate to a document and open the notes-div with the notes-button. I write something in the notes-area and hit the notes-submit button to save the notes with rhaboo to localStorage. I do the same for a second document. For now everything works. Both notes get saved correctly so that I have an object like this:
keyArray = {activeDoc1: ['note1', 'note2'], activeDoc2: ['note1', 'note2']}
saved in rhaboo in notes.presentationNotes. Then I reload my webapplication and rhaboo is already initialised. I navigate to the documents as before and check if I can load the saved notes. This works as expected but when I try to hit the notes-submit button again it gives me the aforementioned error. What am I doing wrong?
var notes = Rhaboo.persistent('Presentation Notes');
$(document).ready(function(event) {
var keyArray, activeDoc;
if (!notes.initialised) {
notes.write('initialised', true);
notes.write('presentationNotes', {});
console.log('Rhaboo Initialised');
keyArray = {};
} else {
console.log('Rhaboo already initialised');
keyArray = notes.presentationNotes;
console.log('notes.presentationNotes onLoad = ');
console.log(notes.presentationNotes);
}
//Notes open
$(document).on('click', '#notes-button', function() {
$('.notes-div').show();
activeDoc = $('.node.active').attr('id');
if (notes.presentationNotes[activeDoc] != null) {
//Iterate notes
$.each(notes.presentationNotes[activeDoc], function(index, value) {
$('#notes-area').append(value + '\n');
});
}
});
//Notes save
$(document).on('click', '#notes-submit', function() {
$('.notes-div').hide();
var str = $('#notes-area').val();
var array = str.split("\n");
keyArray[activeDoc] = array;
//Save notes
notes.write('presentationNotes', keyArray);
//Clear textarea
$('#notes-area').val('');
});
}
Without the HTML I haven't been able to try this, so I'm just guessing here, but I suspect your problem will go away if you stop using keyArray and activeDoc. The whole point of rhaboo is that it is not a place to store your data. It IS your data.
I see no transient data in your program, i.e., no data which you actively want to delete when the user goes away and comes back. All the data is supposed to be persistent, therefore it should all be under the Rhaboo.persistent.
That's the philosophy, but to be more specific, I think your problem is here:
keyArray[activeDoc] = array;
When I wonder what keyArray is is find:
keyArray = notes.presentationNotes;
so the earlier line actually says:
notes.presentationNotes[activeDoc] = array;
but it says on the tin that that should read:
notes.presentationNotes.write(activeDoc, array);
The upshot is that that the hooks that make rhaboo work have not been inserted into array, as notes.presentationNotes.write would have done.
When you next said:
notes.write('presentationNotes', keyArray);
it meant:
notes.write('presentationNotes', notes.presentationNotes).
which is clearly not what you meant. Rhaboo doesn't suspect that array has no hooks yet because it can see that notes.presentationNotes does have hooks.
I also forget to use write sometimes, and it really bugs me that JS offers no way to hook into the creation of a NEW key within an object X, no matter what you've done to X. Without that limitation, there'd be no need for write and it could be foolproof.
I have a problem with konvajs. I have a konva Stage that I clone into a temporary Stage, so I can revert changes made by a user, when the user cancels.
The way I do this is, that I clone the existing Stage into a temporary one, destroy the children of the origin and after that I move the children of the temporary Stage back to the original and destroy the temporary Stage. The problem is, when I try to access the children now, for example via findOne('#id-of-child'), I get undefined, even though the children exist.
Here's what I've done so far:
clone: function()
{
var cloned_stage = this.stage.clone();
Array.each(this.stage.getChildren(), function(layer, lidx) {
if (layer.attrs.id) {
// setting the id to the ones, the children had before
cloned_stage.children[lidx].attrs.id = layer.attrs.id;
}
});
return cloned_stage;
},
restore: function(tmp_stage)
{
this.stage.destroyChildren();
Array.each(tmp_stage.getChildren(), function(layer, lidx) {
var tmp_layer = layer.clone();
tmp_layer.attrs.id = layer.attrs.id;
tmp_layer.moveTo(this.stage);
}.bind(this));
tmp_stage.destroy();
this.stage.draw();
},
Now when the user opens the toolbox to change something, the current stage is cloned with the "clone" function and when the user cancels his changes, the "restore" function is called with the cloned stage as parameter.
But after that when I try to do things like the following they do not work as expected.
some_child_of_the_stage.getLayer(); -> returns null
var edit_layer = this.stage.findOne('#edit-layer'); -> returns undefined
But the "some_child_of_the_stage" does exist and has a layer of course and the stage has a child with the id "edit-layer".
Me and my colleague are at our wit's end.
I appreciate any help and suggestions thank you.
Update:
A short fiddle showing the problem via console.log:
fiddle
It is better not to touch attrs property of a node and use public getters and setters.
Konva has special logic for storing id property. Selector by id #edit-layer may not work because of direct access to attrs id.
You can use name property fo that case.
https://jsfiddle.net/s36hepvg/12/
I have created an array to store a list of selected files. Well, I have to be honest, I looked up the code and when I realized it worked for my purpose, I used it. Now I need to access this array in order to delete some files manually, but I have tried using the index of each sector, but this does not work.
This is how i create the array and store files.
var files = [];
$("button:first").on("click", function(e) {
$("<input>").prop({
"type": "file",
"multiple": true
}).on("change", function(e) {
files.push(this.files);
}).trigger("click");
});
How could I read the array files[] if it contains an object fileList or obtain indexs from the array?
Here's how I understand your code:
Each time the first button in the dom is clicked a file input dialogue which accepts multiple files is generated. Upon return the dialogue emits a change event with a files variable (a FileList object) attached to the function context (this). Your code pushes the newly created FileList onto the files array. Since the input accepts multiple files each object pushed onto the files array is a FileList object.
So if you want to iterate through all elements in the files array you can put a function in the change event handler:
var files = [];
$("button:first").on("click", function(e) {
$("<input>").prop({
"type": "file",
"multiple": true
}).on("change", function(e) {
files.push(this.files);
iterateFiles(files);
}).trigger("click");
});
function iterateFiles(filesArray)
{
for(var i=0; i<filesArray.length; i++){
for(var j=0; j<filesArray[i].length; j++){
console.log(filesArray[i][j].name);
// alternatively: console.log(filesArray[i].item(j).name);
}
}
}
In the iterateFiles() function I wrote filesArray[i][j] isn't really a multidimensional array -- but rather a single dimensional array containing FileList objects which behave very much like arrays...except that you can't delete/splice items out of them -- they are read-only.
For more info on why you can't delete see: How do I remove a file from the FileList
Since you are using jQuery you can use $.grep
files=$.grep( files, function(elementOfArray, indexInArray){
/* evaluate by index*/
return indexInArray != someValue;
/* OR evaluate by element*/
return elementOfArray != someOtherValue;
});
API Reference: http://api.jquery.com/jQuery.grep/
Something like this?
for(var i = 0; i < files.length; i++) {
alert(files[i][0].name);
if (files[i][0].name == 'file.jpg') {
files.splice(i, 1) //remove the item
}
}
That is, there is always one file in each FileList due to the way you select it. So for each filelist you are only interested in the first file in it. For each file you can just get the properties as defined here: http://help.dottoro.com/ljbnqsqf.php
I'm trying to use the Ajax File Upload as featured here: http://valums.com/ajax-upload/
As you can see, I need to create a qq.FileUploader object to initialize the script. However, I need to be able to dynamically create this objects without knowing the IDs of the elements. I've tried creating something like this:
var uploader, i = 0;
$(".file-upload").each(function() {
$e = $(this);
i++;
uploader[i] = new qq.FileUploader({
element: $(this)[0],
action: 'uploadfile.php',
allowedExtensions: ['doc', 'docx', 'pdf'],
multiple: false,
onComplete: function(id, fileName, responseJSON) {
$($e).siblings('input').val(responseJSON.newfilename);
}
});
});
I've learned that the [i] part I have added breaks the script, because I cannot have objects inside of an array.
Is there another way I can create this objects dynamically? They need to all have a unique name, otherwise the onComplete function gets overwritten for all of them. I experimented with using eval(), but I can't seem to make it work correctly.
You have to declare uploader as an array first :
var uploader = [];
Because you declared the variable without defining it, it has the default value of undefined , and your code was translated into something like undefined[i] which triggers an error.
Has to be something like
var uploader = {};
or else uploader is null and you cannot assign anything to it.
EDIT:
So there're two opitions, in my opinion, if one wants to have an array than it makes sense to declare one, var uploader = []; and then use the uploader.push() method or define it as an object var uploader = {}; and just do uploader[i] = ....
It is also possible to do the latter with an a array, but in the latter case I see no point in maintaining the counter (i).