Drag and drop of images on the leaflet map + Creation of pins with images

tutorial
Mathis FRAMIT 3 weeks ago
parent 44d8f017ad
commit 72e1e9fff1

@ -133,6 +133,16 @@
></textarea>
</div>
<!-- <div *ngIf="files.length > 0">
<div *ngFor="let file of files">
<img
[src]="getImagePreview(file)"
alt="Image preview"
width="100"
/>
</div>
</div> -->
<div class="flex justify-between">
<button
type="reset"

@ -6,7 +6,7 @@ import {
FormGroup,
ReactiveFormsModule,
} from '@angular/forms';
import { of } from 'rxjs';
import { forkJoin, of, Subscription } from 'rxjs';
import {
catchError,
debounceTime,
@ -18,6 +18,9 @@ import { ExifService } from '../../services/exif/exif.service';
import { MapReloadService } from '../../services/map-reload/map-reload.service';
import { PinService } from '../../services/pin/pin.service';
import { DragDropComponent } from '../drag-drop/drag-drop.component';
import { ModalService } from '../../services/modal/modal.service';
import { ImageService } from '../../services/image/image.service';
@Component({
selector: 'app-add-pin-popup',
standalone: true,
@ -31,13 +34,17 @@ export class AddPinPopupComponent implements OnInit {
@Input() isHomePage: boolean = false;
files: any[] = [];
isPinModalOpen: boolean = false;
modalId: string = 'add-pin-modal';
private modalSub!: Subscription;
constructor(
private fb: FormBuilder,
private autocompleteService: AutocompleteService,
private pinService: PinService,
private exifService: ExifService,
private mapReloadService: MapReloadService
private modalService: ModalService,
private mapReloadService: MapReloadService,
private imageService: ImageService
) {
this.form = this.fb.group({
title: new FormControl(''),
@ -58,6 +65,20 @@ export class AddPinPopupComponent implements OnInit {
}
ngOnInit(): void {
this.modalSub = this.modalService
.getModalState(this.modalId)
.subscribe((open) => {
this.isPinModalOpen = open;
if (open) {
const images = this.modalService.getImageFiles().getValue();
if (images && images.length > 0) {
this.files = images;
this.form.patchValue({ files: images });
}
}
});
this.form
.get('location')
?.valueChanges.pipe(
@ -110,32 +131,61 @@ export class AddPinPopupComponent implements OnInit {
}
}
ngOnDestroy() {
this.modalSub.unsubscribe();
}
submitForm(): void {
// if (this.form.valid) {
// this.files = this.files.map((file) => {
// // return file.name; //TODO: Mettre le hash du fichier
// this.imageService.postImage(file).subscribe((response) => {
// // console.log('Image uploaded:', response);
// let imageId = response.json();
// return imageId.id;
// });
// });
// console.log('Files : ' + JSON.stringify(this.files));
if (this.form.valid) {
this.files = this.files.map((file) => {
return file.name; //TODO: Mettre le hash du fichier
});
const uploadObservables = this.files.map((file) =>
this.imageService.postImage(file)
);
forkJoin(uploadObservables).subscribe((responses) => {
// responses est un tableau de réponses API
this.files = responses.map((res: any) => res.id); // ou res.json().id si nécessaire
const pinData = {
...this.form.value,
files: this.files,
};
this.pinService.addPin(pinData).subscribe(() => {
console.log('Files : ' + JSON.stringify(this.files));
console.log('Pin Data : ' + JSON.stringify(pinData));
this.pinService.addPin(pinData)?.subscribe(() => {
this.mapReloadService.requestReload(); // Demander le rechargement de la carte
this.closePinModal();
this.form.reset(); // Réinitialiser le formulaire après soumission
});
});
} else {
console.error('Le formulaire est invalide');
}
}
openPinModal() {
this.isPinModalOpen = true;
// this.isPinModalOpen = true;
this.modalService.openModal(this.modalId);
}
closePinModal() {
this.isPinModalOpen = false;
// this.isPinModalOpen = false;
this.modalService.closeModal(this.modalId);
}
getImagePreview(file: File): string {
return URL.createObjectURL(file);
}
}

@ -1,5 +1,10 @@
<div class="map-container h-[calc(100vh_-_72px)]">
<div id="map" class="h-full w-full z-0"></div>
<div
id="map"
class="h-full w-full z-0"
(drop)="onDrop($event)"
(dragover)="onDragOver($event)"
></div>
</div>
<div
@ -40,3 +45,4 @@
</label>
</div>
</div>

@ -1,5 +1,11 @@
import { NgFor } from '@angular/common';
import { Component, OnInit, ViewContainerRef } from '@angular/core';
import {
Component,
EventEmitter,
OnInit,
Output,
ViewContainerRef,
} from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import * as L from 'leaflet';
@ -9,6 +15,7 @@ import { AutocompleteService } from '../../services/auto-complete/auto-complete.
import { MapReloadService } from '../../services/map-reload/map-reload.service';
import { PinService } from '../../services/pin/pin.service';
import { PinMarkerComponent } from '../pin-marker/pin-marker.component';
import { ModalService } from '../../services/modal/modal.service';
@Component({
selector: 'app-leaflet-map',
@ -27,12 +34,16 @@ export class LeafletMapComponent implements OnInit {
selectedCountry: string = '';
selectedPerson: string = '__all__';
fileNames: string[] = [];
@Output() filesSelected = new EventEmitter<FileList>();
constructor(
private viewContainerRef: ViewContainerRef,
private pinsService: PinService,
private autocompleteService: AutocompleteService,
private route: ActivatedRoute,
private router: Router,
private modalService: ModalService,
private mapReloadService: MapReloadService
) {}
@ -230,4 +241,30 @@ export class LeafletMapComponent implements OnInit {
this.loadCountriesForFiltrers(pins); // Ensuite, charger les pays en arrière-plan
});
}
async onFilesSelected(event: Event): Promise<void> {
const input = event.target as HTMLInputElement;
if (input.files && input.files.length > 0) {
this.fileNames = Array.from(input.files).map((f) => f.name);
this.filesSelected.emit(input.files);
this.modalService.openModal('add-pin-modal');
}
}
onDragOver(event: DragEvent) {
event.preventDefault();
}
onDrop(event: DragEvent) {
event.preventDefault();
if (event.dataTransfer && event.dataTransfer.files.length > 0) {
this.filesSelected.emit(event.dataTransfer.files);
const images: File[] = Array.from(event.dataTransfer.files);
this.modalService.openModal('add-pin-modal', images);
}
console.log('Image dropped on map : ', event.dataTransfer?.files);
}
}

@ -26,4 +26,16 @@ export class ImageService {
responseType: 'blob'
});
}
postImage(image: File): Observable<any> {
const token = this.localStorageService.getToken();
const headers = new HttpHeaders({
'Authorization': `Bearer ${token}`
});
const formData = new FormData();
formData.append('image', image);
return this.http.post(`${this.apiUrl}/image/pin/null/add`, formData, { headers });
}
}

@ -4,6 +4,7 @@ import { BehaviorSubject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class ModalService {
private modals: Map<string, BehaviorSubject<boolean>> = new Map();
private imageFilesSubject = new BehaviorSubject<File[] | null>(null);
getModalState(id: string): BehaviorSubject<boolean> {
if (!this.modals.has(id)) {
@ -12,11 +13,20 @@ export class ModalService {
return this.modals.get(id)!;
}
openModal(id: string) {
openModal(id: string, images?: File[]) {
if (images) {
this.imageFilesSubject.next(images);
}
this.getModalState(id).next(true);
}
closeModal(id: string) {
this.getModalState(id).next(false);
this.imageFilesSubject.next(null);
}
getImageFiles(): BehaviorSubject<File[] | null> {
return this.imageFilesSubject;
}
}

@ -4,6 +4,7 @@ import { switchMap } from 'rxjs';
import { environment } from '../../../environments/environment';
import { Pin } from '../../model/Pin';
import { AutocompleteService } from '../auto-complete/auto-complete.service';
// import { ImageService } from '../image/image.service';
@Injectable({
providedIn: 'root',
@ -16,7 +17,7 @@ export class PinService {
constructor(
private http: HttpClient,
private autoCompleteService: AutocompleteService
private autoCompleteService: AutocompleteService // private imageService: ImageService
) {}
getPins(): any {
@ -32,7 +33,7 @@ export class PinService {
title: string;
description: string;
location: string;
files: any[];
files: string[];
}) {
const url = `${this.apiURL}/pin/add`;
const headers = new HttpHeaders({

Loading…
Cancel
Save