I am trying to use the Krajee Bootstrap File Input to upload a file. The way it works now is I have a button, that when clicked, opens a Bootstrap modal dialog and the input file tag is inside that modal. The user clicks a browse button, selects a file, and the file gets uploaded. All of that works just fine!
My issue is if the modal is closed, then reopened again, the file input control no longer works. When I click browse, it lets me select a file, but the Krajee File Input control errors out and says:
You must select at least 1 file to upload.
Even though I select a file, it says that anyway. Like I stated before, it works fine on the first use, but after multiple uses, it starts getting that error. I imagine my issue is in the way I'm destroying and re-creating the control. Here is my modal dialog and file input control html code:
<div class="modal fade" id="modalUpload" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Upload Profile Image</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div id="divFileInput">
<input id="fileFileUpload" type="file" name="files"/>
</div>
<div id="divCropper">
<img id="imgCropperPicture" alt="profile" />
</div>
</div>
</div>
</div>
</div>
Here is my relevant client side code (It's written in TypeScript 2.8):
$("#fileFileUpload").fileinput({
showPreview: true,
uploadAsync: false,
uploadUrl: '/fileupload',
allowedFileExtensions: ['jpg', 'jpeg', 'png'],
allowedPreviewTypes: ['image'],
uploadExtraData: { memberId: $("#hidMemberId").val() },
resizeImage: true,
theme: 'fa',
minFileCount: 1,
maxFileCount: 1,
dropZoneEnabled: false,
showRemove: false,
showUpload: false,
showCancel: false,
overwriteInitial: true
}).on("change", () => {
$("#fileFileUpload").fileinput(("upload") as any).on("filebatchuploadsuccess", (event, data: any) => {
this.uploadFinished(data);
});
});
$('#modalUpload').on('hidden.bs.modal', (e) => {
$("#divCropper").hide();
$("#divFileInput").show();
$("#fileFileUpload").fileinput(("clearStack") as any);
$("#fileFileUpload").fileinput(("clear") as any);
$("#fileFileUpload").fileinput(("destroy") as any).fileinput({
showPreview: true,
uploadAsync: false,
uploadUrl: '/fileupload',
allowedFileExtensions: ['jpg', 'jpeg', 'png'],
allowedPreviewTypes: ['image'],
uploadExtraData: { memberId: $("#hidMemberId").val() },
resizeImage: true,
theme: 'fa',
minFileCount: 1,
maxFileCount: 1,
dropZoneEnabled: false,
showRemove: false,
showUpload: false,
showCancel: false,
overwriteInitial: true
}).on("change", () => {
$("#fileFileUpload").fileinput(("upload") as any).on("filebatchuploadsuccess", (event, data: any) => {
this.uploadFinished(data);
});
});
});
So basically, I'm initializing the file input control for the first time. The $('#modalUpload').on('hidden.bs.modal' ...) code is code that gets executed when the BootStrap modal is closed. What I'm doing is calling the destroy method on the file input control, then recreating it exactly as before.
Any help on getting this working would be appreciated!
I seemed to figure this out for myself. In case anyone else runs into this issue, the problem is because the destroy method does not remove the event handlers. So what was happening is my change event handler was getting called, but because #fileFileUpload was destroyed, it was throwing a JS error in the console window. So what I had to do was just add the line below before calling the destroy method:
$("#fileFileUpload").off("change");
I placed that line before calling the destroy method and now everything works as expected.
The first time you initialize the plugin, it will attach a listener to the -- in this case -- fileFileUpload input. When the modal is closed, it trigger the input deletion but not the listener. And so when this line is triggered :
$("#fileFileUpload").fileinput(("destroy") as any).fileinput ...
it's actually creating a new element and attach the listener, but the old listener still active and so caused your program not working as expected.
You need to remove the listener when the modal is closed.
I had the same problem even without a modal.
I now just call destroy method before I init the file input.
See commit
$("#file-data").fileinput('destroy');
$("#file-data").fileinput({
previewFileType: "any",
uploadUrl: "BLAH",
maxFileSize: BLAHBLAH
});
$("#file-data").on(
"fileuploaded",
function (event, file, previewId, index) {
DO_SOMETHING
}
);
Related
I have an Angular 7.2.15 project that I have installed ng-bootstrap (https://ng-bootstrap.github.io/#/components/modal/examples) on with great success until now when I realize I need to change the contents of the modal dialog.
The dialog will simply display the result of an API call which I know is coming back fine from Chrome Network views, essentially for each record that comes back, we need to display the "name" attribute in the datatable as a link (eventually, that link will load a saved query by its name) and an icon to delete it.
Here are the two important snippets from the search-bar.component.html in question:
<ng-template #contentOpen let-modal>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">Open Query</h4>
<button type="button" class="close" aria-label="Close" (click)="modal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
Select from the following list of saved queries:
<table #dataTable class="table">
<thead>
<tr class="table-header">
<td class="max-width-80">Search Name</td>
<td class="max-width-20">Delete</td>
</thead>
<tbody class="table-body">
</tbody>
</table>
</div>
</ng-template>
...
<div class="col-md-1">
<button class="SearchGrayButton" (click)="openModal(contentOpen)">Open Search <span class="fa fa-folder-open-o"></span></button>
</div>
Then, in our search-bar.component.ts - we successfully get the SearchHistory from our webservice from the filteringService, but rendering the results poses the issue. If I take the datatable out from the ng-template #contentOpen section above, it will render the datatable fine (albeit not in the modal as we would like it to be). So within the modal, it does not render at all, but outside of it, the table will render without issue.
openModal(content) {
this.GetSearchHistory();
this.modalService.open(content, {ariaLabelledBy: 'modal-basic-title'}).result.then((result) => {
this.closeResult = `Closed with: ${result}`;
}, (reason) => {
this.closeResult = `Dismissed`;
});
}
/* Obtains all of the search history for a user and renders table in
the modal to display them */
GetSearchHistory() {
this.filteringService.getSearchHistory().subscribe(
resp => {
console.log(resp, 'res');
let r: WebServiceResponse;
r = resp as WebServiceResponse;
this.searchHistory = r.data;
this.buildTableForSearchHistory();
},
error => { console.log(error, 'error'); }
);
}
buildTableForSearchHistory() {
const options = {
sDom: 't',
renderer: 'bootstrap',
destroy: true,
data: this.searchHistory,
columns: [
{ data: 'name',
className: 'dt-center max-width-10'
}
],
order: [0, 'desc'],
createdRow( row, data, dataIndex ) {
},
drawCallback: () => {
}
};
this.dataTable = $(this.table.nativeElement);
this.dataTable.DataTable(options);
}
As a test, I also set up a mock "refresh" button of sorts within the modal that would trigger the getSearchHistory() we see above and build the table after we know the modal is in focus, and this also does not resolve the issue. The console complains about the following line, which makes sense as I think it's having trouble finding the table in question to render to:
this.dataTable = $(this.table.nativeElement);
I don't know if it's needed beyond context especially for how simple it is, but a sample of the web service's JSON response in question:
{"status":null,"code":null,"messages":[],"data":[{"id":1,"userId":null,"name":"manual test name","queryText":null,"filtersJson":null},{"id":2,"userId":null,"name":"testname","queryText":null,"filtersJson":null},{"id":3,"userId":null,"name":"testname","queryText":null,"filtersJson":null}]}
Also noteworthy is we don't necessarily have to use DataTables here, as requirements really only are to display all the names as links and perhaps have the queryText and filtersJson as metadata since we'll need them for later. I just thought it might be a "nice to have" if we allow them to sort the results.
Does anyone have any thoughts or ways to help resolve this?
Realized binding in this manner resolves it since there was no need for the DataTables usage:
<div class="modal-body">
Select from the following list of saved queries:
<ul>
<li *ngFor="let s of searchHistory">{{s.name}}</li>
</ul>
</div>
I am working on uploading functionality for a product i'm building with vuedropzone 2, from the dropzone docs http://www.dropzonejs.com/#config-autoQueue , it is possible to prevent the accepted/added files from being queued automatically by setting autoQueue to false, also it is stated there that the files can be queued manually by calling enqueueFile(file) manually.
Setting autoQueue to false works, however when i try to add the file manually to the queue, its not working and i am getting this error this.$refs.dropzone.enqueueFile is not a function
my script:
new Vue({
data: {
dropzoneOptions: {
url: 'https://httpbin.org/post',
thumbnailWidth: 150,
maxFilesize: 1000,
addRemoveLinks: true,
autoProcessQueue: false,
autoQueue: false,
dictDefaultMessage: "<i class='is-size-150 fa fa-cloud-upload'>
</i>",
headers: {
'Access-Control-Allow-Origin': '*',
},
parallelUploads: 1,
},
},
methods: {
upload() {
document.querySelector('.dropzone').click();
},
startUpload() {
this.$refs.dropzone.getAcceptedFiles().forEach(f => this.$refs.dropzone.enqueueFile(f));
....
},
})
my template:
div
button.button.is-primary(#click="upload") upload Document(s)
dropzone(
v-show="false",
:id="id",
ref="dropzone",
:maxNumberOfFiles="100" ,
:maxFileSizeInMB="1000",
:options="dropzoneOptions",
#vdropzone-upload-progress="updateFilename",
#vdropzone-files-added="handleFilesAdded",
#vdropzone-error="handleUploadError",
#vdropzone-total-upload-progress="progress",
#vdropzone-queuecomplete="handleUploadComplete",
#vdropzone-success="fileUploaded",
:parallelUploads="1",
)
// this dialog modal shows only when files have been selected after clicking upload document button
el-dialog(title="Upload Files", :visible.sync="hasAddedFiles")
div(slot="title")
// button for adding more files before clicking start upload
button.is-info.button.ml-15(#click="addFiles") Add File(s)
// table that lists all selected files
el-table(:data="addedFiles", :height="400")
el-table-column(type="index" :index="1")
el-table-column(
property="name",
show-overflow-tooltip,
label="Name",
min-width="200"
)
el-table-column(property="type" label="File type")
el-table-column(label="Delete" width="100")
template(scope="props")
// button for removing a file
button.button.trash(
:class="$style.trash",
#click="removeFile(props.row)",
)
i.material-icons delete
div(slot="footer")
// select file type
el-select(
v-model="addedFilesType"
filterable
allow-create
placeholder="Choose file(s) type"
)
el-option(
v-for="(item, index) in documentTypes"
:key="index"
:value="item"
)
// button for enqeueing and start processing the queue in order to start files upload
button.button.is-primary.is-medium.ml-15(
#click="startUpload",
:disabled="!addedFilesType.length",
)
span.icon
i.material-icons cloud_upload
span Start Upload
The enqueueFile is not delegated on the vue-dropzone component. So it's not available on this.$refs.dropzone.
But there is a solution to this (however not very elegant). You should be able to call this.$refs.dropzone.dropzone to get the underlying dropzone object.
So this.$refs.dropzone.dropzone.enqueueFile(f) should work.
I've opened an issue on the github plugin, but it doesn't seem to be very active, so I asked here too.
I'm sending my files through ajax but when the upload fails (as with height too small) I don't get the real error message, but I get an error from my ajax url, but this one makes no sense since nothing is sent.
I think the ajax route shouldn't be called, but anyway, I tried to play with 'fileuploaderror' and I do get the wanted errors, but I don't know how to display them. There must be a simple way in the fileuploaderror event, but I don't know it.
Can anyone help me with this one ?
Issue link |
Plugin page
Thanks
$("#id").fileinput({
uploadUrl: "/ajax/snippet/image/send/78", // server upload action
deleteUrl: "/ajax/snippet/image/remove/",
uploadAsync: false,
showUpload: false, // hide upload button
showRemove: false, // hide remove button
maxFileCount: maxFile,
browseOnZoneClick: true,
language: "fr",
minImageWidth: 150,
minImageHeight: 150,
allowedFileExtensions: ["jpg", "jpeg", "gif", "bmp", "png"],
multiple: true,
maxFileSize: 5000,
uploadExtraData: function (previewId, index) {
return {key: index};
},
initialPreviewAsData: true,
overwriteInitial: false,
}).on("filebatchselected", function (event, files) {
// trigger upload method immediately after files are selected
$(this).fileinput("upload");
}).on('fileuploaderror', function (event, data, msg) {
var form = data.form, files = data.files, extra = data.extra,
response = data.response, reader = data.reader;
// get message
alert(msg);
});
}
Ok, I got the answer, in my "filebatchselected" event, I was forcing the upload.
I need to check if I have valid files to upload first.
$("#id").fileinput({
uploadUrl: "/ajax/snippet/image/send/78", // server upload action
deleteUrl: "/ajax/snippet/image/remove/",
uploadAsync: false,
showUpload: false, // hide upload button
showRemove: false, // hide remove button
maxFileCount: maxFile,
browseOnZoneClick: true,
language: "fr",
minImageWidth: 150,
minImageHeight: 150,
allowedFileExtensions: ["jpg", "jpeg", "gif", "bmp", "png"],
multiple: true,
maxFileSize: 5000,
uploadExtraData: function (previewId, index) {
return {key: index};
},
initialPreviewAsData: true,
overwriteInitial: false,
}).on("filebatchselected", function (event, files) {
// trigger upload method immediately after files are selected
var filesStack = $('#input-id').fileinput('getFileStack');
if (filesStack.length > 0) {
$(this).fileinput("upload");
}
});
}
I would like to use a custom window as popup editor of a Kendo UI Grid. Its content will be complex with search fields and a grid to display search results. To do that, I don't want to use the Kendo template mechanism but a real popup window.
While doing tests with custom editor I faced an issue. Even with a very basic and simple scenario (just the create command), I'm only able to open the editor once. The second time I get an error, the editor doesn't show up anymore and the grid becomes empty.
Here is my HTML code :
<div id="main-content">
<div id="custom-window">
</div>
<table id="my-table-grid">
</table>
</div>
The JavaScript part :
function openCustomWindow(e) {
e.preventDefault();
myWindow.center().open();
}
function editorWindowClosed(e) {
myGrid.cancelRow();
}
var myWindow = $("#custom-window").kendoWindow({
modal: true,
resizable: false,
title: "Test",
visible: false,
close: editorWindowClosed
}).data("kendoWindow");
var dummyDataSource = new kendo.data.DataSource(
{
schema : {
model : {
id: "id",
fields: {
postion: { type: "number"},
name: { type: "string"},
}
}
}
});
dummyDataSource.add({postion: 1, name: "gswin"});
var myGrid = $("#my-table-grid").kendoGrid({
dataSource: dummyDataSource,
toolbar: [ {
name: "create",
text: "Create"
} ],
editable: {
mode: "popup",
window: {
animation: false,
open: openCustomWindow,
}
},
columns: [
{
field: "postion",
title: "Postion"
},
{
field: "name",
title: "Name"
}
]
}).data("kendoGrid");
The error message in the JavaScript console :
Uncaught TypeError: Cannot read property 'uid' of
undefinedut.ui.DataBoundWidget.extend.cancelRow #
kendo.all.min.js:29ut.ui.DataBoundWidget.extend.addRow #
kendo.all.min.js:29(anonymous function) #
kendo.all.min.js:29jQuery.event.dispatch #
jquery-2.1.3.js:4430jQuery.event.add.elemData.handle #
jquery-2.1.3.js:4116
And finally a jsfiddle link to show what I'm doing. (The popup is empty because I just want to fix the open / close mechanism before going any further)
I don't understand why I get this error, I expected to be able to open / close the popup as many time as I wanted. The default editor popup is working fine.
One of my colleague found the solution (thanks to him).
Actually this piece of code
editable: {
mode: "popup",
window: {
animation: false,
open: openCustomWindow,
}
}
is designed to configure the default popup...
The right way to use a custom popup is the following :
editable :true,
edit: openCustomWindow,
With this approach it's also better to use
function editorWindowClosed(e) {
myGrid.cancelChanges();
}
Instead of
function editorWindowClosed(e) {
myGrid.cancelRow();
}
Working jsfiddle link
Actually the approach in my previous answer solved my issue but is causing another issue. The grid becomes editable but in the default mode (which is "inline").
Doing this
editable: "popup",
edit: openCustomWindow
has fixed this other issue but is now displaying 2 popups.
I finally succeeded to hide the default popup and only show my custom popup but it ended with the orginal issue described in initial question...
Considering all those informations I arrived to the conclusion that working with a custom popup window is definitely not an option. I'll start working with teamplates instead.
Another solution would have been to use a template to override the toolbar with a custom "Add" button and use a custom command for edition. But at this point I consider that too "tricky".
To prevent Kendo UI custom grid popup editor window, define the editable property:
editable:
{
mode: "popup",
window: {
animation: false,
open: updateRowEventHandler
}
} // skip edit property
Then paste preventDefault() at the beginning of the handler:
function updateRowEventHandler(e) {
e.preventDefault(); //
I´m using the Fine Upload-Plugin.
I want to upload .docx-files to my application ... only .docx-files.
Surely this is easy to handle with a query, like
if (extension == "docx")
upload something
But I saw a field in which you can specify a data type like "All types" or "All images".
Where can i add/manipulate this validation?
I tried the acceptFiles-options, but this only prevent uploads.
I want to give the user the possibility to show .docx-files only.
HTML-Code:
<div id="manual-fine-uploader"></div>
<div id="triggerUpload" class="btn btn-primary" style="margin-top: 10px;display:none">
<i class="icon-upload icon-white"></i> Datei einfügen
</div>
<div id="uploadNewFile"></div>
JS-Code
$("#uploadNewFile").fineUploader({
element: document.getElementById('manual-fine-uploader'),
request: {
endpoint: 'Upload.aspx'
},
autoUpload: true,
//Part, that may be important
///MEME-Type: docx
acceptFiles: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
allowedExtensions: ["docx"],
//Endpart
maxConnections: 1,
multiple: false,
chunking: {
enabled: true
},
resume: {
enabled: true
},
text: {
uploadButton: 'Datei hochladen'
}
});
EDIT:
Maybe the Question isnt clear enough:
I need a specific filter within the select-file-dialog.
Like the standard "images only" or "all types" etc..
How to add these kind of filter?
Here you see the select
Your allowedExtensions and acceptFiles options are not in the correct place. Your code should look like this:
$("#uploadNewFile").fineUploader({
element: document.getElementById('manual-fine-uploader'),
request: {
endpoint: 'Upload.aspx'
},
validation: {
acceptFiles: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
allowedExtensions: ["docx"]
},
maxConnections: 1,
multiple: false,
chunking: {
enabled: true
},
resume: {
enabled: true
},
text: {
uploadButton: 'Datei hochladen'
}
});
Please see the validation option in the documentation for more details, along with the validation feature page.
Also, if you are using Fine Uploader 4.x, the text.uploadButton option was removed as part of the templating redesign. In 4.x and newer, you can specify the button name, among other things, in a template you declare in your markup.
Finally, I removed the autoUpload option from your configuration, as you were setting it to the default value. No need to declare it in that case.