Populate a input[type=file] with drag and drop without ajax [duplicate] - file-upload

I have a web site with a regular <input type="file"> file upload, POSTing the data to the backend when the form is submitted.
I would like to progressively enhance the form so that you can drop a file from outside the browser anywhere in the viewport (not just on the file input field, as built into some browsers) to upload it.
Whether or not the form autosubmits isn't important. So if the drag-and-drop only selects the file in the file field, without starting an upload, that's fine. I don't need support for multiple files. I don't need to show upload progress, thumbnails or anything fancy.
I know there are JS libs that support drag-and-drop uploads, but they all seem to upload via AJAX. I could do that, but then I would need to modify the backend and frontend to handle upload errors, redirect and show the right messages on success and so on.
I want a progressive enhancement that doesn't require any backend changes. It should happen synchronously using the form in the page. JS is fine, as long as the upload happens "in the foreground". Synchronous AJAX would not work, of course.

Although not really "synchronous" (JavaScript execution won't actually halt), you can set the files selected by <input type="file"> programatically. In fact, such elements and dragging share their file backend implementation (File and FileList instances), so it's really straight-forward. What's more, due to both frontends using FileLists, dragging multiple files work just as seamlessly.
This works in Chrome (using jQuery): http://jsfiddle.net/qMmPr/.
$(document).on("dragover drop", function(e) {
e.preventDefault(); // allow dropping and don't navigate to file on drop
}).on("drop", function(e) {
$("input[type='file']")
.prop("files", e.originalEvent.dataTransfer.files) // put files into element
.closest("form")
.submit(); // autosubmit as well
});

Thanks to #pimvdb comment, I came up with a pretty elegant solution.
Since drag and dropping on the <input type="file" /> works, why not making it full-screen on dragstart to make sure the user can't miss it? Anyway he is dragging so his intentions are clear at this moment.
Here's a demo: https://jsfiddle.net/08wbo4um
NB: unfortunately this doesn't seem to work in an iframe, but it does work on an actual page. You can still apprehend the behavior.
Here's the snippet:
$('input[type="file"]').on('change', function(e){
var fileName = e.target.files[0].name;
if (fileName) {
$(e.target).parent().attr('data-message', fileName);
}
});
$(document).on('drag dragstart dragend dragover dragenter dragleave drop', function(e) {
if ($('input[type="file"]').length) {
if (['dragover', 'dragenter'].indexOf(e.type) > -1) {
if (window.dragTimeout)
clearTimeout(window.dragTimeout);
$('body').addClass('dragged');
} else if (['dragleave', 'drop'].indexOf(e.type) > -1) {
// Without the timeout, some dragleave events are triggered
// when the :after appears, making it blink...
window.dragTimeout = setTimeout(function() {
$('body').removeClass('dragged');
}, 100);
}
}
});
h3, p {
text-align: center;
}
.form-group {
margin: 30px;
}
.file-upload .form-control {
height: 150px;
outline: 1px dashed #ccc;
outline-offset: -15px;
background-color: #eee;
}
.file-upload .form-control:before {
content: "\f093";
font: normal normal normal 14px/1 FontAwesome;
font-size: 3em;
left: 0;
right: 0;
display: block;
margin: 20px auto;
text-align: center;
}
.file-upload .form-control:after {
content: attr(data-message);
left: 0;
right: 0;
bottom: 0;
text-align: center;
display: block;
}
.file-upload .form-control input[type="file"] {
cursor: pointer;
opacity: 0;
width: 100%;
height: 100%;
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
}
body.dragged .file-upload .form-control input[type="file"] {
/* Make sure it is full screen, whatever the position absolute container */
position: fixed;
top: -50vh;
bottom: -50vh;
left: -50vw;
right: -50vw;
height: 200vh;
width: 200vw;
z-index: 10002;
}
body:after {
content: 'You can drop the file. :-)';
font-size: 2em;
text-align: center;
line-height: 100vh;
position: absolute;
top: 10px;
bottom: 10px;
left: 10px;
right: 10px;
background-color: #eee;
z-index: 10000;
border-radius: 4px;
border: thin solid #ccc;
visibility: hidden;
opacity: 0;
transition: visibility 0s, opacity 0.5s ease;
}
body.dragged:after {
opacity: 1;
visibility: visible;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<h3>Drag N Drop file upload without AJAX Demo</h3>
<p>Try drag and dropping a file. :-)</p>
<div class="form-group file-upload" required="required">
<label class="cols-sm-2 control-label" for="document_file">File Upload</label><br>
<div class="cols-sm-10">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-file" aria-hidden="true"></i></span>
<div class="form-control" data-message="Click to select file or drag n drop it here">
<input required="required" title="Click to select file or drag n drop it here" type="file" name="document[file]" id="document_file">
</div>
</div>
</div>
</div>

It can be done by turning autoUpload to false, collecting the files in an array, then on form submit do a single ajax call with all the files together with the form data, as described here.

Related

How to update iframe in vue.js

I have an Iframe
<iframe v-show="this.$store.getters['rackStore/getIframe'].show === 'iframe'" ref="iframe_3d" :src="this.$store.getters['rackStore/getIframe'].ref" id="iframe_3d" allowfullscreen="true" style="width: 100%; height: 100%; z-index: -1; position: fixed; top: 0px; left: 0px; border: 1px solid #00f; "></iframe>
And it displays the data perfectly.
Src I take from Vuex
Then I want to change the src value:
let iframe_param = this.$store.getters['rackStore/getIframe'];
this.$store.commit('rackStore/setIframe', { ...iframe_param, show: 'iframe', ref: model.ref } );
And I mean in the console that the src value of the iframe is changing!
But the iframe itself is not updated in the browser for some reason.
I tried to force update it: this.$ForceUpdate ();
This does not work.
I've tried changing the src in the usual way without Vue, this also doesn't work:
<iframe v-show="this.$store.getters['rackStore/getIframe'].show === 'iframe'" ref="iframe_3d"
src="" id="iframe_3d"
allowfullscreen="true" style="width: 100%; height: 100%; z-index: -1; position: fixed; top: 0px; left: 0px; border: 1px solid #00f; ">
</iframe>
var iframe = document.getElementById('iframe_3d');
iframe.src = model.ref;
src changes but no data refresh occurs, the previous site is still open in the iframe
What to do?
Thank you in advance!
Try update the element using key attribute:
<template>
<!-- ... -->
<iframe v-show="this.$store.getters['rackStore/getIframe'].show === 'iframe'" ref="iframe_3d" :src="this.$store.getters['rackStore/getIframe'].ref" id="iframe_3d" allowfullscreen="true" style="width: 100%; height: 100%; z-index: -1; position: fixed; top: 0px; left: 0px; border: 1px solid #00f; " :key="iframeUpdate"></iframe>
<!-- ... -->
</template>
<script>
export default {
// ...
data() {
return {
iframeUpdate: false
}
},
}
</script>
When your value in Vuex changes, simultaneously change iframeUpdate variable value like this:
this.iframeUpdate = !this.iframeUpdate;

How to show a dialog or popup from .net core without javascript

I want to create a .NET CORE application without using javascript at all and I am struggling to show dialogs to perform various crud operation, I want to call a controller action that will fill the dialog data and then show it to the user, without ever needing to use ajax or jquery.
How to show a dialog or popup from .net core without javascript
You could use CSS style to show the dialog or popup, code like this:
CSS style:
<style>
/* popups without javascript */
/* showing popup background */
a.popup:target {
display: block;
}
/* showing popup */
a.popup:target + div.popup {
display: block;
}
/* popup target anchor and background */
a.popup {
/* background is initially hidden */
display: none;
position: fixed;
top: 0;
left: 0;
z-index: 3; /* anything, really, but bigger than other elements on the page */
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
cursor: default;
}
/* popup window */
div.popup {
/* popup window is initially hidden */
display: none;
background: white; /* choose your color; */
width: 640px; /* width */
height: 480px; /* height */
position: fixed;
top: 50%;
left: 50%;
margin-left: -320px; /* = -width / 2 */
margin-top: -240px; /* = -height / 2 */
z-index: 4; /* z-index of popup backgroung + 1 */
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
/* links to close popup */
div.popup > a.close {
color: white;
position: absolute;
font-weight: bold;
right: 10px;
}
div.popup > a.close.word {
top: 100%;
margin-top: 5px;
}
div.popup > a.close.x {
bottom: 100%;
margin-bottom: 5px;
}
</style>
Html Content:
<h1>Popup without javascript demo</h1>
<p>Open popup</p>
<p>HTML visible body ends here</p>
<!--
Popup: the key is that id of <a> tag below is the same as href of <a> tag above
- that's what opens popup
<a> below serves several purposes:
1) popup background covering the whole window,
2) anchor to show popup when #my_popup is in URL,
3) link to close popup - if you click the background popup disappears
-->
<a id="my_popup" href="#" class="popup"></a>
<div class="popup">
<h3>My popup heading</h3>
<p>Popup content</p>
<a class="close x" href="#">x</a>
<a class="close word" href="#">Close</a>
</div>
The result like this:
Code from this link, and you could also refer this sample.
I am struggling to show dialogs to perform various crud operation, I
want to call a controller action that will fill the dialog data and
then show it to the user, without ever needing to use ajax or jquery
Since you don't want to use JQuery or Ajax, the above popup content should be populated when the page load, and when executing the CURD operations, you have to refresh the whole page. To implement partial view refreshes the popup content, you have to use JQuery and Ajax.

Can I replace the button displayed by Blazor's InputFile component with another element?

I am using the element to upload image files to my hosted Blazor WASM site. The component renders a button with the words "Choose Files" on it.
I would like to replace this button with an image (or my own text, or anything else). I have tried using CSS to set a background image to the URL of an image I would want to use, and set the background-color of the button to "transparent", but this does not seem to change anything.
The source code for this component can be found here:
https://github.com/SteveSandersonMS/BlazorInputFile
I studied the code and found that this component is built using the standard Blazor input type.
<input type=file>
Steve shows a way to override the default functionality and style of the button using CSS.
Here is an example I created based on what I found:
<style>
.file-input-zone {
display: flex;
align-items: center;
justify-content: center;
background-color: blue;
color: white;
cursor: pointer;
position: relative;
width: 120px;
height: 30px;
}
.file-input-zone:hover {
background-color: lightblue;
}
.file-input-zone input[type=file] {
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
}
</style>
<div class="file-input-zone">
<InputFile />
Get me a file!
</div>
It will give you a button that looks like this:
You might pick up more tips by studying his source code further.

How can I change the "Could not reconnect to the server" text in Blazor?

I am using the Blazor server-side.
When the Blazor App disconnect to the remote server, it will shows this:
I want to change the text ('Could not reconnect to the server...' and so on) of the image above.
I want to change it to the language of our country.
I found the file of the project but found nothing about this.
How can I change it? Thank you.
The Blazor App will check whether there's a html element with id={dialogId} in the page:
If such an element doesn't exists, it will use the default handler to display message.
If this element exists, this element's class will be :
set as components-reconnect-show when attempting to reconnect to the server.
set as components-reconnect-failed when reconnection failed, probably due to a network failure. To attempt reconnection, call window.Blazor.reconnect().
set as components-reconnect-rejected when reconnection rejected. The server was reached but refused the connection, and the user's state on the server is lost. To reload the app, call location.reload().
By default, the dialogId is components-reconnect-modal. So you can create an element in the page and use CSS to control the content and styles as you like.
Check out the Microsoft Docs for the latest info.
Demo:
For example, I create three parts of content to display within the Pages/_Host.cshtml:
<div id="components-reconnect-modal" class="my-reconnect-modal components-reconnect-hide">
<div class="show">
<p>
This is the message when attempting to connect to server
</p>
</div>
<div class="failed">
<p>
This is the custom message when failing
</p>
</div>
<div class="rejected">
<p>
This is the custom message when refused
</p>
</div>
</div>
<app>
#(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
</app>
<script src="_framework/blazor.server.js"></script>
And then let's add some CSS to control the style:
<style>
.my-reconnect-modal > div{
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
overflow: hidden;
background-color: #fff;
opacity: 0.8;
text-align: center;
font-weight: bold;
}
.components-reconnect-hide > div
{
display: none;
}
.components-reconnect-show > div
{
display: none;
}
.components-reconnect-show > .show
{
display: block;
}
.components-reconnect-failed > div
{
display: none;
}
.components-reconnect-failed > .failed
{
display: block;
}
.components-reconnect-rejected >div
{
display: none;
}
.components-reconnect-rejected > .rejected
{
display: block;
}
</style>
Finally, we'll get the following message when attempting to connect or failing to connect:
For the javascript side of things Blazor exposes a tiny API via the window.Blazor object.
One part of this API is the defaultReconnectionHandler which allows you to customize the reconnection experience including setting different options for the number of retrys etc.
However, it is also possible to just swap out the logic for displaying the ReconnectionDisplay
A simple implemenation looks like this and enables you to to get controll over the process:
function createOwnDisplay() {
return {
show: () => { alert("put code that shows a toast , ui, or whaterver here"); },
hide: () => { console.log('foo'); },
failed: () => { console.log('foo'); },
rejected: () => { console.log('foo'); }
};
}
Blazor.defaultReconnectionHandler._reconnectionDisplay = createOwnDisplay();
In your css:
#components-reconnect-modal {
display: none;
position: fixed;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
z-index: 1000;
overflow: hidden;
background-color: rgb(255, 255, 255);
opacity: 0.8;
text-align: center;
font-weight: bold;
}
#components-reconnect-modal.components-reconnect-show{
display: block;
}
#components-reconnect-modal.components-reconnect-show div.reconnecting {
display: block;
}
div.reconnecting {
display: none;
}
#components-reconnect-modal.components-reconnect-failed {
display: block;
}
#components-reconnect-modal.components-reconnect-failed div.failedToConnect {
display: block;
}
div.failedToConnect {
display: none;
}
#components-reconnect-modal.components-reconnect-rejected {
display: block;
}
#components-reconnect-modal.components-reconnect-rejected div.connectionRejected {
display: block;
}
div.connectionRejected {
display: none;
}
#components-reconnect-modal.components-reconnect-hide {
display: none;
}
In _Host body:
<div id="components-reconnect-modal">
<div class="reconnecting">
Подключение к серверу...
</div>
<div class="failedToConnect">
Не удалось подключиться к серверу <input type="button" value="переподключиться" onclick="ReconnectToServer()" />
<script>
function ReconnectToServer() {
window.Blazor.reconnect();
}
</script>
</div>
<div class="connectionRejected">
Подключение к серверу прервано <input type="button" value="обновить страницу" onclick="ReloadPage()" />
<script>
function ReloadPage() {
location.reload();
}
</script>
</div>
</div>

How to make a link as a file input in vue.js 2?

My vue component like this :
<template>
...
<a href="javascript:;" class="thumbs"
:title="upload">
<span class="fa fa-plus fa-2x"></span>
</a>
...
</template>
<script>
export default {
props: ['...'],
data() {
return {
...
};
},
computed:{
...
}
}
</script>
I want if click the a link, it can upload file
In javascript, I know it. If javascript like this : How to make a link act as a file input
But How can I do it in vue.js 2?
I believe there is a small misunderstanding: Vue.js 2 is still javascript. Its goal is not the same as Polymer with its fancy components - it is supposed to enhance JS, not replace it with a different structure altogether.
#David Hallberg Jönsson's answer will work perfectly fine in Vue.js 2 perfectly fine. If you want it specifically in Vue's component structure:
<template>
<!-- ... -->
<a class="fileContainer">
Click here to trigger the file uploader!
<input type="file">
</a>
<!-- ... -->
</template>
<script>
export default {
props: ['...'],
data() {
return {
...
};
},
computed:{
...
}
}
</script>
<style>
a.fileContainer {
overflow: hidden;
position: relative;
cursor: pointer;
display: inline-block;
color: lightskyblue;
}
a.fileContainer:hover {
text-decoration: underline;
color: blue;
}
a.fileContainer > input[type=file] {
cursor: inherit;
filter: alpha(opacity=0);
opacity: 0;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
text-align: right;
}
</style>
If you want to use the programmatic way in your link, it's not going to be easy because some browsers don't allow you to trigger click events on input type="file" elements. Your best bet would be to go this way.
(Also, technically you can still use jQuery with Vue, so the code in that link could still work if you wanted it to.)
If you want to know how to handle uploading files, there are many tutorials and some components already pre-made.
You can actually do this using only CSS, as explained here.
Example (from the link above):
.fileContainer {
overflow: hidden;
position: relative;
}
.fileContainer [type=file] {
cursor: inherit;
display: block;
font-size: 999px;
filter: alpha(opacity=0);
min-height: 100%;
min-width: 100%;
opacity: 0;
position: absolute;
right: 0;
text-align: right;
top: 0;
}
/* Example stylistic flourishes */
.fileContainer {
background: red;
border-radius: .5em;
float: left;
padding: .5em;
}
.fileContainer [type=file] {
cursor: pointer;
}
}
<p>So various methods prevent file upload inputs from being styled conveniently. But that needn't be the case!</p>
<label class="fileContainer">
Click here to trigger the file uploader!
<input type="file"/>
</label>