Event firing multiple times in Vuejs - javascript

So, I wrote a function that using which the user can upload multiple files.
<input multiple type="file" id="inputFile" ref="fileSelect" #click="uploadfiles"/>
uploadfiles(){
'use strict'
const inputFile = document.getElementById('inputFile')
console.log(inputFile)
inputFile!.addEventListener('change',async e => {
const files = (e.target as HTMLInputElement).files
const reader = new FileReader()
console.log(files)
console.log(reader)
// New set of results each time.
this.results.splice(0)
for (const file of files!){
if(file["type"] == "application/json"){
this.results.push(await this.readjsonfile(reader, file))}
else if(file["type"]=="text/csv"){
this.results.push(await this.readcsvfile(reader, file))
}
}
})}
Now when I upload a file for the 1st time it works fine, but when I click on the choose files button again to upload another file, it uploads that file twice.
I think the problem is that when I click again on choose files I "activate" the uploadfiles function again, which causes the Event Listener to fire twice, but I am not sure how to do it any other way.
I am new to JavaScript and vuejs, so any suggestions are greatly appreciated.
Thanks to #Daniel I finally ended up figuring out how to fix this.
My final code is
<input multiple type="file" id="inputFile" ref="fileSelect" #change="uploadfiles"/>
uploadfiles(event){
'use strict'
console.log(event)
//console.log(event.target.files)
const q = [event.target.files]
q.forEach(async w => {
const reader = new FileReader()
this.results=[]
for(const file of w){
if(file["type"] == "application/json"){
this.results.push(await this.readjsonfile(reader, file))}
else if(file["type"]=="text/csv"){
this.results.push(await this.readcsvfile(reader, file))
}
}
})
console.log(this.results)
}

The reason is that you're adding inputFile!.addEventListener every time the user clicks on the input form
Instead, if you were to use #change, instead of #click, that should not be happening any more.
example:
<script setup>
import {ref} from "vue"
let files = ref([])
function uploadfiles(event){
files.value.splice(0);
[...event.target.files].forEach(async file => {
// do the logic here
files.value.push(file)
})
}
</script>
<template>
<input multiple type="file" id="inputFile" ref="fileSelect" #change="uploadfiles"/>
<pre>{{files}}</pre>
</template>
SFC playground

Related

Custom validator and dinamic data in Angular8

The first thing to say is that I have been searching for a long time, questions similar to the one I am going to ask and although I have found some, none have helped me with my problem. Having said this, I will explain the problem in question.
I am creating a custom validator for the file upload component. This validator must be able to validate the size of the files to be uploaded. At the moment I have this:
file-validator.ts
export function fileSizeValidator(maxFileSize: number, files: any[]) {
console.log(files)
const maxFileSizeToKb = maxFileSize * 1024;
return (): { [key: string]: any } | null => {
let forbidden = true;
files.forEach(file => {
if (file.file.size > maxFileSizeToKb) {
forbidden = false;
}
});
return forbidden ? { inValidSize: true } : null;
};
}
file-uploader.component.ts
...
ngOnInit() {
this.uploadForm = this.formBuilder.group({
filesToUpload: ['', fileSizeValidator(this.data.maxMbSize, this.rawFiles)],
});
}
...
file-uploader.component.html
...
<form [formGroup]="uploadForm" (ngSubmit)="onFormSubmit()">
<ng-container *ngIf="canUploadMultiple; else unique">
<input #file formControlName="filesToUpload" type="file" [accept]="allowedFilesToString" (click)="fileInputClick($event)" id="{{fileId}}" name="file" multiple (change)="onUploadFile($event.target.files)" />
</ng-container>
<ng-template #unique>
<input #file type="file" formControlName="filesToUpload" [accept]="allowedFilesToString" (click)="fileInputClick($event)" id="{{fileId}}" name="file" (change)="onUploadFile($event.target.files)"/>
</ng-template>
...
<button type="submit" [disabled]="!uploadForm.valid">Submit</button>
</form>
...
The problem is that when I add files to upload and the validator function is executed, the array of files always appears empty.
I have tried many things to make it work. I have tried to look for the value that I need inside the object control: AbstractControl that is passed to the function to see if from there I could find the array that is supposed to be passed to it when executing the validator function, but the only thing I find from array is the name of the files.
I have also tried to do a .bind() and an arrow function like this:
this.uploadForm = this.formBuilder.group({
filesToUpload: ['', fileSizeValidator(this.data.maxMbSize, this.rawFiles).bind(this)],
});
this.uploadForm = this.formBuilder.group({
filesToUpload: ['', fileSizeValidator(this.data.maxMbSize, (() => this.rawFiles)],
});
Another thing that I also thought might work is to put the validator back into the form once the dynamic array has been modified:
...
this.rawFiles.push(this.formData.getAll('files'));
this.uploadForm.controls['filesToUpload'].setValidators(Validators.compose([fileSizeValidator(this.data.maxMbSize, this.rawFiles)]));
...
But none of this works for me, I have tried other things that I have found on the internet, but they have not worked either. The only thing I can think of is to move the validator function from file-validator.ts to files-uploader.component.ts in order to have direct access to the dynamic array.
Edit: I tried that last option and dont't work too.

How to show multiple attachments in JavaScript

I'm completly new in JavaScript and HTML, and i want to attach some files in my site. The problem is that: when i want to attach some files, from the view, i selected them but in the view appear only one file instead of the list of file that i selected.
For example, if I attach file1, file2 and file3, it shows me only file1, like that:
So I want to obtain this situation:
Here is the code:
JS wrote by me to fix the problem, but it shows me the same result (only one file appears):
const fileinput = require("./button");
const updateList = () => {
var arrayFiles = document.getElementById("input_issue").files;
for (i = 0; i < arrayFiles.length; i++) {
fileinput.append('<i class="text">arrayFiles[i].name</i>')
}
}
<form id="issueModalFormAttachment" method="POST">
<table>
<tr class="form-group">
<td class="label">{% translate 'Attachments' %}</td>
<td class="field">
<input id="input_issue" name="input_issue[]" type="file" class="file" multiple data-show-upload="true" data-show-caption="true">
</td>
</tr>
</table>
</form>
I don't know if it is useful because i know nothing about html and js and someone else wrote the entire HTML and JS code... but my tutor gave me the issue to fix this problem
ArrayFiles should be the array of files that i want to upload. I thought that with a for loop i could scan the array and append the files in the button.
Can someone help me? Sorry again for the ignorance
Edit: I solved in this way
const setupInput = () => {
$('#input_issue').change( () => {
let arrayFiles = document.getElementById("input_issue").files;
for(i=1;i<arrayFiles.length;i++){
let t = $('#button_file_upload_issue i.text').text();
$('#button_file_upload_issue i.text').text(t + ', '+ arrayFiles[i].name);
}
})
}
try something like this:
fileinput.innerHTML += '<i class="text">arrayFiles[i].name</i>';

How to get file path from File object observer in v-file-input Vuetify?

I am building feature upload image with Vuetify. I need preview image before upload to server
In v-input-file
<v-file-input
accept="image/*"
multiple
label="Add your files"
chips
filled
prepend-icon="mdi-camera"
#change="onAddFiles"
></v-file-input>
In method change
onAddFiles(files) {
console.log(files);
}
You can see there is no information file path in File object observer array
I don't know how to get file path from this File object observer. Please help me and i'm so grateful!
in data, include the following
previews: [],
errorImage: "url of an image to use to indicate an error",
obviously you need to set errorImage to something useful
Then add some code to onAddFiles to generate an array of previews
onAddFiles(files) {
this.previews = [];
files.forEach((file, index) => {
const reader = new FileReader();
reader.addEventListener('load', e => this.previews[index] = e.target.result);
reader.addEventListener('error', e => this.previews[index] = this.errorImage);
reader.readAsDataURL(file);
});
}
In your markup - wherever you want you previews shown, something like
<span v-for="src in previews">
<img v-if="src && src.length" :src="src"/>
</span>

Trigger .py file when JS page is opened on nodjs server browser

I have a nodjs server set up with several JS pages. On one of them, I want a local .py file to be opened and run one time in the background. I DON'T need to generate a response from it.
I have flask set up already, but I don't understand how to incorporate that with my current React Javascript page (the one I want my python file triggered from).
In different instances, I have tried using:
var path = require('C:/Users/myPath/python.exe');
var nwDir = path.dirname(process.execPath);
var spawn = require("child_process").spawn;
var child = spawn(nwDir + '/test.py');]
as well as:
const spawn = require("child_process").spawn;
const pythonProcess = spawn('python',["/test.py"]);
but I receive this error in both instances:
TypeError: spawn is not a function
located at:
const pythonProcess = spawn('python',["/test.py"]);
lastly I have also tried:
var express = require('express');
var app = express();
const {PythonShell} = require('python-shell');
var options = {
mode: 'text',
pythonPath:
'C:/Users/myPath/python.exe',
pythonOptions: [],
// make sure you use an absolute path for scriptPath
scriptPath:
C:/Users/myPath/test.py', args: []
};
PythonShell.run('test.py', options, function (err, results) {
if (err) throw err;
// results is an array consisting of messages collected during
execution
console.log('results: %j', results);
});
but I receive this error:
TypeError: Cannot read property 'prototype' of undefined
located at my response.js file:
var res = Object.create(http.ServerResponse.prototype);
Is there something I should be including in my server.js file?
Is there a way I can incorporate flask with my current JS page?
For reference, here is a basic version of my current JS file:
myFile.js:
import React from "react";
import Header from "../Header";
import Footer from "../Footer";
class SummaryPage extends React.Component {
render() {
return (
<div>
<Header />
<div className="container-fluid ">
<b><h1>Update and Read CSV File </h1></b>
</div>
<form enctype="multipart/form-data" action="/upload/image"
method="post">
<input type="file" id="fileUpload" />
<div id="dvCSV">
</div>
</form>
<Footer/>
</div>
);
}
}
export default SummaryPage;
To reiterate, how can I have a local .py file be opened and run in the background when my JS page is opened? I would also be happy if it had to occur with the click of a button. No response from the .py file is needed.
Thank you for the time.

Uploading images using aurelia to asp.net core backend

I have been searching for a solution for this but none of the guides are updated or suited for my intention. I need a user uploaded image to be loaded into javascript/aurelia which then sends it to the asp.net core backend using its http fetch client so the image is saved on disk(not in a database). I'm currently using the following code but I'm getting the following error and no images are being saved.
extract from html code being used to upload image
<input class="hiddenButton" id="images" type="file" accept=".jpeg" file.bind="image">
<button class="upload" onclick="document.getElementById('images').click()">
<i class="fa fa-pencil" style="color:green"></i>
</button>
extract of javascript code used to invoke saving
save() {
this.api.saveEmployee(this.employee).then(employee => this.employee = employee);
this.ea.publish(new EmployeeAdded(this.employee));
this.api.saveImage(this.image);
return this.employee;
}
Javascript/aurelia code
saveImage(image) {
var form = new FormData()
form.append('image', image)
this.http.fetch('/api/Images', {
method: 'POST',
//headers: { 'Content-Type': image.type },
body: form
})
.then(response => {
return response
})
.catch(error => {
console.log("Some Failure...");
throw error.content;
})
return true;
}
Asp.net core MVC code(backend)
[HttpPost]
public async Task<IActionResult> SaveImage(IFormFile file)
{
Console.WriteLine("Images controller");
var filePath = Path.Combine(Directory.GetCurrentDirectory(),"Image");
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return Ok();
}
error message
The HTML element <input type="file" /> does not have a property file, the correct property is files, so it sounds like the problem is with aurelia/javascript and binding.
Since the property files is a FileList (collection) you will need to access the first file in the collection. Even though you haven't used multiple I think files will still be a collection.
You could try this:
// html
<input class="hiddenButton" id="images" type="file" accept=".jpeg" files.bind="image">
// ^ files
// jss/aurelia
saveImage(image) {
var form = new FormData();
form.append('image', image[0]); // access the first image in collection image[0]
// the other code remains the same
//...
}
PS I haven't used aurelia so can't be 100% sure this is the issue but hopefully points you in the correct direction.
PPS: since files is a collection, technically image in your view model is a collection too, so you could consider renaming it to images to make it clearer (even if you're only using one image). It should still work using image[0] but images[0] would be clearer.

Categories