Vue Template not updating after data is changed - django

I am new to Vue, and I have played around with small vue applications, but this is the first project I've done using vue and webpack (vue-cli).
I have a django rest API set up, for which I then use vue to consume the API and display the data. The issue i'm having is when I want to do anything the templates aren't updating. The data is loaded in correctly, but if I try and do a #click event and then a v-show (to toggle displaying an item) it doesn't change the view.
Example,
lets say I have a list of pubs that I consume from the API.
I get them via axios, and store them in the data function in a array called pubs:
<script>
import axios from 'axios'
export default {
name: 'Pubs',
data () {
return {
pubs: [
{
pub_id:'',
name:'',
address:'',
showPub: false,
},
]
}
},
created: function () {
this.loadPubs();
},
methods: {
loadPubs: function () {
var vm = this;
axios.get('http://127.0.0.1:8000/api/pubs/')
.then(function (response) {
vm.pubs = response.data;
vm.pubs.forEach(function (pub) {
pub.showPub = true;
});
console.log("loaded");
})
.catch(function (error) {
this.pubs = 'An error occured.' + error;
});
},
togglePub: function (pub) {
pub.showPub = !pub.showPub;
console.log(pub.showPub);
console.log(pub);
return pub;
},
}
}
</script>
The template could be:
<template>
<div class="pubs">
<h1>VuePubs</h1>
<ul>
<li v-for="pub in pubs">
<section>
<h2 #click="togglePub(pub)">{{ pub.name }}</h2>
<section v-show="pub.showPub">
<h3>{{ pub.address }}</h3>
<h3>{{ pub.postcode }}</h3>
<h3>{{ pub.showPub }}</h3>
</section>
</section>
</li>
</ul>
</div>
</template>
I can see that the data is changing in the model, thanks to the vue-dev tools, but the template doesn't change. The section doesn't hide and the h3 tags don't update with the new showPub field.
Any help would be greatly appreciated!

Related

500 (Internal Server Error) when fetching from Django Rest API default router (to create object)

I am trying to make Django and React to work together, and although I have a basic knowledge of Django (I can develop a site), I am just starting to learn React.
My project structure is based on this video which is loosely based on this blog in turn.
My API works perfectly fine, I can create, retrieve, update, and delete objects without problems.
Also, on my frontend, I have managed to correctly display a list of the items from my database, by fetching the related API Url.
Just to make it clear, here is the main 'backend' urls.py:
from rest_framework import routers
from .views import AssessmentViewSet
router = routers.DefaultRouter()
router.register('api/assessments', AssessmentViewSet, 'assessments')
urlpatterns = router.urls
Now, from what I see on my API webpage, objects are created and retrieved on the same Url, and namely http://127.0.0.1:8000/api/assessments/, as you can see from this screenshot.
The only difference, I guess, is that when I want to create an object, all I have to do ist to specify inside the fetch function, that I am making a POST request. This is done in my CreateAssessmentForm.jsx file:
import React, { Component } from "react";
class CreateAssessmentForm extends Component {
state = {
loading: false,
title: "",
is_finished: false,
created_at: "",
};
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
let value = event.target.value;
console.log("Value: ", value);
this.setState({
title: value,
});
}
handleSubmit(event) {
event.preventDefault(console.log("Item:", this.state));
fetch("http://127.0.0.1:8000/api/assessments", {
method: "POST",
headers: {
"Content-type": "application/json",
},
body: JSON.stringify(this.state.title),
}).then(response => {
this.setState({
loading: false,
title: "",
is_finished: false,
created_at: "",
}).catch(function (error) {
console.log("ERROR:", error);
});
});
}
render() {
return (
<div>
{this.state.loading ? (
<div className="text-center">Loading...</div>
) : (
<form onSubmit={this.handleSubmit} method="POST">
<h2 className="text-center">Assessment Title</h2>
<div className="form-group">
<label htmlFor="Title">Title</label>
<input
onChange={this.handleChange}
type="text"
className="form-control"
id="TitleInput"
value={this.state.title}
placeholder="E.g. History Quiz"
/>
</div>
<button type="submit" className="btn btn-primary">
Submit
</button>
</form>
)}
</div>
);
}
}
export default CreateAssessmentForm;
When I type something inside the title input, the console logs properly every entry, and when I press the submit button it also logs an object (which as you can see from the code above is my actual state - {this.state}).
The problem is that when submitting, I get a 500 (Internal Server Error) and the object is not saved on the backend database.
Does anyone know what I am doing wrong?
Best shot would be to check what are the django logs as you get Internal Server Error. It should be clear enough of what you are doing wrong, if not, please post the error log.

Ember Octane How to Get Error Messages to be Displayed?

This question is related to Ember Octane Upgrade How to pass values from component to controller
How do I get Ember Octane to display on the webpage? For instance, if the old password and new password are the same we want that error to display on the page.
Ember-Twiddle here
Code example:
User Input Form
ChangePasswordForm.hbs
<div class="middle-box text-center loginscreen animated fadeInDown">
<div>
<h3>Change Password</h3>
<form class="m-t" role="form" {{on "submit" this.changePassword}}>
{{#each this.errors as |error|}}
<div class="error-alert">{{error.detail}}</div>
{{/each}}
<div class="form-group">
<Input #type="password" class="form-control" placeholder="Old Password" #value={{this.oldPassword}} required="true" />
</div>
<div class="form-group">
<Input #type="password" class="form-control" placeholder="New Password" #value={{this.newPassword}} required="true" />
</div>
<div class="form-group">
<Input #type="password" class="form-control" placeholder="Confirm Password" #value={{this.confirmPassword}} required="true" />
</div>
<div>
<button type="submit" class="btn btn-primary block full-width m-b">Submit</button>
</div>
</form>
</div>
</div>
Template Component
ChangePassword.hbs
<Clients::ChangePasswordForm #chgpwd={{this.model}} #changePassword={{action 'changePassword'}} #errors={{this.errors}} />
Component
ChangePasswordForm.js
import Component from '#glimmer/component';
import { tracked } from '#glimmer/tracking';
import { action } from '#ember/object';
export default class ChangePasswordForm extends Component {
#tracked oldPassword;
#tracked newPassword;
#tracked confirmPassword;
#tracked errors = [];
#action
changeOldPassword(ev) {
this.oldPassword = ev.target.value;
}
#action
changeNewPassword(ev) {
this.newPassword = ev.target.value;
}
#action
changeConfirmPassword(ev) {
this.confirmPassword = ev.target.value;
}
#action
changePassword(ev) {
ev.preventDefault();
this.args.changePassword({
oldPassword: this.oldPassword,
newPassword: this.newPassword,
confirmPassword: this.confirmPassword
});
}
}
Controller
ChangePassword.js
import Controller from '#ember/controller';
import { inject as service } from '#ember/service';
import { action } from '#ember/object';
export default class ChangePassword extends Controller {
#service ajax
#service session
#action
changePassword(attrs) {
if(attrs.newPassword == attrs.oldPassword)
{
shown in the UI.
this.set('errors', [{
detail: "The old password and new password are the same. The password was not changed.",
status: 1003,
title: 'Change Password Failed'
}]);
}
else if(attrs.newPassword != attrs.confirmPassword)
{
this.set('errors', [{
detail: "The new password and confirm password must be the same value. The password was not changed.",
status: 1003,
title: 'Change Password Failed'
}]);
}
else
{
let token = this.get('session.data.authenticated.token');
this.ajax.request(this.store.adapterFor('application').get('host') + "/clients/change-password", {
method: 'POST',
data: JSON.stringify({
data: {
attributes: {
"old-password" : attrs.oldPassword,
"new-password" : attrs.newPassword,
"confirm-password" : attrs.confirmPassword
},
type: 'change-passwords'
}
}),
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/vnd.api+json',
'Accept': 'application/vnd.api+json'
}
})
.then(() => {
this.transitionToRoute('clients.change-password-success');
})
.catch((ex) => {
this.set('errors', ex.payload.errors);
});
}
}
}
Model
ChangePassword.js
import Route from '#ember/routing/route';
import AbcAuthenticatedRouteMixin from '../../mixins/efa-authenticated-route-mixin';
export default class ChangePasswordRoute extends Route.extend(AbcAuthenticatedRouteMixin) {
model() {
// Return a new model.
return {
oldPassword: '',
newPassword: '',
confirmPassword: ''
};
}
}
In your form component, you reference the errors like
{{#each this.errors as |error|}}
<div class="error-alert">{{error.detail}}</div>
{{/each}}
From class components -> glimmer components, there's been a fundamental shift in the way you access the component's arguments vs the component's own values (for the better!)
In class components, arguments are assigned directly to the class
instance. This has caused a lot of issues over the years, from methods
and actions being overwritten, to unclear code where the difference
between internal class values and arguments is hard to reason about.
New components solve this by placing all arguments in an object
available as the args property.
When referencing an argument to a component in javascript, you use: this.args.someArg. In the template, you use the shorthand #someArg. These are known as "named arguments" (feel free to read the rfc for more info). When you, as you did here, use this.errors in your template, you are looking for a local component property errors.
Just to emphasize, this does not work because errors is passed to Clients::ChangePasswordForm via #errors here:
<Clients::ChangePasswordForm #chgpwd={{this.model}} #changePassword={{action 'changePassword'}} #errors={{this.errors}} />
and must be #errors in the template
{{#each #errors as |error|}}
<div class="error-alert">{{error.detail}}</div>
{{/each}}

Nuxt JS Apollo data only available after page refresh

I am fetching some data using Apollo inside of Nuxt. Somehow, when navigating to that page I get an error of
Cannot read property 'image' of undefined
When I refresh the page, everything works as expected.
I have a found a few threads of people having similar issues but no solution seems to work for me :/
This is my template file right now:
/products/_slug.vue
<template>
<section class="container">
<div class="top">
<img :src="product.image.url"/>
<h1>{{ product.name }}</h1>
</div>
</section>
</template>
<script>
import gql from 'graphql-tag'
export default {
apollo: {
product: {
query: gql`
query Product($slug: String!) {
product(filter: { slug: { eq: $slug } }) {
slug
name
image {
url
}
}
}
`,
prefetch({ route }) {
return {
slug: route.params.slug
}
},
variables() {
return {
slug: this.$route.params.slug
}
}
}
}
}
</script>
Basically the $apolloData stays empty unless I refresh the page. Any ideas would be much appreciated
EDIT
Got one step closer (I think). Before, everything (image.url and name) would be undefined when navigating to the page for the first time.
I added:
data() {
return {
product: []
};
}
at the top of my export and now at least the name is always defined so if I remove the image, everything works as expected. Just the image.url keeps being undefined.
One thing I noticed (not sure how relevant) is that this issue only occurs using the , if I use a normal a tag it works but of course takes away the vue magic.
EDIT-2
So somehow if I downgrade Nuxt to version 1.0.0 everything works fine
I stumbled on this issue as well, and found it hidden in the Vue Apollo documents.
Although quite similar to the OP's reply, it appears the official way is to use the "$loadingKey" property.
It's quite confusing in the documents because there are so many things going on.
https://vue-apollo.netlify.com/guide/apollo/queries.html#loading-state
<template>
<main
v-if="!loading"
class="my-8 mb-4"
>
<div class="w-3/4 mx-auto mb-16">
<h2 class="mx-auto text-4xl text-center heading-underline">
{{ page.title }}
</h2>
<div
class="content"
v-html="page.content.html"
></div>
</div>
</main>
</template>
<script>
import { page } from "~/graphql/page";
export default {
name: 'AboutPage',
data: () => ({
loading: 0
}),
apollo: {
$loadingKey: 'loading',
page: {
query: page,
variables: {
slug: "about"
}
},
}
}
</script>
If you need to use a reactive property within vue such as a slug, you can do so with the following.
<template>
<main
v-if="!loading"
class="my-8 mb-4"
>
<div class="w-3/4 mx-auto mb-16">
<h2 class="mx-auto text-4xl text-center heading-underline">
{{ page.title }}
</h2>
<div
class="content"
v-html="page.content.html"
></div>
</div>
</main>
</template>
<script>
import { page } from "~/graphql/page";
export default {
name: 'AboutPage',
data: () => ({
loading: 0
}),
apollo: {
$loadingKey: 'loading',
page: {
query: page,
variables() {
return {
slug: this.$route.params.slug
}
}
},
}
}
</script>
I think it's only a problem of timing on page load.
You should either iterate on products, if you have more than one, or have a v-if="product != null" on a product container, that will render only once the data is fetched from GraphQL.
In that way you'll use the object in your HTML only when it's really fetched and avoid reading properties from undefined.
To fix this, you add v-if="!$apollo.loading" to the HTML container in which you're taying to use a reactive prop.

Unable to locate button to simulate click - Unit Testing React w/ Mocha, Chai, Enzyme

I'm attempting to simulate a button click with Enzyme. I've been able to write simple tests if an element renders, however the fun tests such as button clicks etc. are falling short.
In this example the error in terminal is:
1) Front End #Profile Component clicks a button :
Error: This method is only meant to be run on single node. 0 found instead.
at ShallowWrapper.single (node_modules/enzyme/build/ShallowWrapper.js:1093:17)
at ShallowWrapper.props (node_modules/enzyme/build/ShallowWrapper.js:532:21)
at ShallowWrapper.prop (node_modules/enzyme/build/ShallowWrapper.js:732:21)
at ShallowWrapper.simulate (node_modules/enzyme/build/ShallowWrapper.js:505:28)
at Context.<anonymous> (cmpnt-profile.spec.js:36:32)
the unit test is:
describe('Front End #Profile Component', () => {
const wrapper = shallow(<Profile/>);
...(other tests here)...
it('clicks a button ', () => {
wrapper.find('button').simulate('click');
expect(onButtonClick.calledOnce).to.equal(true);
})
});
the component is:
import _ from 'lodash';
import React, { Component, PropTypes } from 'react';
import { reduxForm } from 'redux-form';
import ExpireAlert from '../components/alert';
import SocialAccount from '../components/socialAccounts'
const FIELDS = {
name : {
type : 'input',
label : 'name'
},
username : {
type : 'input',
label: 'username'
},
email : {
type : 'input',
label: 'email'
}
};
let alert = false;
export default class Profile extends Component {
componentWillReceiveProps(nextProps){
if(nextProps.userIsUpdated){
alert = !alert
}
}
handleSubmit(userData) { // update profile
userData.id = this.props.userInfo.id
this.props.updateUserInfo(userData)
}
renderField(fieldConfig, field) { // one helper per ea field declared
const fieldHelper = this.props.fields[field];
return (
<label>{fieldConfig.label}
<fieldConfig.type type="text" placeholder={fieldConfig.label} {...fieldHelper}/>
{fieldHelper.touched && fieldHelper.error && <div>{fieldHelper.error}</div>}
</label>
);
}
render() {
const {resetForm, handleSubmit, submitting, initialValues} = this.props;
return (
<div className="login">
<div className="row">
<div className="small-12 large-7 large-centered columns">
<div className="component-wrapper">
<ExpireAlert
set={this.props.userIsUpdated}
reset={this.props.resetAlert}
status="success"
delay={3000}>
<strong> That was a splendid update! </strong>
</ExpireAlert>
<h3>Your Profile</h3>
<SocialAccount
userInfo={this.props.userInfo}
unlinkSocialAcc={this.props.unlinkSocialAcc}
/>
<form className="profile-form" onSubmit={this.props.handleSubmit(this.handleSubmit.bind(this))}>
<div className="row">
<div className="small-12 large-4 columns">
<img className="image" src={this.props.userInfo.img_url}/>
</div>
<div className="small-12 large-8 columns">
{_.map(FIELDS, this.renderField.bind(this))}
</div>
<div className="small-12 columns">
<button type="submit" className="primary button expanded" disabled={submitting}>
{submitting ? <i/> : <i/>} Update
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
);
}
}
const validate = values => {
const errors = {}
if (!values.name) {
errors.name = 'Name is Required'
}
if (!values.username) {
errors.username = 'Username is Required'
} else if (values.username.length > 30) {
errors.username = 'Must be 30 characters or less'
}
if (!values.email) {
errors.email = 'Email is Required'
} else if (!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = 'Invalid email address'
}
return errors;
}
Profile.propTypes = {
fields: PropTypes.object.isRequired,
handleSubmit: PropTypes.func.isRequired,
resetForm: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired
}
export default reduxForm({
form: 'Profile',
fields: _.keys(FIELDS),
validate
})(Profile)
Any suggestions appreciated. I would love to make some amazing unit tests!
ZekeDroid is correct. Another way to handle the situation though would be to use mount, which is Enzyme's way to deep render components, meaning the entire tree of components are rendered. This would be considered an integration test, instead of a unit test. ZekeDroid mentions "This assumes your component can handle not receiving whatever reduxForm supplies it with, which is good practice anyhow as you will find it easier to test.", and I believe he's referencing unit testing. Ideally, your tests should include both unit tests and integration tests. I created a simple project to show how to do both unit and integration tests with redux-form. See this repo.
You have two export defaults on your file. This will naturally mean that the second one is overriding the first. If you really want to be able to export the reduxForm version and the component itself, as you should for testing, then remove the word default from the component.
On your test, you shallow render, meaning no children are rendered. Since you import the reduxForm, nothing much will get rendered, definitely not your Profile. Therefore, if you removed the default keyword like I mentioned, your test will start working when you import it using a named import:
import { Profile } from 'path/to/component';
*This assumes your component can handle not receiving whatever reduxForm supplies it with, which is good practice anyhow as you will find it easier to test.

How to add text input to dropzone upload

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>