Rendering a dynamic array of input - javascript

I'm trying to render a dynamic FormArray (When "+" is clicked it should add a new), but always when I put some file in the input box the Message ("Nenhum Arquivo Selecionado" which means "File Doesn't Exist") stays on the screen.
However, if I check the info on this.filterForm.get('Documents'), the row is filled correctly.
Does anyone have a sugestion to fix this error?
protocolo.component.ts
items: FormArray;
filterForm = new FormGroup({
IdProtocolo: new FormControl(),
Documentos: this.formBuilder.array([ this.createItem() ]
);
ngOnInit() {
this.items = this.filterForm.get('Documentos') as FormArray;
}
createItem(): FormGroup{
return this.formBuilder.group({
filename: '',
filetype: '',
value: ''
})
}
addItem(){
this.items.push(this.createItem());
}
removeItem(index){
if(this.items.length > 1) {
this.items.removeAt(index);
}
}
onFileChange(event: any, index: any) {
let reader = new FileReader();
if(event.target.files && event.target.files.length > 0) {
let file = event.target.files[0];
reader.readAsDataURL(file);
this.items.at(index).patchValue({
filename: file.name,
filetype: file.type,
value: (reader.result as string).split(',')[1]
})
}
}
protocolo.component.html
<div *ngFor="let item of filterForm.value.Documentos; let i = index;">
<div class="row" style="margin-bottom: 10px;">
<div class="col-md-4">
<input type="file" formControlName="Documentos" (change)="onFileChange($event, i)">
</div>
<div class="col-md-8">
<button class="btn btn-success-tce" (click)="addItem()">+</button>
<button class="btn btn-success-tce" (click)="removeItem(i)"style="margin-left: 5px">-</button>
</div>
</div>

[Updated] Possibly wrong implementation of formArray. I cannot see a formArrayName in your template. I would have implemented this like
In your template
<p> Dynamic File Form </p>
<form [formGroup]="someForm" (submit)="formSubmit()">
<div formArrayName="documents">
<div *ngFor="let item of files?.controls; let i = index;">
<input type="file" placeholder="Upload file" [formControlName]="i" (change)="onFileChange($event, i)"/>
</div>
</div>
<button type="submit"> Submit </button>
</form>
<button type="button" (click)="addFileControl()"> Add File </button>
In your component.
initForm() {
this.someForm = this.fb.group({
documents: this.fb.array([this.fileControl])
})
}
get files() {
return this.someForm.get('documents') as FormArray;
}
get fileControl() {
return this.fb.group({
file_item: [null]
})
}
addFileControl() {
this.files.push(this.fileControl);
}
formSubmit() {
console.log(this.someForm.value);
}
onFileChange(event, i) {
let reader = new FileReader();
if (event.target.files && event.target.files.length) {
const [file] = event.target.files;
reader.readAsDataURL(file);
reader.onload = () => {
this.files.controls[i].get('file_item').setValue(reader.result);
// need to run CD since file load runs outside of zone
this.cd.markForCheck();
};
}
}
Here is the stackblitz example. This will give you the output in base64 format but you can also get it in file format by modifying.
onFileChange(event, i) {
if (event.target.files && event.target.files.length) {
this.files.controls[i].get('file_item').setValue(event.target.files;);
}
}
Note:- It is just a rough code but does the job :).

Related

Send Json file for Async Validation

I am working with Angular reactive forms and async validation and instead of a normal value I want to send Json file for validation.
My form Looks like
createGoogleCloudForm = new FormGroup(
{
name: new FormControl('', [Validators.required, Validators.pattern(RegexUtil.cloudName)]),
organizationId: new FormControl(''),
config: new FormControl(),
//zones: new FormControl({ value: '', disabled: true }, Validators.required),
},
undefined,
//[GoogleCloudCredentialsValidator.checkGoogleCloudCredentials(this.cloudCredentialsCheckerService)]);
My HTML file looks like
<form class="form" [formGroup]="createGoogleCloudForm" (ngSubmit)="createGoogleCloudCredential()">
<div class="image-upload">
<label for="background-upload" class="button button--outline display--inline-flex">Choose Config File</label>
<span class="file-name">
{{ googleCloudFormData?.get('config')?.name | truncate: 40:'...'
}}
</span>
<input
type="file"
id="background-upload"
class="hidden-input"
formControlName="config"
(change)="fileChange($event.target.files, 'config')"
/>
</div>
</div>
<div class="display--flex justify--content--end">
<button class="button button--primary display--inline-flex" type="submit" [disabled]="!createGoogleCloudForm.valid">
<svg-icon key="circle-plus"></svg-icon> Add Cloud Credentials
</button>
</div>
</form>
The file change method is like
fileChange(files: File[], controlName: string) {
console.log(files);
if (files && files.length > 0) {
this.googleCloudFormData.append(controlName, files[0]);
console.log('adding');
this.createGoogleCloudForm.setAsyncValidators([
GoogleCloudCredentialsValidator.checkGoogleCloudCredentials(this.cloudCredentialsCheckerService),
]);
}
}
The Async Validator is
static checkGoogleCloudCredentials(cloudCredentialsCheckerService: CloudCredentialsCheckerService): AsyncValidatorFn {
return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> =>
// half a second delay to prevent BE requesting after each user's key stroke
timer(500).pipe(
switchMap(() => cloudCredentialsCheckerService.checkGoogleCloud(control.get('config').value)),
map(() => null),
catchError(() => of({ invalidGoogleCloudCredentials: true })),
);
}
and the service
checkGoogleCloud(file: File[]) {
return this.http.post(`${this.baseUrl}/google`, { file });
}
The problem is when I upload the file the async validator does not get fired, I want to send the file with the request.
Any ideas?

FormData Object not receiving file

I am trying to create an avatar editor following the Build a Forum video series.
I am on Laravel 5.8.34.
The console.log in the method #handleFileUpload(e)# shows the file uploaded.
The uploaded image appears on the page.
The console.log in the method #persist(file)# shows an empty object.
DATA FormData {}
The upload does not persist.
My Controller Method:
public function avatar_upload($id)
{
$validate = request()->validate([
'avatar' => ['required', 'image']
]);
$emp = Employee::with('user')->where('user_id', $id)->first();
$avatar = $emp->user->firstName . $emp->user->lastName . '.png';
Storage::disk('spaces')
->putFileAs('avatars', request()->file('avatar'), $avatar, 'public');
$emp->avatar = $avatar;
$emp->save();
return response([], 204);
} // end function
My Component:
<template>
<div>
<div class="text-center mb-4">
<div class="flex justify-center font-thin text-grey-dark text-2xl">
{{user.office}}
</div>
<div class="text-center">
<img class="relative rounded-lg"
:src="avatar">
</div>
<form #submit.prevent="handleFileUpload"
enctype="multipart/form-data"
v-if="canEdit">
<input
type="file"
name="avatar"
ref="file"
accept="image/png"
class="tw-input"
#change="handleFileUpload">
</form>
</div>
</div>
</template>
<script type="text/babel">
export default {
name: 'AvatarReplace',
data() {
return {
canEdit: true,
avatar: this.user.avatar
};
},
props: ['user'],
methods: {
handleFileUpload(e) {
if(! e.target.files.length) { return; } // end if
let file = e.target.files[0];
console.log('FILE', file);
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = e => {
this.avatar = e.target.result;
};
this.persist(file);
},
persist(file) {
let data = new FormData();
data.append('avatar', file);
console.log('DATA', data);
let path = `/api/staff/avatar_upload/${this.user.id}`;
axios.post(path, data)
.then((rsp) => {
//console.log(rsp);
//this.$toastr.s('File Uploaded');
});
}
}
};
</script>
This is not a normal form, Make axios knows that content-type is multipart/form-data
axios.post(path, data, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then((response) => {
//
});

How to use case statement with forEach in angular 6?

I want to select product in app.html from typesFurniture, in the uploadFiles() function to execute the product that I've selected, but i can't make it work. Help me please!
I've tryed with forEach and case statement but it didn't work. Don't know why...
i want to add images when i select one of my products through the dropdown list, my products are in array typesFurniture. But my problem here is i don't know to target selected product from dropdown list and then paste this code
const fileToUpload = this.files;
const filesIdx = _.range(fileToUpload.length);
_.each(filesIdx, (idx) => {
this.upload = new Upload(fileToUpload[idx]); this.uploadService.uploadFile(this.upload);
app.ts
export class DodavanjeSlikaComponent {
typesFurniture = ["Kuhinje", "Klizni ormari", "Ormari", "Radni stolovi", "Stolovi", "Cipelari", "TV Komode", "Police", "Paravani"];
files: FileList;
upload: Upload;
constructor(private uploadService: UploadService) { }
handleFiles(event) {
this.files = event.target.files;
}
uploadFiles() {
/* this have to include!
const fileToUpload = this.files;
const filesIdx = _.range(fileToUpload.length);
_.each(filesIdx, (idx) => {
this.upload = new Upload(fileToUpload[idx]);
this.uploadService.uploadFile(this.upload);
*/
// why this does not work, it won't execute the check that I've selected
this.typesFurniture.forEach(i => {
const fileToUpload = this.files;
const filesIdx = _.range(fileToUpload.length);
switch (i) {
case "Kuhinje":
_.each(filesIdx, (idx) => {
this.upload = new Upload(fileToUpload[idx]);
this.uploadService.uploadFile(this.upload);
});
break;
case "Klizni ormari":
_.each(filesIdx, (idx) => {
this.upload = new Upload(fileToUpload[idx]);
this.uploadService.uploadFile2(this.upload);
});
break;
}
})
}
}
app.html
<div id="dodavanje-proizvoda">
<div class="dodavanje-kocka">
<div class="vrsta">
<p>Izaberite vrstu proizvoda:</p>
<select>
<option *ngFor="let furniture of typesFurniture">{{furniture}}</option>
</select>
</div>
<div class="vrsta2">
<input type="file" (change)="handleFiles($event)" multiple>
</div>
<div class="vrsta3">
<input type="submit" value="Dodajte sliku" (click)="uploadFiles()">
</div>
</div>
</div>

I won't submit form on upload Javascript

Which part of function submit my form on upload image? I won't submit form on upload image. I want on submit button. Which part of code make mi problem? One think this code work okay , but I want submit form on upload photo automation. Also which part of my code maybe not need me for this time?
uploadFile(event) {
const formData = new FormData()
formData.append('image', event.target.files[0])
axios({
method: "post",
url: "linkmyapi",
data: formData,
headers: {
"Content-Type": "multipart/form-data"
}
})
.then(response => {
this.items.push(response.data);
this.image = "";
this.profile_image = ''
this.loading = false
this.dragAndDropUpload = false
this.styleObject.backgroundColor = ''
})
.catch(error => {
this.loading = false;
},
onDropFile(e) {
this.dragAndDropUpload = true
e.stopPropagation()
e.preventDefault()
let files = e.dataTransfer.files
this.createFile(files[0])
},
onChangeFile(e) {
// this.manualUpload = true
let files = e.target.files;
this.createFile(files[0])
},
createFile(file) {
if (!file.type.match('image.*')) {
alert('Select an image')
return
}
let reader = new FileReader()
let vm = this
reader.onload = function (e) {
vm.profile_image = e.target.result
}
reader.readAsDataURL(file)
this.uploadFile(event)
},
removeFile() {
this.profile_image = ''
this.styleObject.backgroundColor = ''
},
onDragOver () {
this.styleObject.backgroundColor = 'rgba(0, 160, 223, 0.4)'
},
onDragLeave () {
this.styleObject.backgroundColor = ''
},
HTML is
<div class="upload-container">
<div
:style="styleObject"
class="drop drop-profile"
id="2"
#dragover.prevent="onDragOver()"
#dragleave.prevent="onDragLeave()"
#drop="onDropFile($event)"
:class="{ 'loading-image': loading }">
<label v-if="!profile_image" class="label-text label-text-profile">
Choose or drag
<br> and drop your
profile image
here
<br>
<input
type="file"
name="profile_image"
#change="onChangeFile($event)">
</label>
<div v-else class="hidden">
<img :src="profile_image" alt="Profile image" class="image-profile" />
<div v-if="!loading" class="lc-loupe-trash-container">
<div #click="removeFile" class="lc-trash"></div>
</div>
</div>
</div>
<div v-if="loading" class="spinner-container">
<i class="fa fa-spinner fa-spin"></i>
</div>
</div>
Your question isn't so clear, can you try editing it to be a little clearer? Do you want to automatically upload onDrop into drop area or you want to upload onClick of submit button?

File is not setting Angular 4

I'm trying to get a file attached via a button. The problem is that the file arrives but not this setting, as it shows in the images below, just below the images has the method and html of the button.
Link image
SetValue Error
Text image
ERROR TypeError: Cannot read property 'setValue' of null
Link image
Archive arrives null
Text image
"documentos": [
{
"id": null,
"tipoDocumento": "",
"numero": "",
"arquivo": null,
"pessoa": {
"id": null
}
}
]
docFile(event: any) {
let reader = new FileReader();
let size = event.target.files[0].size;
if (event.target.files && event.target.files.length > 0) {
let file = event.target.files[0];
if (size > 2097152) {
this.template.openModal()
} else {
reader.readAsDataURL(file);
reader.onload = (event: any) => {
this.imagemDoc = event.target.result;
console.log(this.imagemDoc);
this.dadosPessoaisForm.get('documentos.arquivo').setValue({
id: this.arquivo.id,
nome: file.name,
tipo: file.type,
// dados: reader.result.split(',')[1]
})
};
}
}
}
<div class="input-group-addon" style="background-color: #ffffff">
<div class="upload-btn-wrapper">
<button type="file" class="fa fa-paperclip"></button>
<input type="file" class="btn btn-default" id="arquivo" accept='file/*' (change)="docFile($event)" #fileInput>
</div>
</div>

Categories

Resources