Skip to content

Added Icon component to easily add dynamic composed marker icons #268

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 6, 2018
Merged

Conversation

byWulf
Copy link
Contributor

@byWulf byWulf commented Nov 4, 2018

As suggested in #267 I added a LIcon component which can be placed inside a marker for dynamically generating the icon.

This way you can bind dynamic values to properties of the icon (f.e. icon size to make the icon smaller on zoom out) and you can place HTML code inside the <l-icon> tag to transform it into a L.divIcon, which updates its content as Vue rerenders the slot template.

I also added an example file (examples/src/components/Icon.vue) to see the different implementation possibilities.

for (let i = 0; i < keys.length; i++) {
this.$watch(keys[i], (newVal, oldVal) => {
//TODO: why is watch triggered on foreign props when changing icon size in my example?
if (JSON.stringify(newVal) === JSON.stringify(oldVal)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm relatively new to Vue, so if someone could help me out how to avoid this line, I would be very thankful!

Problem is, when sliding the slider in the example, the watcher is also triggered for the fourth icon elements properties, but it should only be called for the iconSize and iconAnchor properties of the third icon.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vue pass any prop written in the component template to the $props, even if not defined in the props definition.
Also you don't need this method, for an inspiration please take a look at how the function in utils/propsBinder.js works and how is used in any other component.

The gist of it is that you can write a function in the methods called: setPROPNAME and that function will be called only when your props change.

Copy link
Contributor Author

@byWulf byWulf Nov 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your reply and your awesome code review!

I updated the code (marked all props as custom so the component functions will get called, because Icon doesn't have setters on an existing Icon), but when you check it out and test the example, you will notice that it still doesn't behave correct.

When changing the slider, the first icon will get recreated twice (because two props are affected by the slider), AND the second icon is also recreated (because of the iconAnchor prop). This prop shouldn't change.
Also when you change the text, again the first icon is recreated twice (because of the iconSize and iconAnchor props) and the second one also twice (first correctly because of the html change but then also because of the iconAnchor prop).

------ Page load:
1541353711324 - recreate icon 1 (initial)
1541353711327 - recreate icon 2 (initial)
------ Changing slider:
1541353713991 - recreate icon 1 (iconSize)
1541353713992 - recreate icon 1 (iconAnchor)
1541353713994 - recreate icon 2 (iconAnchor)
------ Changing text:
1541353720983 - recreate icon 1 (iconSize)
1541353720985 - recreate icon 1 (iconAnchor)
1541353720986 - recreate icon 2 (iconAnchor)
1541353720988 - recreate icon 2 (html slot)

So two problems:

  1. When two or more props on the same component are change simultanuosly, is it possible to trigger a recreation only once?
  2. Why are all props detected as changed (even on different icon components), when only one of them changes?

I hope you can help me out here too! :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@byWulf this could be an issue with the fact that the icon component gets recreated by his parent ( marker) you can check this by verifying that the mounted / updated lifecycle hooks get called.

there is a lot of performance issues with icon and markers but I have the feeling that you can get it right ( and I will help you to do so )

Really appreciate all your efforts and attitude to this!
If you want to discuss this more faster ping me on Discord, in VueLand you can find me by the name of DonNico

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lordfuoco I spent the last night on analysing and tweaking the performance.

Problem 1 was in the example, that I didn't bind computed values to the component, but directly binded arrays (<l-icon :icon-anchor="[0, 50]" />). This led to vue thinking the value has changed on every check. <l-icon :icon-anchor="iconAnchor" /> + `computed: { iconAnchor() { return [0, 50]; }}`` solved this.

Problem 2 with the "if multiple props were affected by a single change, the icon gets recreated also multiple times" was solved by scheduling a recreation on $nextTick and remember in a data variable, if recreation in this tick already happend, if multiple recreations were scheduled.

Also I added another tweak: If only the html content changed, I just swap the HTML in the DOM and don't recreate the icon itself.

In my real world project (880 markers, where only few of them have individual divIcons) it was recommended to put the default icons in data variables and bind them to the icon prop of the marker and add an v-if to the l-icon component. Works like a charme now :)

Looking forward for your feedback/code review on the current status, so we could release it soon :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@byWulf Amazing work! I will need to go through it thoroughly, but I think that by the end of the day I should be able to check it

Copy link
Member

@DonNicoJs DonNicoJs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good work! Please address my comments and then we can proceed to merge this.
We will need to do some performance tests but it seems okay to me.
Please remember to run npm run lint or enable an automatic linter in your editor of choice :)

name: 'LIcon',
mixins: [Options],
props: {
iconUrl: String,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the props please define the default value even if null or undefined this triggers linting errors :)

for (let i = 0; i < keys.length; i++) {
this.$watch(keys[i], (newVal, oldVal) => {
//TODO: why is watch triggered on foreign props when changing icon size in my example?
if (JSON.stringify(newVal) === JSON.stringify(oldVal)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vue pass any prop written in the component template to the $props, even if not defined in the props definition.
Also you don't need this method, for an inspiration please take a look at how the function in utils/propsBinder.js works and how is used in any other component.

The gist of it is that you can write a function in the methods called: setPROPNAME and that function will be called only when your props change.

@DonNicoJs DonNicoJs merged commit ac4f076 into vue-leaflet:master Nov 6, 2018
@DonNicoJs
Copy link
Member

@byWulf Merged, I did not find anything to complain about in the code ;)

@byWulf
Copy link
Contributor Author

byWulf commented Nov 6, 2018

@lordfuoco Thanks again for the great review and response!

@laurencei
Copy link

@byWulf @lordfuoco - thanks for this.

This is the exact feature I was looking for today - so glad it's been implemented. Seems to be working perfectly for me so far.

@DonNicoJs
Copy link
Member

@byWulf I am almost done writing the docs, the base of it. When I am done would you like to refine the section about l-icon ? with tips and stuff, since you wrote it :)

@byWulf
Copy link
Contributor Author

byWulf commented Dec 1, 2018

@lordfuoco just let me know when I can help :)

@DonNicoJs
Copy link
Member

@byWulf the documentation is online :) the docs are run with docsify

@blackspike
Copy link

Hi all, I'm not sure the custom text is working in the div icons, it only seems to show an image – not the divIcon content.

The example isn't showing text for me either

@DonNicoJs
Copy link
Member

@blackspike the example that you linked does not use the custom icon, maybe you linked not the one that you mentioned ? Can we see a bit of your component code ?

@blackspike
Copy link

blackspike commented Jan 30, 2019

Apologies the link does not go directly to the example, same page but last example in the list Custom Marker Icons , there's a text box called 'Custom text' that doesn't seem to work.

@MagicTrevor
Copy link

MagicTrevor commented Feb 22, 2019

On the example it seems to be throwing the same error I am getting in my project which seems to prevent it from working correctly:

screen shot 2019-02-21 at 6 28 49 pm

I am under the impression that the following should work:

<l-marker :key="grid.id" :lat-lng="grid.getCenter()">
  <l-icon class-name="zone-label">
    <div class="zone-text">test</div>
  </l-icon>
</l-marker>

@DonNicoJs
Copy link
Member

I will give a look here @blackspike @MagicTrevor.

Sorry for the delay I am a bit swamped at the moment. if anyone wants to take this please let me know it would be really helpful

@byWulf byWulf mentioned this pull request Feb 22, 2019
@byWulf
Copy link
Contributor Author

byWulf commented Feb 22, 2019

Hey @DonNicoJs , just started looking into this when you wrote that (telapathy :D). And I found the bug and created a PR which fixes divIcons :)

See #342

@blackspike @MagicTrevor

@MagicTrevor
Copy link

Thanks @byWulf! If this is resolved will there be a patch release? Or an updated 2.0.0-beta?

@DonNicoJs
Copy link
Member

@MagicTrevor I think I am releasing 2.0.0 as not beta soon with this fix included. In this week I think. I just need to update the docs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants