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>
Related
So I have this issue that I can't seem to find a solution for. I have an 'upload image' area that's part of my form. You're allowed to either upload an image that has been downloaded on your computer or copy and paste an image address in the url, which should render the image in the image window. Everything works fine, except if:
I upload a downloaded image from my computer downloaded-image
I decide to instead copy an image url from the internet and use that instead image-from-internet
The image from the internet appears and overwrites the old image
I click on the image window again
The old image reappears and overwrites my new image old-image
I've tried resetting the form control of the image window when the user clicks on it, but that ends up clearing the entire image window, discarding the image. How do I prevent the old image from overwriting my new image?
The code snippets:
isLoadingImage = false;
imageUrl ? : string;
imageContainerSelected = true;
ngOnInit(): void {
this.initFormFields();
}
beforeUpload = (file: NzUploadFile, _fileList: NzUploadFile[]) =>
new Observable((observer: Observer < boolean > ) => {
let isLt2M;
if (file.size) {
isLt2M = file.size / 1024 / 1024 < 0.2;
}
if (!isLt2M) {
this.message.error('Image must be smaller than 200KB!');
observer.complete();
return;
}
observer.next(isLt2M);
observer.complete();
});
uploadCompanyImage = (item: NzUploadXHRArgs) => {
const fileToUpload = item.file as any;
const reader = new FileReader();
reader.readAsDataURL(fileToUpload);
reader.onload = () => {
this.formValues.patchValue({
company: {
imageUrl: reader.result ? .toString()
}
});
this.imageUrl = reader.result ? .toString();
};
};
checkForExistingImage(): boolean {
while (this.formValues.get('company.imageUrl') ? .value === '' || this.formValues.get('company.imageUrl') ? .value === null) {
return false;
}
return true;
}
selectImageInputBar(): void {
this.imageContainerSelected = false;
}
selectImageContainer(): void {
this.imageContainerSelected = true;
if (this.formValues.get('company.imageUrl') ? .value === '' || this.formValues.get('company.imageUrl') ? .value === null) {
this.formValues.get('company.imageUrl') ? .reset();
}
}
getImageUrlFromInputBar(): string {
const selectedImageUrl = this.formValues.get('company.imageUrl') ? .value.trim();
return selectedImageUrl;
}
getImageUrlFromImageContainer(): string {
const selectedImageUrl = this.imageUrl || '';
return selectedImageUrl.trim();
}
initFormFields() {
this.formValues = this.fb.group({
id: 0,
userId: '',
jobTitle: ['', [Validators.required]],
description: '',
company: this.fb.group({
name: ['', [Validators.required]],
url: '',
industry: '',
imageUrl: ''
}),
companyLocation: this.fb.group({
city: '',
state: '',
postCode: '',
country: ''
}),
dateStart: ['', [Validators.required]],
dateEnd: '',
associatedGoals: [],
});
}
<nz-form-item class="container">
<nz-form-control class="upload-col">
<div>
<nz-upload class="avatar-uploader company-image-container" nzName="avatar" nzListType="picture-card" [nzShowUploadList]="false" [nzBeforeUpload]="beforeUpload" [nzCustomRequest]="uploadCompanyImage" tabindex="-1" (click)="selectImageContainer()">
<ng-container class="upload-container" *ngIf="!checkForExistingImage()">
<i class="upload-icon" nz-icon [nzType]="'plus'"></i>
<div class="ant-upload-text">Upload</div>
</ng-container>
<div *ngIf="imageContainerSelected==true; then imageContainerIsSelected else imageContainerIsNotSelected">
</div>
<ng-template #imageContainerIsSelected>
<img class="company-image" *ngIf="getImageUrlFromImageContainer()" [src]="getImageUrlFromImageContainer()" tabindex="-1" />
</ng-template>
<ng-template #imageContainerIsNotSelected>
<img class="company-image" *ngIf="getImageUrlFromInputBar()" [src]="getImageUrlFromInputBar()" tabindex="-1" />
</ng-template>
</nz-upload>
</div>
<input class="image-url-input" [nzDisabled]="true" nz-input formControlName="imageUrl" placeholder="Image URL" tabindex="-1" (click)="$event.target.select()" (ngModelChange)="selectImageInputBar()" />
</nz-form-control>
</nz-form-item>
So I am facing with an error which it doesn't send the cropped image to the backend with multer.
I can upload the file in frontend and then I crop the image but the cropped image is not sent in the backend, but it is the uploaded img sent to the backend.
I am using the cropper.js at Angular 11 version with a module that someone did develop for Angular.
this is the cropper.js https://fengyuanchen.github.io/cropperjs/
And this is the module https://github.com/matheusdavidson/angular-cropperjs
So my idea it is like this.
Upload photo -> crop it and save it in a folder at backend.
The save works but it saves the default img not the cropped img.
When user uses click event to saveImage , take the cropped image and save it in the backend.
This is my UI and methods.
<angular-cropper #angularCropper
[cropperOptions]="imgConfig"
[imageUrl]="imgUrl | safeurl"></angular-cropper>
<div class="btn-group">
<label class="btn btn-primary btn-upload" for="inputImage" title="Upload image file" >
<input type="file" class="sr-only" id="inputImage" name="file" accept="image/*" (change)="fileChangeEvent($event)">
<span class="docs-tooltip" data-toggle="tooltip" title="" data-original-title="Import image with Blob URLs">
<span class="fa fa-upload"></span>
</span>
</label>
</div>
<button type="button" class="btn btn-primary" data-method="crop" title="Crop" (click)="saveImage()">
<span class="docs-tooltip" data-toggle="tooltip" title="" data-original-title="cropper.crop()">
<span class="fa fa-check"></span>
</span>
</button>
TS file
imgUrl;
imageURL;
imageCrop;
#ViewChild("angularCropper", {static: false}) public angularCropper: CropperComponent;
public imgConfig = {
aspectRatio : 3/4,
dragMode : "move",
background : true,
movable: true,
rotatable : true,
scalable: true,
zoomable: true,
viewMode: 1,
checkImageOrigin : true,
checkCrossOrigin: true,
width: 0,
height: 0,
};
fileChangeEvent(event: any): void {
this.imgUrl = URL.createObjectURL(event.target.files[0]);
this.imageCrop = event.target.files[0];
}
saveImage() {
this.angularCropper.cropper.crop();
this.imageService.addImage(this.imageCrop).subscribe((res: any) => {
if (res.body) {
this.imageService.getImageByID(res.body._id).subscribe((t: Image) => {
this.imageURL = t.imageUrl;
console.log(this.imageURL);
});
}
}, (err: any) => {
console.log(err);
});
}
And this is my service
#Injectable({
providedIn: "root"
})
export class ImageService {
apiUrl = environment.backend;
constructor(private http: HttpClient) { }
addImage(file: File): Observable<any> {
const formData = new FormData();
formData.append("file", file);
const header = new HttpHeaders();
const params = new HttpParams();
const options = {
params,
reportProgress: true,
headers: header
};
const req = new HttpRequest("POST", `${this.apiUrl}/images/${file.name}`, formData, options);
return this.http.request(req);
}
getImageByID(id: string): Observable<any> {
const url = `${this.apiUrl}/${id}`;
return this.http.get<Image>(url).pipe(
catchError(this.handleError)
);
}
private handleError(error: HttpErrorResponse): any {
if (error.error instanceof ErrorEvent) {
console.error('An error occurred:', error.error.message);
} else {
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
return throwError(
'Something bad happened; please try again later.');
}
}
you will the cropped image from the output of cropper component.. export is the output name
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) => {
//
});
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 :).
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?