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)"/>
Related
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.
I am working on making custom multi select using text box.if user enter 1,2,3,4 then 1 2 3 4 should be considered as separate value.i am bale to make this using <input type="text"> but instead of text box i want to div so user can click anywhere in the div and able to add item.
following image is my current implementation.
custom-autocomplete.component.html
<div name="test">
<ul class="autocomplete-text base-auto-select-cointainer">
<li class="d-inline-flex selected-item" *ngFor="let item of itemList;let i=index" title="{{item}}">{{item}}
<span class="remove-item pointer" role="presentation" (click)="handelOnItemRemove(i)">×</span></li>
<li class="d-inline-flex">
<input class="item-input" type="text" (keyup)="handelOnItemKeyUp($event)"
tabindex="0" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"
role="textbox">
</li>
</ul>
</div>
custom-autocomplete.component.ts
import { Component, OnInit, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-custom-autocomplete',
templateUrl: './custom-autocomplete.component.html',
styleUrls: ['./custom-autocomplete.component.css']
})
export class CustomAutocompleteComponent implements OnInit {
itemList: any[] = []; // for collection selcted items
userInputs: any;
model = 'some text';
constructor() { }
// event emiters for passing data to parent componet.
#Output() handelItemSelection: EventEmitter<any> = new EventEmitter<any>();
ngOnInit() {
}
handelOnItemKeyUp(event) {
if (event.target.value) {
let userInput: string = event.target.value;
let splitChar = userInput.charAt(userInput.length - 1);
let tempArray: any[] = userInput.split(',');
if (tempArray.length > 1) {
if (tempArray[0] != "" && tempArray[0] != undefined) {
this.itemList.push(tempArray[0])
this.handelItemSelection.emit(this.itemList); //passing updated items to parent
}
}
console.log('')
splitChar == ',' ?event.target.value = null : '' // setting empty value to text box when user enter ,
}
}
// code block for handel item removal from list.
handelOnItemRemove(index: number) {
this.itemList.splice(index, 1);
this.handelItemSelection.emit(this.itemList); //passing updated items to parent
}
}
can someone suggest me to how to implement this using <div contenteditable="true"></div>
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);
}
I have a parent component in which I have 2 input type="file" elements which call the function getFileForParent() on file change :
<input type="file" (change)="getFileForParent()" />
And in my child component I have one :
<input type="file" (change)="getFileForChild()" />
but whenever I select a file on the child component the parents getFileForParent is called.
I am using ng2-file-upload.
Parent ts:
getFileForParent(){
if(this.uploaderForParent.queue[0].file.type != 'application/PDF'){
this.showError("Please select pdf files only");
return;
}
this.uploaderForParent.uploadAll();
}
Child ts:
getFileForChild(){
if(this.uploaderForChild.queue[0].file.type != 'application/PDF'){
this.showError("Please select pdf files only");
return;
}
this.uploaderForChild.uploadAll();
}
Its working fine for me
DEMO
parent.component.html
<h1>
Parent Component File inputs:
</h1>
<input type="file" ng2FileSelect [uploader]="uploaderForParent" (change)="getFileForParent()" />
<input type="file" ng2FileSelect [uploader]="uploaderForParent" (change)="getFileForParent()" />
<h1>
Child Component File inputs:
</h1>
<app-child-comopnent></app-child-comopnent>
parent.component.ts:
uploaderForParent: FileUploader = new FileUploader({ url: 'any' });
getFileForParent() {
console.log("Parent");
console.log(this.uploaderForParent);
if (this.uploaderForParent.queue[0].file.type != 'application/PDF') {
alert("Please select pdf files only");
return;
}
//this.uploaderForParent.uploadAll();
}
child.component.html:
<input type="file" ng2FileSelect [uploader]="uploaderForChild" (change)="getFileForChild()" />
child.component.ts:
uploaderForChild: FileUploader = new FileUploader({ url: 'any' });
getFileForChild() {
console.log("child");
console.log(this.uploaderForChild);
if (this.uploaderForChild.queue[0].file.type != 'application/PDF') {
alert("Please select pdf files only");
}
//this.uploaderForChild.uploadAll();
}
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