I have a snippet below which is essentially my entire code block at this point, and essentially it creates a div and when you click "add another zone" it will clone that div. This allows the user to enter multiple lines of info and each have their own result and image.
The issue is that I'm successfully cloning everything with it's own unique identity thanks to my card setup. However, dropzone is not replicating. The first file dropzone form will work perfectly, but when I clone the div and have 2 or more dropzone insnstances on the page they don't work (they don't show the upload image text or anything)
How can I successfully apply my same logic to the dropzone instance here?
new Vue({
components: {},
el: "#commonNameDiv",
data() {
return {
searchString: [''],
results: [],
savedAttributes: [],
cards: [],
showList: false,
zoneNumber:[],
imageZoneNames: [] }
},
methods: {
autoComplete(ev, card) {
this.results = [];
console.log(this.searchString);
if (ev.target.value.length > 2) {
axios.get('/product/parts/components/search', {
params: {
searchString: ev.target.value
}
}).then(response => {
card.results = response.data;
this.showList = true;
console.log(this.results);
console.log(this.searchString);
});
}
},
saveAttribute(result, card) {
card.value = result.attribute_value;
card.results = [];
card.zone = this.zoneNumber;
this.showList = false;
},
addCard: function() {
this.cards.push({
index: "",
value: "",
zoneNumber: "",
results: [],
componentImage:""
});
console.log(this.cards);
},
hideDropdown() {
this.showList = false;
},
},
created() {
this.addCard();
let instance = this;
Dropzone.options = {
maxFilesize: 12,
renameFile: function (file) {
var dt = new Date();
var time = dt.getTime();
return time + file.name;
},
acceptedFiles: ".jpeg,.jpg,.png,.gif",
addRemoveLinks: true,
timeout: 50000,
removedfile: function (file) {
console.log(file.upload.filename);
var name = file.upload.filename;
var fileRef;
return (fileRef = file.previewElement) != null ?
fileRef.parentNode.removeChild(file.previewElement) : void 0;
},
init: function() {
this.on("addedfile",
function(file) {
instance.imageZoneNames.push({name: file.upload.filename, desc: 'Line Drawing'});
console.log(file);
console.log(instance.imageZoneNames);
});
}
};
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.5.0/dropzone.js"></script>
<div id="commonNameDiv">
<div class="uk-grid" v-for="(card, i) in cards" :key="i">
<div class="uk-width-1-10" >
<input v-model=" card.zoneNumber" size="4" type="text" name="mapNumber">
</div>
<div class="uk-width-6-10">
<input
style="width:100%"
placeholder="what are you looking for?"
v-model="card.value"
v-on:keyup="autoComplete($event, card)"
>
<div v-if="showList" class="panel-footer componentList" v-if="card.results.length">
<ul>
<li v-for="(result, i) in card.results" :key="i">
<a v-on:click="saveAttribute(result, card)">#{{ result.attribute_value }}</a>
</li>
</ul>
</div>
</div>
<div class="uk-width-3-10">
<form method="post" action="{{url('product/parts/upload/store')}}" enctype="multipart/form-data"
class="dropzone">
</form>
</div>
</div>
<div style="height: 35px;">
</div>
<div>
<a v-on:click="addCard">Add another zone</a>
</div>
</div>
When you instantiate the Dropzone class, it automatically looks for elements to transform in dropzones (by default, elements with the .dropzone class).
It looks like you want to dynamically add elements that are dropzones. Then you need to trigger the dropzone transformation yourself.
I would suggest you disable the autoDiscover option, and manually designates each element you want to transform into dropzones :
addCard() {
this.cards.push({
...
});
let cardIndex = this.cards.length - 1;
// Waiting for the element #dropzone-X to exist in DOM
Vue.nextTick(function () {
new Dropzone("#dropzone-"+cardIndex, {
...
});
});
},
created() {
...
Dropzone.autoDiscover = false
// no new Dropzone()
...
// Starting setup
this.addCard();
},
<form ... class="dropzone" v-bind:id="'dropzone-'+i">
Working jsbin
There are several ways to select the element to transform ($refs, ids, classes), here I'm suggesting ids.
See the doc on programmatically creating dropzones
Actually it is being created, but the Dropzone is not being reconstructed.
I think you have to create a new instance of the Dropzone.
if you try to insert:
created() {
this.addCard();
var myDropzone = new Dropzone('.dropzone')
let instance = this;
Dropzone.options.myDropzone = {
or even add the options to the addCard method or set a setupDropzones method and add it to the addCard method.
Related
I chose Vue.js over React and Angular, because it felt much more easier to migrate my existing website (old technologies, ASP.NET Razor Pages inside Visual Studio and no npm) to new technologies.
Everything went great. I changed small portions of my website into Vue applications. For example:
Mini cart viewer
Comment form
Rating
Add to cart button
Dynamic forms
However, now that I want to show a dynamic form inside a modal dialog, things don't work.
The point is, modal dialog is a Vue application, and the dynamic form is also another Vue application. So, basically what I do is to load a Vue application inside another Vue application.
Here's my code for modal:
var modalAppJson = {
data() {
return {
isShown: false
}
},
methods: {
show: function (data) {
var _this = this;
_this.isShown = true;
},
hide: function () {
var _this = this;
_this.isShown = false;
}
}
};
var modalApp = Vue.createApp(modalAppJson);
modalApp.config.globalProperties.app = app;
modalApp.mount('.modalInstance');
And here's my code for dynamic form (Vue.js inside Razor Pages):
formApp = {
data() {
return {
form: {
},
fields: []
}
},
methods: {
addField(key, value, isValid) {
var field;
for (var i = 0; i < this.fields.length; i++) {
if (this.fields[i].key === key) {
field = this.fields[i];
}
}
if (!field) {
field = { key: key, value: value, isValid: isValid };
this.fields.push(field);
}
field.value = value;
field.isValid = isValid;
},
removeField: function () {
},
send() {
var form = $('##(Model.Key)Form');
var loader = $('##Model.LoaderKey');
var results = $('##Model.ResultsKey');
var submitButton = $('#submit_#Model.Key');
submitButton.hide();
loader.show();
results.text('');
app.postJson('action=SaveForm&formKey=#Model.Key', JSON.parse(JSON.stringify(this.form)), function (data) {
submitButton.show();
loader.hide();
results.text('Form is saved successfully');
results.addClass('success');
form[0].reset();
}, function (error) {
submitButton.show();
loader.hide();
results.text(error);
results.addClass('error');
});
}
},
computed: {
isValid: function () {
var result = true;
for (var i = 0; i < this.fields.length; i++) {
if (this.fields[i].isValid === false) {
result = false;
break;
}
}
return result;
}
}
};
Vue.createApp(formApp).mount('##(Model.Key)Form');
And here's an example of HTML for these two apps:
<div class="modalInstance">
<div>
Purchase
</div>
<Transition>
<div v-if="isShown">
<form novalidate id="#(Model.FormKey)Form" class="formInstance">
<h1 class="title">#Html.Raw(Model.DatabaseForm.Title)</h1>
<h3 class="shortDescription">#Html.Raw(Model.DatabaseForm.ShortDescription)</h3>
#if (hasFieldsRenderer)
{
<partial name="/Pages/Form/#(pascalizedKey)FieldsRenderer.cshtml" model='#Model.Fields' />
}
else
{
foreach (var field in Model.Fields)
{
<partial name="/Pages/Fields/#(field.RelatedItems.TypeKey).cshtml" model='field' />
}
}
<div class="actions">
<partial name="#Actions.Save" model='new ActionParameters { Classes="submit button", FormKey=Model.FormKey, Text="Save" }' />
<span id="#(Model.FormKey)Loader" style="display: none;">Saving...</span>
<span id="#(Model.FormKey)Result"></span>
</div>
</form>
</div>
</Transition>
</div>
The top-level <div> is controlled by the modal Vue application, and the <form> element is controlled by the dynamic form Vue application.
However I see this error when I open the dialog:
[Vue warn]: Property "form" was accessed during render but is not defined on instance.
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'Name')
This works if I show the form in the page and not in a modal. But breaks when I put it inside modal.
What can I do to solve this?
I know this has a simple answer but I appear to be stuck. I have an upload image input in a form. Following several tutorials, I have successfully created the upload method. My issue is once the image is uploaded to Firestore storage I use this.$emit('imgurl', downloadURL)
My problem is I do not know how to get that value so when the user submits the form the url gets added to the database.
Parts of the code:
HTML:
<div class="field avatar">
<label for="avatar">Avatar</label>
<input type="file" name="imgurl" accept="image/*" #change="detectFiles($event.target.files)">
<div class="progress-bar green" :style="{ width: progressUpload + '%'}">{{ progressUpload }}%</div>
<img class="avatar" v-bind:src="this.downloadURL">
</div>
Methods:
detectFiles (fileList) {
Array.from(Array(fileList.length).keys()).map( x => {
this.upload(fileList[x])
})
},
upload (file) {
var storage = firebase.storage();
this.uploadTask = storage.ref('avatars/'+file.name).put(file);
}
Watch:
watch: {
uploadTask: function() {
this.uploadTask.on('state_changed', sp => {
this.progressUpload = Math.floor(sp.bytesTransferred / sp.totalBytes * 100)
},
null,
() => {
this.uploadTask.snapshot.ref.getDownloadURL().then(downloadURL => {
this.downloadURL = downloadURL
this.$emit('imgurl', downloadURL)
})
})
}
}
Add to the database:
db.collection('teams').add({
team_name: this.team_name,
team_id: this.team_id,
bio: this.bio,
url: this.imgurl,
}).then(() => {
this.$router.push({ name: 'Admin' })
}).catch(err => {
console.log(err)
})
You can pass a function as a prop to a child component, then call this function passing your downloadURL as argument.
Parent Component
HTML
<child passURL="getDownloadURL">
JS
data: {
return {
downloadURL: null
}
},
methods: {
getDownloadURL: function(url) {
this.downloadURL = url
}
}
Child Component
JS
props: ['passURL'],
Inside your watcher, you can call
this.passURL(downloadURL)
Instead of $emit.
I found the answer. I added a hidden input field
<input type="hidden" name="imgurl" v-model="imgurl">
and replaced the emit with this.imgurl = downloadURL
I have used List.JS before successfully, but this time I'm trying to use it with a Vue.JS rendering of a list from JSON data.
I have a button at the top that when clicked should show only the QB position player.
Unfortunately I just get nothing, all list items are removed and I don't get an error in the console so I'm not sure how to diagnose this.
Could it have something to do with the fact that the list elements aren't prerendered/static html but injected using vue.js?
https://jsfiddle.net/nolaandy/hw2mheem/
HTML/Vue Template
<div id='app'>
<div class="all-players-wrapper" id="all-player-listings">
<button id="filter-qb">QB</button>
<ul class="list">
<li v-for="player in playerJSON">
<div class="player-listing">
<div class="player-left">
<div class="player-name">{{player.firstName}} {{player.lastName}}</div>
<div class="playerPosition">{{ player.Position }}</div>
</div><!-- end player-left -->
<div class="player-right">
<div class="player-grade">GRADE <span>{{player.NFLGrade}}</span></div>
</div> <!--end player-right -->
</div>
</li>
</ul>
</div>
</div>
JS
var vm = new Vue({
el: '#app',
data: {
status: 'Combine Particpants',
playerJSON: []
},
created: function () {
this.loadData();
},
methods: {
loadData: function () {
var self = this;
axios.get('https://s3-us-west-2.amazonaws.com/s.cdpn.io/500458/tiny.json').then(function (response) {
self.playerJSON = response.data
console.log(response.data);
})
.catch(function (error) {
self.status = 'An error occurred - ' + error
});
}
}
});
var options = {
valueNames: [ 'playerPosition' ]
};
var featureList = new List('all-player-listings', options);
$('#filter-qb').click(function() {
featureList.filter(function(item) {
if (item.values().playerPosition == "QB") {
return true;
} else {
return false;
}
});
return false;
});
As you suspected, List.js isn't going to work properly if the DOM changes unpredictably. In this case, axios makes its call and populates the data after the (empty) List has been read into featureList.
Your example would work if you put the list-selecting-and-filtering code in the resolution of the axios call, but that's not going to be a solution that works in a truly dynamic environment.
A custom directive will be called every time the DOM updates, so you can apply your adjustments consistently. Here's a directive to apply a filter using List.js:
directives: {
filteredList(el, binding) {
if (binding.value) {
const options = {
valueNames: ['playerPosition']
};
const featureList = new List(el, options);
featureList.filter((item) => item.values().playerPosition === binding.value);
}
}
}
Apply it like so:
<div class="all-players-wrapper" v-filtered-list="filterValue">
Add the filterValue data item, and have the button set it:
<button id="filter-qb" #click="() => filterValue='QB'">QB</button>
and you're in business.
It's worth noting that you could get the same effect by using a computed to filter the data, and you wouldn't need an external library.
Updated fiddle
new Vue({
el: "#app",
data: {
msg: "hello world"
},
methods: {
trigger: function() {
var fileLoader = document.getElementById('fileLoader');
//clear file value
if(fileLoader.files.length > 0) {
fileLoader.value == null;
alert(fileLoader.files[0].name);
}
fileLoader.click();
},
fetchData: function() {
console.log('hello');
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<button #click="trigger">upload image</button>
<input type="file" id="fileLoader" v-show="false" #change="fetchData">
</div>
as you know in chrome, when you first time choose a file, input will trigger onchange event, but second time you choose the same file , input won't trigger onchange event,so i want to set input's value to null,before choose the same file, which works using jquery or plain javascript. but in vuejs, it dosen't work. what's the problem?
You are doing a comparison instead of an assignment:
fileLoader.value == null;
This should be:
fileLoader.value = null;
Also this means you won't be able to alert the filename after you have done this, because the value has already been cleared by then. So this line should come before you clear it:
alert(fileLoader.files[0].name);
new Vue({
el: "#app",
data: {
msg: "hello world"
},
methods: {
trigger: function() {
var fileLoader = document.getElementById('fileLoader');
//clear file value
if(fileLoader.files.length > 0) {
alert(fileLoader.files[0].name);
fileLoader.value = null;
}
fileLoader.click();
},
fetchData: function() {
console.log('hello');
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<button #click="trigger">upload image</button>
<input type="file" id="fileLoader" v-show="false" #change="fetchData">
</div>
I am developing MVC application and using razor syntax.
In this application I am giving comment facility.
I have added a partial view, which loads the comment/Records from DB.
In below image, we can see the comment box which is called run-time for employee index view.
Now as we can see comment box, I called at run-time, which is partial view, but problem is I can add comment for only on first record...after first record that button wont work at all...
anything is missing ?
Is there separate process when we call any partial view run-time and make in action on it ?
See the pic...
Here is the code....
#model PagedList.IPagedList<CRMEntities.Customer>
<link href="../../Content/Paging.css" rel="stylesheet" type="text/css" />
<link href="../../Content/EventEntity.css" rel="stylesheet" type="text/css" />
<script src="<%=Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")%>" type="text/javascript"></script>
<div id="ListBox">
<div id="ListHeader">
All customers(#Model.TotalItemCount)
</div>
#foreach (var item in Model)
{
<div id="ListContent">
<span class="ContentTitleField">#Html.ActionLink(item.Name, "Details", new { id = item.Id }, new { #style="color:#1A6690;" })</span>
#if (item.Owner != null)
{
<span class="ContentSecondaryField">#Html.ActionLink(item.Owner.FullName, "Details", "Employee", new { id = item.OwnerId }, new { #style = "color:#1A6690;" })</span>
}
<span class="ContentSecondaryField">#Html.DisplayFor(modelItem => item.Address)</span>
<span id="flagMenus">
#Html.Action("ShowFlag", "Flagging", new { entityId=item.Id, entityType="Customer"})
</span>
#if (item.Opportunities.Count > 0)
{
<span class="FlagOpportunity">#Html.ActionLink("opportunities(" + item.Opportunities.Count + ")", "Index", "Opportunity", new { custid = item.Id }, new { #style = "color:#fff;" })</span>
}
<div style="float:right;">
#Html.Action("SetRate", "Rating", new { entityId = item.Id, rating = item.Rating, entityname = "Customer" })
</div>
<div id="subscribeStatus" style="float:right;">
#Html.Action("ShowSubscribedStatus", "Subscribing", new { entityId = item.Id, entityType = "Customer" })
</div>
<div class="ListLinks">
<span class="ListEditLinks">
<span style="float:left;">#Html.ActionLink("edit", "Edit", new { id = item.Id })</span>
<span class="LinkSeparator"></span>
</span>
<span class="ListAddLinks">
<span style="float:left;">#Html.ActionLink("+opportunity", "Create", "Opportunity", new { custid = item.Id }, null)</span>
<span class="LinkSeparator"></span>
<span>#Ajax.ActionLink("+Comment", null, null, null, new { id = item.Id, #class = "addremark" })</span>
</span>
<div class="RemarkBox"></div>
</div>
<span class="CommentAdd">
</span>
<div class="CommentBlock">
</div>
<span>#Ajax.ActionLink("Add Comment", null, null, null, new { id = item.Id, #class = "addremark" })</span>
</div>
}
<div class="PagingBox">
#Html.Action("CreateLinks", "Pager", new { hasPreviousPage = Model.HasPreviousPage, hasNextPage = Model.HasNextPage, pageNumber = Model.PageNumber, pageCount = Model.PageCount })
</div>
</div>
<script type="text/javascript">
$(document).ready(function () {
$('.RemarkBox').hide();
$('a.addremark').click(function () {
var url="#Html.Raw(Url.Action("ShowCommentBox", "Comment", new { Id = "idValue", EntityType = "Customer" }))";
url=url.replace("idValue",event.target.id);
$('.RemarkBox').load(url);
$(this).closest('div').find('div.RemarkBox').slideToggle(300);
return false;
});
$("a.pagenumber").click(function () {
var page = 0;
page = parseInt($(this).attr("id"));
$.ajax({
url: '#Url.Action("GetPagedCustomers")',
data: { "page": page },
success: function (data) { $("#customerlist").html(data); }
});
return false;
});
});
</script>
To expand on what Alberto León is saying, the partial page load will not cause the document ready event to fire, so the re-rendered elements will not have the javascript event handlers registered after the first comment is added.
To resolve this, you could put the event registration code into a function, and call this both from the document ready event and the success handler of the AJAX call. Something like this:
function AssignEventHandlers() {
$('a.addremark').click(function () {
....
});
$("a.pagenumber").click(function () {
var page = 0;
page = parseInt($(this).attr("id"));
$.ajax({
url: '#Url.Action("GetPagedCustomers")',
data: { "page": page },
success: function (data) {
$("#customerlist").html(data);
AssignEventHandlers();
}
});
return false;
});
}
$(document).ready(function () {
$('.RemarkBox').hide();
AssignEventHandlers();
}
In success function you need to recall the javascript, or the jquery code that makes the button work. Is an error that taked me a lot of time. Anything uploaded by ajax or any renderpartiAl needs to recall javascript.
$('.RemarkBox').load(url, function() {
//RECALL JAVASCRIPT
});