How to add text input to dropzone upload - templates

I'd like to allow users to submit a title for each file that is dragged into Dropzone that will be inputted into a text input. But i don't know how to add it. Everyone can help me?
This is my html code code
<form id="my-awesome-dropzone" class="dropzone">
<div class="dropzone-previews"></div> <!-- this is were the previews should be shown. -->
<!-- Now setup your input fields -->
<input type="email" name="username" id="username" />
<input type="password" name="password" id="password" />
<button type="submit">Submit data and files!</button>
</form>
And this is my script code
<script>
Dropzone.options.myAwesomeDropzone = { // The camelized version of the ID of the form element
// The configuration we've talked about above
url: "upload.php",
autoProcessQueue: false,
uploadMultiple: true,
parallelUploads: 100,
maxFiles: 100,
maxFilesize:10,//MB
// The setting up of the dropzone
init: function() {
var myDropzone = this;
// First change the button to actually tell Dropzone to process the queue.
this.element.querySelector("button[type=submit]").addEventListener("click", function(e) {
// Make sure that the form isn't actually being sent.
e.preventDefault();
e.stopPropagation();
myDropzone.processQueue();
});
// Listen to the sendingmultiple event. In this case, it's the sendingmultiple event instead
// of the sending event because uploadMultiple is set to true.
this.on("sendingmultiple", function() {
// Gets triggered when the form is actually being sent.
// Hide the success button or the complete form.
});
this.on("successmultiple", function(files, response) {
// Gets triggered when the files have successfully been sent.
// Redirect user or notify of success.
});
this.on("errormultiple", function(files, response) {
// Gets triggered when there was an error sending the files.
// Maybe show form again, and notify user of error
});
},
accept: function (file, done) {
//maybe do something here for showing a dialog or adding the fields to the preview?
},
addRemoveLinks: true
}
</script>

You can actually provide a template for Dropzone to render the image preview as well as any extra fields. In your case, I would suggest taking the default template or making your own, and simply adding the input field there:
<div class="dz-preview dz-file-preview">
<div class="dz-image"><img data-dz-thumbnail /></div>
<div class="dz-details">
<div class="dz-size"><span data-dz-size></span></div>
<div class="dz-filename"><span data-dz-name></span></div>
</div>
<div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div>
<div class="dz-error-message"><span data-dz-errormessage></span></div>
<input type="text" placeholder="Title">
</div>
The full default preview template can be found in the source code of dropzone.js.
Then you can simply pass your custom template to Dropzone as a string for the previewTemplate key of the option parameters. For example:
var myDropzone = new Dropzone('#yourId', {
previewTemplate: "..."
});
As long as your element is a form, Dropzone will automatically include all inputs in the xhr request parameters.

I am doing something fairly similar. I accomplished it by just adding a modal dialog with jquery that opens when a file is added. Hope it helps.
this.on("addedfile", function() {
$("#dialog-form").dialog("open");
});

In my answer, substitute your "title" field for my "description" field.
Add input text or textarea to the preview template. For example:
<div class="table table-striped files" id="previews">
<div id="template" class="file-row">
<!-- This is used as the file preview template -->
<div>
<span class="preview"><img data-dz-thumbnail /></span>
</div>
<div>
<p class="name" data-dz-name></p>
<input class="text" type="text" name="description" id="description" placeholder="Searchable Description">
</div> ... etc.
</div>
</div>
Then in the sending function, append the associated data:
myDropzone.on("sending", function(file, xhr, formData) {
// Get and pass description field data
var str = file.previewElement.querySelector("#description").value;
formData.append("description", str);
...
});
Finally, in the processing script that does the actual upload, receive the data from the POST:
$description = (isset($_POST['description']) && ($_POST['description'] <> 'undefined')) ? $_POST['description'] : '';
You may now store your description (or title or what have you) in a Database etc.
Hope this works for you. It was a son-of-a-gun to figure out.

This one is kind of hidden in the docs but the place to add additional data is in the "sending" event. The sending event is called just before each file is sent and gets the xhr object and the formData objects as second and third parameters, so you can modify them.
So basically you'll want to add those two additional params and then append the additional data inside "sending" function or in your case "sendingmultiple". You can use jQuery or just plain js to get the values. So it should look something like:
this.on("sendingmultiple", function(file, xhr, formData) {
//Add additional data to the upload
formData.append('username', $('#username').val());
formData.append('password', $('#password').val());
});

Here is my solution:
Dropzone.autoDiscover = false;
var myDropzone = new Dropzone("#myDropzone", {
url: 'yourUploader.php',
init: function () {
this.on(
"addedfile", function(file) {
caption = file.caption == undefined ? "" : file.caption;
file._captionLabel = Dropzone.createElement("<p>File Info:</p>")
file._captionBox = Dropzone.createElement("<input id='"+file.filename+"' type='text' name='caption' value="+caption+" >");
file.previewElement.appendChild(file._captionLabel);
file.previewElement.appendChild(file._captionBox);
}),
this.on(
"sending", function(file, xhr, formData){
formData.append('yourPostName',file._captionBox.value);
})
}
});
yourUploader.php :
<?php
// Your Dropzone file named
$myfileinfo = $_POST['yourPostName'];
// And your files in $_FILES
?>

$("#my-awesome-dropzone").dropzone({
url: "Enter your url",
uploadMultiple: true,
autoProcessQueue: false,
init: function () {
let totalFiles = 0,
completeFiles = 0;
this.on("addedfile", function (file) {
totalFiles += 1;
localStorage.setItem('totalItem',totalFiles);
caption = file.caption == undefined ? "" : file.caption;
file._captionLabel = Dropzone.createElement("<p>File Info:</p>")
file._captionBox = Dropzone.createElement("<textarea rows='4' cols='15' id='"+file.filename+"' name='caption' value="+caption+" ></textarea>");
file.previewElement.appendChild(file._captionLabel);
file.previewElement.appendChild(file._captionBox);
// this.autoProcessQueue = true;
});
document.getElementById("submit-all").addEventListener("click", function(e) {
// Make sure that the form isn't actually being sent.
const myDropzone = Dropzone.forElement(".dropzone");
myDropzone.processQueue();
});
this.on("sending", function(file, xhr, formData){
console.log('total files is '+localStorage.getItem('totalItem'));
formData.append('description[]',file._captionBox.value);
})
}
});

For those who want to keep the automatic and send datas (like an ID or something that does not depend on the user) you can just add a setTimeout to "addedfile":
myDropzone.on("addedfile", function(file) {
setTimeout(function(){
myDropzone.processQueue();
}, 10);
});

Well I found a solution for me and so I am going to write it down in the hope it might help other people also. The basic approach is to have an new input in the preview container and setting it via the css class if the file data is incoming by succeeding upload process or at init from existing files.
You have to integrate the following code in your one.. I just skipped some lines which might necessary for let it work.
photowolke = {
render_file:function(file)
{
caption = file.title == undefined ? "" : file.title;
file.previewElement.getElementsByClassName("title")[0].value = caption;
//change the name of the element even for sending with post later
file.previewElement.getElementsByClassName("title")[0].id = file.id + '_title';
file.previewElement.getElementsByClassName("title")[0].name = file.id + '_title';
},
init: function() {
$(document).ready(function() {
var previewNode = document.querySelector("#template");
previewNode.id = "";
var previewTemplate = previewNode.parentNode.innerHTML;
previewNode.parentNode.removeChild(previewNode);
photowolke.myDropzone = new Dropzone("div#files_upload", {
init: function() {
thisDropzone = this;
this.on("success", function(file, responseText) {
//just copy the title from the response of the server
file.title=responseText.photo_title;
//and call with the "new" file the renderer function
photowolke.render_file(file);
});
this.on("addedfile", function(file) {
photowolke.render_file(file);
});
},
previewTemplate: previewTemplate,
});
//this is for loading from a local json to show existing files
$.each(photowolke.arr_photos, function(key, value) {
var mockFile = {
name: value.name,
size: value.size,
title: value.title,
id: value.id,
owner_id: value.owner_id
};
photowolke.myDropzone.emit("addedfile", mockFile);
// And optionally show the thumbnail of the file:
photowolke.myDropzone.emit("thumbnail", mockFile, value.path);
// Make sure that there is no progress bar, etc...
photowolke.myDropzone.emit("complete", mockFile);
});
});
},
};
And there is my template for the preview:
<div class="dropzone-previews" id="files_upload" name="files_upload">
<div id="template" class="file-row">
<!-- This is used as the file preview template -->
<div>
<span class="preview"><img data-dz-thumbnail width="150" /></span>
</div>
<div>
<input type="text" data-dz-title class="title" placeholder="title"/>
<p class="name" data-dz-name></p><p class="size" data-dz-size></p>
<strong class="error text-danger" data-dz-errormessage></strong>
</div>
<div>
<div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div class="progress-bar progress-bar-success" style="width:0%;" data-dz-uploadprogress></div>
</div>
</div>
</div>

Related

How to autofocus an input field in semantic-ui-react?

I'm having a difficult time autofocusing an input field with semantic-ui-react. The documentation doesn't seem to include an autoFocus prop and the focus prop doesn't place the cursor inside the input field as would be expected.
<Form onSubmit={this.handleFormSubmit}>
<Form.Field>
<Form.Input
onChange={e => this.setState({ username: e.target.value })}
placeholder='Enter your username'
fluid />
</Form.Field>
</Form>
EDIT: This code works:
<Form onSubmit={this.handleFormSubmit}>
<Form.Input
onChange={e => this.setState({ username: e.target.value })}
placeholder="Enter your username"
autoFocus
fluid />
</Form>
The focus prop is purely to add a focus effect on the input's appareance, it does not actually set the focus.
Any props unused by Semantic are passed down to the DOM element, so if you set an autoFocus prop, it should go down to the input.
However, as explained in the Form documentation:
Form.Input
Sugar for <Form.Field control={Input} />.
So your code should rather be:
const yourForm = (
<Form onSubmit={this.handleFormSubmit}>
<Form.Input
onChange={e => this.setState({ username: e.target.value })}
onSelect={() => this.setState({ usernameErr: false })}
placeholder="Enter your username"
error={usernameErr}
iconPosition="left"
name="username"
size="large"
icon="user"
fluid
autoFocus
/>
</Form>
)
Note that this only works if you want the focus to happen right when the wrapper component is mounted. If you want to focus the input after it has been mounted, you have to use a ref and call the focus() method on it, just as showed in the documentation, like so:
class InputExampleRefFocus extends Component {
handleRef = (c) => {
this.inputRef = c
}
focus = () => {
this.inputRef.focus()
}
render() {
return (
<div>
<Button content='focus' onClick={this.focus} />
<Input ref={this.handleRef} placeholder='Search...' />
</div>
)
}
}
Hope that helps!
I would have assumed that semantic UI would pass all unknown props to the root element, the input. So if it does, you should be able to add the autoFocus attribute to it, if not, you will have to control which input is being focused in your state.
<Input placeholder='Search...' focus={this.state.focusedElement === "search"}/>
In order to tell the input field to focus, you need to create a reference (ref) to the input field as follows:
import React, { useState, useRef } from 'react';
import { Input, Button } from 'semantic-ui-react';
const SearchInputExample = () => {
const [searchValue, setSearchValue] = useState('');
// Create reference to the input field
const searchRef = useRef(null);
const handleSearchValueChange = event => setSearchValue(event.target.value);
return (
<div>
<Input
placeholder="Search..."
// Assign the ref created to a ref attribute
ref={searchRef}
value={searchValue}
onChange={handleSearchValueChange}
/>
<Button
onClick={() => {
setSearchValue('');
// Use the ref assigned to put the focus inside the input
searchRef.current.focus();
}}
>
Clear search (and focus)
</Button>
</div>
);
};
export default SearchInputExample;
You can read more about the useRef() hook here

I'm struggling to create a new comment the correct way on the frontend ember

I am trying to create a comment with this addComment action where I want to use the input text as the comment text and do a save to create the comment.
I couldn't connect the input box to comment.body because the position of this code does not have the comment model available.
I created a body field on the item model so I connect item.body to the text box and then use this as the comment.body when creating the comment which seems very wrong. Does anyone have any suggestions as to how to do this the correct way?
<form class="comments-list__add-comment add-comment" action="">
{{input type="text" class="add-comment__input" name="" value=item.body placeholder="Please add a comment"}}
<button {{action "addComment" item}} type="button" class="btn add-comment__submit" name="button">Add comment</button>
</form>
addComment(item){
const plan = item.get('plan');
const text = this.get('item.body');
const currentUserName = plan.get('appConfig.currentUser');
const currentUserId = plan.get('appConfig.currentUserId');
const itemid = item.id;
if(text.trim() !== ''){
let comment = this.get('item.store').createRecord('comment', {
body: text,
createdAt: new Date(),
commentableId: itemid,
commentableType: 'Plan',
unread: true,
commenterName: currentUserName,
commenterId: currentUserId
});
item.get('comments').pushObject(comment);
comment.save();
item.set('displayAddCommentForm', false);
this.set('item.body', '');
}
},
You can create a comment record in your route and assign it to a controller property. Then you can bind your template to the controller's comment property, like this:
route
export default Ember.Route.extend({
setupController(controller, model) {
this._super(...arguments);
controller.set('comment', this.store.createRecord('comment');
}
});
template
{{input value=comment.body}}
Then, in your Route's save method:
let comment= this.controller.get('comment');
// the remainder of your save should follow...
// At this point, comment.body should have the text entered by user

Google places API only autocompletes street name field . . . but throws no errors

I'm new to Google's Places API. I'm trying to get a Django form to autocomplete, but for some reason, only one of the fields (Street 2) will autocomplete. The rest are just blank. And my console throws no errors, so I really have no idea what the issue is.
The other WEIRD thing . . . the inputs are holding the initial values that I passed to the form from the Django view even though the google autocomplete javascript has set them to "" before trying to autofill them. Is that normal?
Here's the HTML:
<div id="locationField">
<input id="autocomplete" name="search_address" onFocus="geolocate()" placeholder="Search for your address . . ." type="text" />
</div>
<hr class="hr-style">
<div >
<strong>Street</strong>
<input id="street_name" name="street" type="text" value="1030 E State Street" />
</div>
<div >
<strong>Street 2</strong>
<input id="route" name="street2" type="text" value="Apt. 2A" />
</div>
<div >
<strong>City</strong>
<input id="city" name="city" type="text" value="Los Angeles" />
</div>
<div class="6u 12u$(small) ">
<strong>State</strong>
<select id="state" name="state">
<!-- options removed for brevity's sake -->
</div>
<div class="6u 12u$(small) ">
<strong>Zip</strong>
<input id="zipcode" name="zipcode" type="text" value="90210" />
</div>
And the javascript, just copied from Google and modified with my input id's:
//geosearch powered by Google
// This example displays an address form, using the autocomplete feature
// of the Google Places API to help users fill in the information.
// This example requires the Places library. Include the libraries=places
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places">
$(function(){
initAutocomplete();
});
var placeSearch, autocomplete;
var componentForm = {
street_name: 'short_name',
route: 'long_name',
city: 'long_name',
state: 'short_name',
zipcode: 'short_name'
};
function initAutocomplete() {
// Create the autocomplete object, restricting the search to geographical
// location types.
autocomplete = new google.maps.places.Autocomplete(
/** #type {!HTMLInputElement} */(document.getElementById('autocomplete')),
{types: ['geocode']});
// When the user selects an address from the dropdown, populate the address
// fields in the form.
autocomplete.addListener('place_changed', fillInAddress);
}
// [START region_fillform]
function fillInAddress() {
// Get the place details from the autocomplete object.
var place = autocomplete.getPlace();
for (var component in componentForm) {
document.getElementById(component).value = "";
document.getElementById(component).disabled = false;
}
// Get each component of the address from the place details
// and fill the corresponding field on the form.
for (var i = 0; i < place.address_components.length; i++) {
var addressType = place.address_components[i].types[0];
if (componentForm[addressType]) {
var val = place.address_components[i][componentForm[addressType]];
document.getElementById(addressType).value = val;
}
}
}
// [END region_fillform]
// [START region_geolocation]
// Bias the autocomplete object to the user's geographical location,
// as supplied by the browser's 'navigator.geolocation' object.
function geolocate() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var geolocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
var circle = new google.maps.Circle({
center: geolocation,
radius: position.coords.accuracy
});
autocomplete.setBounds(circle.getBounds());
});
}
}
// [END region_geolocation
I'm thinking it has got to be failing somehow at this if statement in fillinAddress(), but I can't tell why:
if (componentForm[addressType]) {
var val = place.address_components[i][componentForm[addressType]];
document.getElementById(addressType).value = val;
Any help would be appreciated! And here's a screenshot of the form!
Turns out you can NOT rename the address form components. (I had renamed 'locality' to be 'city' and 'administrative_area_level_1' to be 'state.') I'm so new to this; I had no idea! I just thought that the variable names in the javascript had to match your input id's in your HTML. Turns out the address form components have to stay:
street_number: 'short_name',
route: 'long_name',
locality: 'long_name',
administrative_area_level_1: 'short_name',
country: 'long_name',
postal_code: 'short_name'

How to make react.js play nice together with zurb reveal modal form

I am trying to integrate zurb reveal with form into react component. So far next code properly displays modal form:
ModalForm = React.createClass({
handleSubmit: function(attrs) {
this.props.onSubmit(attrs);
return false;
},
render: function(){
return(
<div>
Add new
<div id="formModal" className="reveal-modal" data-reveal>
<h4>Add something new</h4>
<Form onSubmit={this.handleSubmit} />
<a className="close-reveal-modal">×</a>
</div>
</div>
);
}
});
The Form component is pretty standard:
Form = React.createClass({
handleSubmit: function() {
var body = this.refs.body.getDOMNode().value.trim();
if (!body) {
return false;
}
this.props.onSubmit({body: body});
this.refs.body.getDOMNode().value = '';
return false;
},
render: function(){
return(
<form onSubmit={this.handleSubmit}>
<textarea name="body" placeholder="Say something..." ref="body" />
<input type="submit" value="Send" className="button" />
</form>
);
}
});
Problem: When I render form component within modal form component and enter something into form input then I see in console exception Uncaught object. This is a stack:
Uncaught object
invariant
ReactMount.findComponentRoot
ReactMount.findReactNodeByID
getNode
...
If I just render form component directly in the parent component then everything works. Could anybody help please?
In short, you're doing this wrong and this is not a bug in react.
If you use any kind of plugin that modifies the react component's dom nodes then it's going to break things in one way or another.
What you should be doing instead is using react itself, and complementary css, to position the component in the way you'd like for your modal dialog.
I would suggest creating a component that uses react's statics component property to define a couple of functions wrapping renderComponent to give you a nice clean function call to show or hide a react dialog. Here's a cut down example of something I've used in the past. NB: It does use jQuery but you could replace the jQ with standard js api calls to things like elementById and etc if you don't want the jQuery code.
window.MyDialog = React.createClass({
propTypes: {
title: React.PropTypes.string.isRequired,
content: React.PropTypes.string.isRequired
},
statics: {
// open a dialog with props object as props
open: function(props) {
var $anchor = $('#dialog-anchor');
if (!$anchor.length) {
$anchor = $('<div></div>')
.prop('id', 'dialog-anchor');
.appendTo('body');
}
return React.renderComponent(
MyDialog(props),
$anchor.get(0)
);
},
// close a dialog
close: function() {
React.unmountComponentAtNode($('#dialog-anchor').get(0));
}
},
// when dialog opens, add a keyup event handler to body
componentDidMount: function() {
$('body').on('keyup.myDialog', this.globalKeyupHandler);
},
// when dialog closes, clean up the bound keyup event handler on body
componentWillUnmount: function() {
$('body').off('keyup.myDialog');
},
// handles keyup events on body
globalKeyupHandler: function(e) {
if (e.keyCode == 27) { // ESC key
// close the dialog
this.statics.close();
}
},
// Extremely basic dialog dom layout - use your own
render: function() {
<div className="dialog">
<div className="title-bar">
<div className="title">{this.props.title}</div>
<a href="#" className="close" onClick={this.closeHandler}>
</div>
</div>
<div className="content">
{this.props.content}
</div>
</div>
}
});
You then open a dialog by calling:
MyDialog.open({title: 'Dialog Title', content: 'My dialog content'});
And close it with
MyDialog.close()
The dialog always attaches to a new dom node directly under body with id 'dialog-anchor'. If you open a dialog when one is already open, it will simply update the dom based on new props (or not if they're the same).
Of course passing the content of the dialog as a props argument isn't particularly useful. I usually extend below to either parse markdown -> html for the content or get some html via an ajax request inside the component when supplying a url as a prop instead.
I know the above code isn't exactly what you were looking for but I don't think there's a good way to make a dom-modifying plugin work with react. You can never assume that the dom representation of the react component is static and therefore it can't be manipulated by a 3rd party plugin successfully. I honestly think if you want to use react in this way you should re-evaluate why you're using the framework.
That said, I think the code above is a great starting point for a dialog in which all manipulation occurs inside the component, which afterall is what reactjs is all about!
NB: code was written very quickly from memory and not actually tested in it's current form so sorry if there are some minor syntax errors or something.
Here is how to do what Mike did, but using a zf reveal modal:
var Dialog = React.createClass({
statics: {
open: function(){
this.$dialog = $('#my-dialog');
if (!this.$dialog.length) {
this.$dialog = $('<div id="my-dialog" class="reveal-modal" data-reveal role="dialog"></div>')
.appendTo('body');
}
this.$dialog.foundation('reveal', 'open');
return React.render(
<Dialog close={this.close.bind(this)}/>,
this.$dialog[0]
);
},
close: function(){
if(!this.$dialog || !this.$dialog.length) {
return;
}
React.unmountComponentAtNode(this.$dialog[0]);
this.$dialog.foundation('reveal', 'close');
},
},
render : function() {
return (
<div>
<h1>This gets rendered into the modal</h1>
<a href="#" className="button" onClick={this.props.close}>Close</a>
</div>
);
}
});

Returning a proper response for post request in Django

<form id="login_frm" method="post" action = "/login/user_auth/">
<fieldset>
<legend>Login:</legend>
<label for="id_email">Email</label>
<input type="text" name="email" id="id_email" />
<label for="id_password">Password</label>
<input type="password" name="password" id="id_password" />
</fieldset>
<input name = "login" type="submit" value="Login" />
</form>
$(document).ready(function () {
$('#login_frm').submit(function() {
var $form = $(this);
$.post('/login/user_auth/' , form.serialize(), function(data) {
// alert ("function");
alert (data);
});
return false;
});
});
Django View:
def login_user(request):
if request.method == 'POST':
# perform all logic / and db access
data = "hello"
return HttpResponse(data)
# return HttpResponse ('success.html')
I have been stuck on this all afternoon.
When I return data as my response, for some reason, the browser displays "Hello" by loading a blank webpage with just "Hello" written on it; in the JavaScript function above, alert (data); is never called (I cannot understand why).
I am unable to render the success.html. I believe that if I write render_to_response inside the HttpResponse, I will solve this problem. However I think making point 1 work is a first priority.
Goal
After the post, I would like to capture the returned response from the server (whether it is just the "hello" message, or a webpage that displays a success message- stored in "success.html") and display it in place of the login_frm without having the browser refresh a new webpage.
Interesting. form is undefined (you defined it as $form) and changing to $form fixed the problem.
$(document).ready(function () {
$('#login_frm').submit(function() {
var $form = $(this);
$.post('/login/user_auth/' , $form.serialize(), function(data) {
// alert ("function");
alert (data);
});
return false;
});
});
You might want to use something like event.preventDefault() so that future errors are not hidden from you like this. $('form').submit(function(e){ e.preventDefault(); .....})