简体   繁体   中英

Leaflet open popup on mouse hover over list

One view of my application is, a list of all the pins on the one side, and the map with the actual pins on the other side.

I would like the popups to open as I hover through the list with my mouse. Lets say my mouse is on #1 on the list I would like the popup of the corresponding pin to open.

this the html from the list component:

    <table class="table-striped">
      <thead>
        <!-- <th>id</th> -->
        <th>Kunstwerkname</th>
        <th>Strasse</th>
        <th>PLZ</th>
        <th>
          <button class="button-list" (click)="addArtwork()">
            <img src="assets/icons/add.svg">
          </button>
        </th>
      </thead>
      <tbody>
        <tr *ngFor="let artwork of artworkList;" class="artworkList" (mouseover)="previewPopup(artwork)">
          <td>{{artwork.name}}</td>
          <td>{{artwork.streetname}}</td>
          <td>{{artwork.zipcode}}</td>
          <td>
            <button class="button-list" (click)="editArtwork(artwork)">
              <img src="assets/icons/edit.svg">
            </button>
            <button class="button-list" (click)="deleteArtwork(artwork)">
              <img src="assets/icons/delete.svg">
            </button>
          </td>
        </tr>
      </tbody>
    </table>

the ts of the list component:

import { Artwork, ArtworkService } from './../_services/artwork.service';
import { Component, OnInit, Output, EventEmitter } from '@angular/core';


@Component({
  selector: 'app-artwork-list',
  templateUrl: './artwork-list.component.html',
  styleUrls: ['./artwork-list.component.css']
})
export class ArtworkListComponent implements OnInit {
  @Output() private add = new EventEmitter();
  @Output() private edit = new EventEmitter<number>();
  @Output() private preview = new EventEmitter<number>();
  artworkList: Artwork[];

  constructor(
    private artworkService: ArtworkService,
) { }

  ngOnInit() {
    this.refresh();
  }
  refresh() {
    this.artworkService.retrieveAll().then(
      artworkList => this.artworkList = artworkList
    );
  }

  addArtwork() {
    console.log('add artwork');
    this.add.emit();
  }

  editArtwork(artwork: Artwork) {
    console.log('edit artwork ' + artwork.name + ' ' + artwork.id );
    this.edit.emit(artwork.id);
  }

  deleteArtwork(artwork: Artwork) {
    console.log('delete artwork ' + artwork.name + ' ' + artwork.id );
    this.artworkService.delete(artwork.id).then(
      () => this.refresh()
    );
  }

  previewPopup(artwork: Artwork) {
    console.log("hovering mouse");
    console.log(artwork.id);
    this.preview.emit(artwork.id);
  }

}

and ts of the map component:

import { Component, OnInit, Output, EventEmitter, ElementRef, Input } from '@angular/core';

/*When you include the leaflet script inside the Angular project, it gets
loaded and exported into a L variable.*/
declare let L; //this is the leaflet variable!

import { MapButtonsComponent } from './map-buttons/map-buttons';
import { Artwork, ArtworkService } from '../_services/artwork.service';
import { FilterMapComponent } from './filter-map/filter-map.component';



@Component({
  selector: 'app-open-street-map',
  templateUrl: './open-street-map.component.html',
  styleUrls: ['./open-street-map.component.css']
})
export class OpenStreetMapComponent implements OnInit {
  @Output() private add = new EventEmitter();
  @Output() private edit = new EventEmitter<number>();
  artworkList: Artwork[];
  map;

  // markerIcon
  markerIcon = {
    icon: L.icon({
      iconSize: [25, 41],
      iconAnchor: [13, 16],
      iconUrl: 'assets/icons/marker.svg',
      shadowUrl: 'assets/icons/marker-shadow.png'
    })
  };

  constructor(
    private artworkService: ArtworkService,
    private elementRef: ElementRef
  ) { }

  ngOnInit() {
    this.map = L.map('map', {
      center: [48.208, 16.373],
      zoom: 13,
      zoomControl: false,
    });

    this.refresh();

    // base layer
    L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
      attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
      maxZoom: 18,
      minZoom: 13,
      id: 'mapbox.streets',
      accessToken: 'pk.eyJ1IjoicHcxN2wwMDgiLCJhIjoiY2pua2c2OWxuMGVkOTNxbWh5MWNqajEwdyJ9.X_SuGwNGs12TwCsrsUvBxw'
    }).addTo(this.map);

    MapButtonsComponent.renderZoom(this.map);
    MapButtonsComponent.renderCompass(this.map);
    MapButtonsComponent.renderLocation(this.map);
  }

  previewArtwork() {
   this.map.eachLayer(layer => {
  if (layer instanceof L.Marker) {
    this.map.removeLayer(layer);
  }
});
let previewedMarker = L.marker([artwork.latitude, artwork.longitude], this.markerIcon)
  .addTo(this.map)
  .on('mouseover', () => {
    openPopup();
  });
}

  }
}

The following approach could be considered. Lets assume the initial data for markers is represented in the following format:

locations = [
    {
      name: "Oslo",
      lat: 59.923043,
      lng: 10.752839
    },
    {
      name: "Stockholm",
      lat: 59.339025,
      lng: 18.065818
    },
    //..
  ];

Then for the given data markers on map could be initialized like this:

this.markers = this.locations.map(loc => {
    const marker = new L.marker({ lat: loc.lat, lng: loc.lng });
    marker.bindPopup(loc.name);
    marker.addTo(map);
    return marker;
}); 

Now comes the turn of communication between selector component and map itself, lets assume the following selector:

<ul>
  <li (mouseenter) ="mouseEnter(idx) "  (mouseleave) ="mouseLeave(idx)"  *ngFor="let loc of locations; let idx = index">{{ loc.name }}</li>
</ul>

then the popup visibility could be controlled like this:

mouseEnter(selectedIndex) {
   const selectedMarker = this.markers[selectedIndex];
  selectedMarker.openPopup();
}

mouseLeave(selectedIndex) {
   const selectedMarker = this.markers[selectedIndex];
   selectedMarker.closePopup();
}

Here is a demo

Instead of using mouseover you have to listen to mouseenter and mouseleave

On mouseenter you will create a popup and place it on the map, on mouseleave you will close the popup

Example code with jQuery (my postContent equates to your artwork)

$("div.postContent").on("mouseenter", function(e) {
        var postId = $(this).attr("data-postId");

        tooltipPopup = L.popup();   
        var title = postlistByGlobalId[postId].title;
        tooltipPopup.setContent(title);
        tooltipPopup.setLatLng(markersByGlobalId[postId].getLatLng());
        tooltipPopup.openOn(map);

        $(this).addClass('hover');
    });

$("div.postContent").on("mouseleave", function(e) {
        $(this).removeClass('hover');
        map.closePopup(tooltipPopup);
    });

To achieve that you need to give a unique id to all your artworks (postId in my example) and put your markers in an associative array markersByGlobalId

This could be useful: http://franceimage.github.io/map and http://franceimage.github.io/map-v1

My examples are monolitic (1 html, 1 js file). You will need some extra work to use it in your Angular app.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM