Here i choose multiple images and shows using *ngFor And there I have placed a delete button which is appear in the screenshot, click on delete button i want to delete chosen image from chosen list i tried below code but i not getting proper solution.
add.component.html
<button mat-raised-button (click)="fileInput.click()">Select File</button>
<input style="display: none" type="file" (change)="onFileChanged($event)" #fileInput multiple="true">
<div *ngFor="let selected of selectedFile;let index = index">
<h3>{{selected.name}}</h3>
<button mat-icon-button (click)="removeSelectedFile(index)">delete</button>
</div>
add.component.ts
selectedFile: File;
ArrayOfSelectedFile = new Array<string>();
onFileChanged(event : any) {
this.ArrayOfSelectedFile= [];
this.selectedFile = event.target.files;
this.ArrayOfSelectedFile.push(event.target.files);
}
removeSelectedFile(index){
this.ArrayOfSelectedFile.splice(index,1);
}
HTML Code:
<button mat-raised-button (click)="fileInput.click()">Select File</button>
<input style="display: none" #attachments type="file" (change)="onFileChanged($event)" #fileInput multiple="true">
<div *ngFor="let selected of listOfFiles;let index = index">
<h3>{{selected}}</h3>
<button mat-icon-button (click)="removeSelectedFile(index)">delete</button>
</div>
And TS code:
Import this:
import { Component, OnInit, Inject, ViewChild } from '#angular/core';
And Inside your component class:
#ViewChild('attachments') attachment: any;
fileList: File[] = [];
listOfFiles: any[] = [];
onFileChanged(event: any) {
for (var i = 0; i <= event.target.files.length - 1; i++) {
var selectedFile = event.target.files[i];
this.fileList.push(selectedFile);
this.listOfFiles.push(selectedFile.name)
}
this.attachment.nativeElement.value = '';
}
removeSelectedFile(index) {
// Delete the item from fileNames list
this.listOfFiles.splice(index, 1);
// delete file from FileList
this.fileList.splice(index, 1);
}
If you notice that the Deleted file is not available for upload again for that I have used #ViewChild to reset the value = '', then you can select the deleted file again.
Here is a working StackBlitz example.
event.target.files is already an array, which is why you can iterate over it with ngFor.
When you assign selectedFile = event.target.files, you are making it an array.
Try this:
selectedFile: File;
ArrayOfSelectedFile = new Array<string>();
onFileChanged(event : any) {
this.selectedFile = event.target.files[0];
this.ArrayOfSelectedFile = event.target.files;
}
removeSelectedFile(index){
this.ArrayOfSelectedFile.splice(index,1);
}
<div *ngFor="let selected of ArrayOfSelectedFile;let index = index">
<h3>{{selected.name}}</h3>
<button mat-icon-button (click)="removeSelectedFile(index)">delete</button>
</div>
You can check this Multiple file upload delete, let me know if you want any clarification on same.
You should remove it from a selectedFile array.
this.selectedFile.splice(index, 1);
In your html code
<label class="form-label">Add Images</label>
<input type="file"
class="form-control"
(change)="onSelectFile($event)"
multiple accept="image/*" />
</div>
//this is your ts code
onSelectFile(event) {
if (event.target.files && event.target.files[0]) {
var filesAmount = event.target.files.length;
for (let i = 0; i < filesAmount; i++) {
var reader = new FileReader();
reader.onload = (event: any) => {
this.imageurls.push({ base64String: event.target.result, });
}
reader.readAsDataURL(event.target.files[i]);
}
}
}
For more details:https://findandsolve.com/articles/how-to-upload-and-remove-multiple-image-using-anular-code-example
There are two better ways to achieve this if you want to avoid explicit for loop.
1- Using spread operator:
ArrayOfSelectedFile: File[] =[];
onFileChanged(event : any) {
this.ArrayOfSelectedFile = [...event.target.files];
}
removeSelectedFile(index){
this.ArrayOfSelectedFile.splice(index,1);
}
event.target.files is FileList object. To remove a file from a JavaScript FileList is to use the spread operator to convert the FileList to an array.
And in removeSelectedFile function, you can use splice function to remove an element of particular index.
2- Using Array.from method:
ArrayOfSelectedFile: File[] =[];
onFileChanged(event : any) {
this.ArrayOfSelectedFile = Array.from(event.target.files);
}
removeSelectedFile(index){
this.ArrayOfSelectedFile.splice(index,1);
}
Related
I have an image component formed from two input types :
1- text input
2- file input
image.html
<div>
<mat-card>
<mat-card-content>
<label for="cam">Take picture</label>
<input id="cam" style="display: none;" type="file" accept="image/*" capture="camera" (change)="onSelectFile($event)" /> <br>OR<br>
<label for="gallery">Open gallery</label>
<input id="gallery" style="display: none;" type="file" accept="image/*" multiple (change)="onSelectFile($event)" />
</mat-card-content>
<img *ngFor="let image of imagesList" [src]="imagesMap.get(image)" mat-card-avatar (click)="deleteImage(image)"> <br/>
<br>
<mat-card-header *ngIf="imagesList.length > 0">
<mat-card-subtitle>Click on image to remove it</mat-card-subtitle>
</mat-card-header>
</mat-card>
<input type="text" placeholder="Ex. pat#example.com">
image.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-image',
templateUrl: './image.component.html',
styleUrls: ['./image.component.css']
})
export class ImageComponent implements OnInit {
files!: File[];
imagesMap: Map<string, string> = new Map<string, string>();
imagesList: string[] = new Array<string>();
url = "";
constructor() { }
ngOnInit(): void {
}
onSelectFile(event: any) {
this.files = event.target.files;
if (this.files && this.files.length > 0) {
for (let file of this.files) {
if (!this.imagesMap.has(file.name)) {
let url: string = "";
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (event) => {
//#ts-ignore
url = event.target.result;
this.imagesMap.set(file.name, url);
this.imagesList.push(file.name);
}
}
}
}
}
deleteImage(imageName: string) {
let indexToRemove = this.imagesList.indexOf(imageName);
if (indexToRemove != -1) {
this.imagesList.splice(indexToRemove, 1);
this.imagesMap.delete(imageName);
indexToRemove = this.files.findIndex(file => file.name == imageName);
if (indexToRemove != -1)
this.files.splice(indexToRemove, 1);
}
}
}
The problem is that when I call this component several times or duplicate it from app components ,
app.component.html
<app-image></app-image>
<app-image></app-image>
the image that had been selected from the second or third … component , it always shows in the first one instead, this problem is with inputs of type File , inputs with type text are working fine . Kindly refer to the image below for a clearer view.
Component screenshot
I want to when I add pictures in the second component (same component but duplicated) , they must shows in the second component . Each duplicated component must be independent from others.
Please can help me to solve this issue.
Thanks
That's because of duplicate Id. You have two input with Id of cam and gallery so when you click on second, one browser finds the first element and opens file chooser. It means you basically clicked on first element. don't use this approach, just simply use a template variable
<label (click)="file.click()">Open gallery</label>
<input #file style="display: none;" type="file"
accept="image/*" multiple (change)="onSelectFile($event)"/>
Currently, I'm developing a drag and drop feature. The problem is that I can't figure out how to get multiple files from user separately. Let's say that we have a drop zone container and when user drops there images, it assigns them to <input type="file">. Let's say, a user drops here an image and then decides to drop another image and we have to somehow add this second image to the input. I tried finding solution in the Internet(of course :)) but found nothing that solves this problem.
Here is my HTML:
<form action="" method="POST" enctype="multipart/form-data">
<div class="drop_zone">
<span class="drop_zone__prompt">Drop your files here</span>
<input required name="images" type="file" multiple class="drop_zone__input">
</div>
<input type="submit" value="Отправить">
</form>
JavaScript:
document.querySelectorAll('.drop_zone__input').forEach(InputElement => {
const dropZoneElement = InputElement.closest('.drop_zone');
dropZoneElement.addEventListener('drop', e => {
e.preventDefault();
if (e.dataTransfer.files.length){
InputElement.files = e.dataTransfer.files;
}
});
I tried this:
dropZoneElement.addEventListener('drop', e => {
e.preventDefault();
if (e.dataTransfer.files.length){
myfiles = e.dataTransfer.add(InputElement.files);
InputElement.files = myfiles;
}
});
But it returns error saying that 'e.dataTransfer.add is not a function'
Why I tried this:
I found add() method here
And this article says:
The DataTransferItemList object is a list of DataTransferItem objects representing items being dragged. During a drag operation, each DragEvent has a dataTransfer property and that property is a DataTransferItemList.
Actually, there is a way to do that. It's far from straightforward, but it does work.
The only way to create a FileList object is to create a custom DataTransfer object. You can add files to it using dataTransfer.items.add(), and obtain the corresponding FileList through dataTransfer.files.
So, create a new DataTransfer object every time you want to add files, add the existing and the new files to it, and assign its FileList to the files property of the input element.
Note: You can't use the drop event's DataTransfer object for this, because it's read-only.
document.querySelectorAll('.drop_zone__input').forEach(InputElement => {
const dropZoneElement = InputElement.closest('.drop_zone');
dropZoneElement.addEventListener('dragover', e => {
e.preventDefault()
});
dropZoneElement.addEventListener('drop', e => {
e.preventDefault();
//Create a new DataTransfer object
const dataTransfer = new DataTransfer
//Add new files from the event's DataTransfer
for(let i = 0; i < e.dataTransfer.files.length; i++)
dataTransfer.items.add(e.dataTransfer.files[i])
//Add existing files from the input element
for(let i = 0; i < InputElement.files.length; i++)
dataTransfer.items.add(InputElement.files[i])
//Assign the files to the input element
InputElement.files = dataTransfer.files
});
})
.drop_zone{
height: 200px;
width: 200px;
border: solid black 1px;
}
<form action="" method="POST" enctype="multipart/form-data">
<div class="drop_zone">
<span class="drop_zone__prompt">Drop your files here</span>
<input required name="images" type="file" multiple class="drop_zone__input">
</div>
<input type="submit" value="Отправить">
</form>
You can also reuse the same DataTransfer object, so you don't have to re-add the existing files.
However, in this case, you also have to handle input events on the input element.
document.querySelectorAll('.drop_zone__input').forEach(InputElement => {
const dropZoneElement = InputElement.closest('.drop_zone');
const dataTransfer = new DataTransfer
dropZoneElement.addEventListener('dragover', e => {
e.preventDefault()
});
dropZoneElement.addEventListener('drop', e => {
e.preventDefault();
//Add new files from the event's DataTransfer
for(let i = 0; i < e.dataTransfer.files.length; i++)
dataTransfer.items.add(e.dataTransfer.files[i])
//Assign the files to the input element
InputElement.files = dataTransfer.files
});
InputElement.addEventListener('input', e => {
dataTransfer.items.clear()
for(let i = 0; i < InputElement.files.length; i++)
dataTransfer.items.add(InputElement.files[i])
})
})
.drop_zone{
height: 200px;
width: 200px;
border: solid black 1px;
}
<form action="" method="POST" enctype="multipart/form-data">
<div class="drop_zone">
<span class="drop_zone__prompt">Drop your files here</span>
<input required name="images" type="file" multiple class="drop_zone__input">
</div>
<input type="submit" value="Отправить">
</form>
Or, if you want to add the files when interacting with the file input instead of replacing them, you can do this:
document.querySelectorAll('.drop_zone__input').forEach(InputElement => {
const dropZoneElement = InputElement.closest('.drop_zone');
const dataTransfer = new DataTransfer
dropZoneElement.addEventListener('dragover', e => {
e.preventDefault()
});
dropZoneElement.addEventListener('drop', e => {
e.preventDefault();
//Add new files from the event's DataTransfer
for(let i = 0; i < e.dataTransfer.files.length; i++)
dataTransfer.items.add(e.dataTransfer.files[i])
//Assign the files to the input element
InputElement.files = dataTransfer.files
});
InputElement.addEventListener('input', e => {
e.preventDefault()
for(let i = 0; i < InputElement.files.length; i++)
dataTransfer.items.add(InputElement.files[i])
InputElement.files = dataTransfer.files
})
})
.drop_zone{
height: 200px;
width: 200px;
border: solid black 1px;
}
<form action="" method="POST" enctype="multipart/form-data">
<div class="drop_zone">
<span class="drop_zone__prompt">Drop your files here</span>
<input required name="images" type="file" multiple class="drop_zone__input">
</div>
<input type="submit" value="Отправить">
</form>
You can remove files from the DataTransfer object using dataTransfer.items.remove():
document.querySelectorAll('.drop_zone__input').forEach(InputElement => {
const dropZoneElement = InputElement.closest('.drop_zone');
const removeFirstElement = dropZoneElement.querySelector('.drop_zone__remove_first')
const dataTransfer = new DataTransfer
dropZoneElement.addEventListener('dragover', e => {
e.preventDefault()
});
dropZoneElement.addEventListener('drop', e => {
e.preventDefault();
//Add new files from the event's DataTransfer
for(let i = 0; i < e.dataTransfer.files.length; i++)
dataTransfer.items.add(e.dataTransfer.files[i])
//Assign the files to the input element
InputElement.files = dataTransfer.files
});
InputElement.addEventListener('input', e => {
e.preventDefault()
for(let i = 0; i < InputElement.files.length; i++)
dataTransfer.items.add(InputElement.files[i])
InputElement.files = dataTransfer.files
})
removeFirstElement.addEventListener('click', () => {
dataTransfer.items.remove(0)
InputElement.files = dataTransfer.files
})
})
.drop_zone{
height: 200px;
width: 200px;
border: solid black 1px;
}
<form action="" method="POST" enctype="multipart/form-data">
<div class="drop_zone">
<span class="drop_zone__prompt">Drop your files here</span>
<input required name="images" type="file" multiple class="drop_zone__input">
<input type="button" class="drop_zone__remove_first" value="Remove first file">
</div>
<input type="submit" value="Отправить">
</form>
You can't add to the files to the input's list of files, although apparently you can replace that list as described by FZs, which largely comes to the same thing.
Another way to deal with this is to make the original input hidden via CSS and add a new input where it used to be, so it can receive new files. You can make the UI clean by listing all of the files you're going to upload separately from the input.
How you deal with on submission depends on how you're handling submission.
If you're doing a standard HTTP form submission, ensure that your server side script handles all of the files regardless of which input they came from (it will receive more than one).
If you're submitting via ajax, you can use a FormData object and append each file to it, then submit that.
Here's a quick and dirty example maintaining the files in a FormData object:
let nextFileNum = 1;
const formData = new FormData();
const fileList = document.getElementById("file-list");
document.getElementById("inputs").addEventListener("input", function() {
let input = event.target.closest("input[type=file]");
if (!input) {
return;
}
for (const file of input.files) {
const fileId = `file${nextFileNum++}`;
formData.append(fileId, file, file.name);
debugShowCurrentFiles("added");
let div = document.createElement("div");
// This is a Q&D sketch, needs a11y
div.innerHTML = "<span tabindex class=delete>[X]</span><span class=filename></span>";
div.querySelector(".filename").textContent = file.name;
div.querySelector(".delete").addEventListener("click", function() {
formData.delete(fileId);
debugShowCurrentFiles("deleted");
div.remove();
div = null;
});
fileList.appendChild(div);
}
this.removeChild(input);
this.insertAdjacentHTML("beforeend", input.outerHTML);
input = null;
});
function debugShowCurrentFiles(action) {
const contents = [...formData.entries()];
console.log(`--- ${action}, updated formData contents (${contents.length}):`);
for (const [key, value] of contents) {
console.log(`${key}: ${value.name}`);
}
}
#file-list .delete {
margin-right: 2em;
cursor: pointer;
}
<div id="inputs">
<input type="file" multiple>
</div>
<div id="file-list"></div>
You can do this with FileList, heres an article about it.
Also added code snippet which outputs file name and type after you submit the form just for visualization.
let form = document.getElementById('file_drop_form');
function getFiles(event) {
event.preventDefault();
let files = document.getElementById('file').files;
for(let i = 0; i < files.length; i++) {
document.write(`Title: ${files[i].name}; Type: ${files[i].type} <br>`);
}
}
form.addEventListener('submit', getFiles);
<form id="file_drop_form" method="post" enctype="multipart/form-data">
<div>
<label for="file">Drop your files here</label>
<input type="file" id="file" name="file" required multiple>
</div>
<input type="submit" value="Submit">
</form>
I have the following problem that I cannot solve
is a form of this type
in HTML
<button (click)="addform()">agregar formulario</button>
<div class="conten-form">
<div class="MyForm">
<label>Nombre</label>
<input type="text" class="name">
<label>descripcion</label>
<input type="text" class="description">
<label>Foto</label>
<div class="img-cont">
<div class="img">
<img src="{{img}}">
</div>
<div class="addimg">
<input type="file" (change)="createimg($event)">
</div>
</div>
</div>
<div class="AddForm"></div>
<input type="buttom" value="enviar" (click)="enviarform()">
</div>
in TS
img : any; //to show
imgfile : any; //to send it through the form
constructor(...){...}
addform(){
let addform = document.querySelectorAll('.AddForm');
let myform = document.querySelectorAll('.MyForm');
let cloneform = myform.cloneNode(true);
addform.appendChild(cloneform);
}
createimg(event){
this.imgfile = event.target.files[0];
const reader = new FileReader();
reader.readAsDataURL(this.imgfile);
reader.onload = () => {
this.img = reader.result;
};
}
enviarform(event){
let nombre = document.querySelectorAll(".name");
let description = document.querySelectorAll(".description");
let formdata = new FormData;
//add all inputs class name
for (let i = 0; i < nombre .length; i++) {
let stringname = (nombre.[i] as HTMLTextAreaElement).value;
formdata.append('name',stringname);
}
//add all inputs class description
for (let i = 0; i < description.length; i++) {
let stringdescription = (description.[i] as HTMLTextAreaElement).value;
formdata.append('description',stringdescription );
}
//send the form
}
As you will see, I can add more forms with the add button, cloning the first one, the part of the text inputs I have solved to add as many as I want, but in the part of the inputs for the image I have no idea how to do it, the operation is that after attaching an image to the input, I get a preview image of what I am going to attach in the div.class = img, this makes it only work in the first form, and it no longer works in the forms that I add dynamically, as it could fix this issue? taking into account that I have to send it through the formdata in a single shipment, I thank you in advance.
you should not use
let addform = document.querySelectorAll('.AddForm');
let myform = document.querySelectorAll('.MyForm');
let cloneform = myform.cloneNode(true);
at all. to add forms. instead you should create a separate component for each form.
check my question earlier on stackoverflow to get what I mean.
more specifically check the answer stackblitz.
This question already has answers here:
Javascript - How to extract filename from a file input control
(15 answers)
Closed 2 years ago.
Trying to display the file names from a file input element. I am able to console log when I am inside the onchange function but not the addeventlistener. In the code below the console.log('Inside change eventlistener'); will not execute. I can I both log being inside the event listener and get the file names in order to display them? Here is the codepen of the code. Thank you.
html:
<label>Attachments</label>
<div>
<input type="file" class="form-control input-lg" name="attachments" id="attachments" multiple onchange="getFileData()">
</div>
js:
function getFileData() {
console.log('Inside getFileData()...')
var elem = document.getElementById('attachments');
console.log(elem);
elem.addEventListener('change', function(e) {
console.log('Inside change eventlistener');
console.log(e.target);
var fileName = e.target.files[0].name;
console.log(fileName);
});
};
UPDATE:
I am able to console the file names but still not able to display the names to the user. How do I show the names of the files within on the html page. elem.value = ... does not work.
Updated JS:
function getFileData() {
console.log('Inside getFileData()...')
var elem = document.getElementById('attachments');
var files = document.getElementById('attachments').files;
var names = '';
for (let i = 0; i < files.length; i++) {
console.log(files[i].name);
names += files[i].name;
}
console.log(names);
console.log(Object.keys(elem));
//elem.setAttribute('value', names);
};
You may try something like this
var elem = document.getElementById('attachments');
elem.addEventListener('change', getFileData);
function getFileData() {
const files = this.files;
const list = document.getElementById("result");
let child;
for ( let i = 0; i < files.length; i++) {
child = document.createElement("li")
child.textContent = files[i].name;
list.append(child);
}
}
<label>Attachments</label>
<div>
<input type="file" class="form-control input-lg" name="attachments" id="attachments" multiple>
</div>
<ul id="result"></ul>
How to remove specific file from files selected with input type with multiple attribute?
<input type="file" (change)="onFileChange($event)" #fileInput multiple>
I want to delete one of the selected file.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file
https://jsfiddle.net/Sagokharche/eL3eg6k4/
Do you need it to be impossible to choose? Then use HTML Input file accept property. accept="image/png" for instance.
Or you want it to filter from the input after the user selected it?
Then you should use a custom directive or check for the file types in the ts code upon upload.
EDIT
in that case, in your code:
onFileChange(event) {
const fileList = event.target.files;
console.log("User selected fileList:", fileList)
Array.from(fileList).filter(
item => {
console.log("file mime type:", item['type'])
})
const filesToUpload = Array.from(fileList).filter(
item => { return item['type'] != "application/zip" })
console.log("reduced list:", filesToUpload)
}
Working stackblitz example here.
You can access the inputs FileList-object in .ts side like this:
onFileChange(event) {
console.log(event.srcElement.files);
}
Edit:
If you are looking for a solution how to make dynamic form (add and delete inputs), then have a look at this answer and demo:
Angular 4 Form FormArray Add a Button to add or delete a form input row
In your hmtl code
<div class="row">
<div class="col-md-2 productAddfromImages" *ngFor='let url of imageurls; let i = index'>
<img class="img-fluid" [src]="url.base64String">
<a (click)="removeImage(i)" class="btn btn-xs btn-danger">Remove</a>
</div>
</div>
Remove function
removeImage(i) {
this.imageurls.splice(i, 1);
}
Add Function
onSelectFile(event) {
if (event.target.files && event.target.files[0]) {
var filesAmount = event.target.files.length;
for (let i = 0; i < filesAmount; i++) {
var reader = new FileReader();
reader.onload = (event: any) => {
this.imageurls.push({ base64String: event.target.result, });
}
reader.readAsDataURL(event.target.files[i]);
}
}
}
}
For more details:https://findandsolve.com/articles/how-to-upload-and-remove-multiple-image-using-anular-code-example