0

Lit-Element updated .props not invoking full re-render of child component, i.e. the javascript code inside firstUpdated() of child constructs a leaflet map based on .props being passed in from parent component, when the parent component updates location and city, it doesn't create a re-render of the map with new location and city.

When user clicks on button to update location from Seattle to Toronto, the parents props are updated and passed to child, however, the map doesn't rebuild itself, how do I force the map to be "rebuilt" (re-rendered) based on the new .props being passed into the child ??

Git repo for my working sample code

THANKS! :) Been struggling with this for days on end - FYI new to Lit-Element.

Index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link type="text/css" href="./styles.css"
  <script src="./webcomponents/webcomponents-loader.js"></script>
  <script>
    if (!window.customElements) { document.write('<!--'); }
  </script>
  <script src="./webcomponents/custom-elements-es5-adapter.js"></script>
  <!-- ! DO NOT REMOVE THIS COMMENT, WE NEED ITS CLOSING MARKERS -->
</head>
<body>

  <app-view></app-view>

</body>
</html>

Index.js:

import './views/app-view.js'

Parent component app-view.js

import { html, LitElement } from 'lit-element';
import './esri-map.js'

class AppView extends LitElement{
  
  static get properties() {
    return {
      location: { type: Object },
      city: { type: String }
    }
  }
  
  constructor() {
    super();
    // set fallback location to seattle -- GET will pull in coordinates for Toronto
    this.location = { lat: "47.608013", long: "-122.335167" }
    this.city = "Seattle"
  }

  render() {
    return html`
    <style>
      #app-container{
        width: 100%,;
        height: 100%;
        display: flex;
      }
      #map-container{
        flex-grow: 1;
        height: 800px;
      }
    </style>
    <button @click=${ (event) => this.updateLocation() }
    >Set to Toronto
    </button>
    <div id="app-container">
      <div id="map-container">
        <esri-map 
          .location=${this.location}
          .city=${this.city}  
        >
        </esri-map>
      </div>
    </div>
    `;
  }

  updateLocation(){
    var oldLocation = this.location;  
    this.location = { lat: "43.651070", long: "-79.347015"} // Set to Toronto
    this.city = "Toronto";  // Set to Toronto
    console.log("New location is: " + this.city)
    console.log("Coordinates: ");
    console.log(this.location);
  }

}

customElements.define('app-view', AppView);

Child Component

import { html, LitElement } from 'lit-element';
import * as L from 'leaflet';
import * as esri from 'esri-leaflet';

class EsriMap extends LitElement{
  static get properties() {
    return {
      location: { type: Object }, // prop passed from parent
      city: { type: String } // prop passed from parent
    }
  }

  render() {
    return html`
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css"/>
    <style>
      #map{
        height: 100%;
        width: 100%;
      }
    </style>
    <h2>Current City: ${this.city} </h2>
    <h3>Coordinates: Lat: ${this.location.lat}</h3>
    <div id="map"></div>
    `
  }

  firstUpdated() {
    const mapNode = this.shadowRoot.querySelector('#map');
    
    // Render map with props from parent component
    var map = L.map(mapNode, {
      maxZoom: 18,
      minZoom: 2,
    }).setView([this.location.lat, this.location.long],8); // [Lat,Lng]
    const esriLayer = esri.basemapLayer('Streets');
    map.addLayer(esriLayer);

    // Render circle with props from parent component
    var circle = L.circle([this.location.lat, this.location.long], {
      color: 'red',
      fillColor: '#f03',
      fillOpacity: 0.5,
      radius: 20000,
    }).addTo(map);
  }

}

customElements.define('esri-map', EsriMap);
Jdd B
  • 1
  • 2

1 Answers1

0

When properties change, the render() function gets invoked.

firstUpdated() gets only invoked after the first update and not on every property change.

Try this:

updated(changedProps) {
  if (changedProps.has('location')) {
    this._setMap();
  }
}

_setMap() {
  const mapNode = this.shadowRoot.querySelector('#map');
  
  if (mapNode == null || this.location == null) {
    return;
  }
    
    // Render map with props from parent component
  var map = L.map(mapNode, {
      maxZoom: 18,
      minZoom: 2,
    }).setView([this.location.lat, this.location.long],8); // [Lat,Lng]
    const esriLayer = esri.basemapLayer('Streets');
    map.addLayer(esriLayer);

    // Render circle with props from parent component
  var circle = L.circle([this.location.lat, this.location.long], {
      color: 'red',
      fillColor: '#f03',
      fillOpacity: 0.5,
      radius: 20000,
    }).addTo(map);
}
toto11
  • 1,552
  • 1
  • 17
  • 18