CliffCloud / Leaflet.EasyButton

leaflet control buttons with icons and callbacks
http://cliffcloud.github.io/Leaflet.EasyButton/v1/
MIT License
520 stars 124 forks source link

Change button state icon at runtime #105

Open Diferno opened 2 years ago

Diferno commented 2 years ago

Hi! I have a button with 3 states and want to refresh the 3rd state's icon with the count of features retrieved from backend. So far I've tried updating the button.option.state[2].icon in a watcher, but... although the components option have been changed, the button itself doesn't refresh.

Any tips on how to force that refresh?

      const _this = this
      this.searchInArea = this.$leaflet.easyButton({
        states: [{
          stateName: 'search',
          icon: `<i class="mdi mdi-map-search search-in-zone-btn text-primary">${this.$t('searchInZone')}</i>`,
          title: _this.$t('searchInZone'),
          onClick: (btn, map) => {
            btn.state('loading')
            _this.loadFeatures()
              .then(() => {
                btn.state('loaded')
              })
          }
        }, {
          stateName: 'loading',
          icon: `<i class="search-in-zone-btn mdi mdi-loading mdi-spin">${this.$t('loading')}</i>`
        }, {
          stateName: 'loaded',
          icon: `<i class="search-in-zone-btn">Showing ${_this.layerFeatures.length} features</i>`
        }]
      })

(....)

watch: {
    layerFeatures() {
          if (this.layerFeatures) {
            this.searchInArea.options.states.find(st => st.stateName === 'loaded').icon = `<i class="search-in-zone-btn">Showing ${this.layerFeatures.length} features</i>`
          }
        }
}
atstp commented 2 years ago

I'd probably create an empty div for the third state and update it as you would with any other dom element

      const _this = this
      this.searchInArea = this.$leaflet.easyButton({
        states: [{
          stateName: 'search',
          icon: `<i class="mdi mdi-map-search search-in-zone-btn text-primary">${this.$t('searchInZone')}</i>`,
          title: _this.$t('searchInZone'),
          onClick: (btn, map) => {
            btn.state('loading')
            _this.loadFeatures()
              .then(() => {
                btn.state('loaded')
              })
          }
        }, {
          stateName: 'loading',
          icon: `<i class="search-in-zone-btn mdi mdi-loading mdi-spin">${this.$t('loading')}</i>`
        }, {
          stateName: 'loaded',
          icon: `<div id="feature-count-container"></div>`
        }]
      })

const featureCount = document.getElementById('feature-count-container');

watch: {
    layerFeatures() {
          if (this.layerFeatures) {
            featureCount.innerHTML = `<i class="search-in-zone-btn">Showing ${this.layerFeatures.length} features</i>`
          }
        }
}

Let me know if this presents any issues or you have any other questions

Diferno commented 2 years ago

Thx for the answer, but I may be doing something wrong, cause the feature-count-container div can't be found in the DOM.

Is it created even if the initial state is not itself? (currently the initial state is 'search')

atstp commented 2 years ago

Ah, I see. The icon element is created when you initialize the easyButton, but document.getElementById will only find elements that are attached to the document.

What framework/library handles the watch function?

this is a bit hacky, but I think it should get things working for you:

// switch states to attach the icon to the document
this.searchInArea.state('loaded');

// search the dom and grab a reference to the element
const featureCount = document.getElementById('feature-count-container');

// switch back to the default state
this.searchInArea.state('search');