0
votes

I'm new to Ember and have a leaking state problem. I have a carousel widget that displays one item at a time and allows the user to click previous/next to see each item.

Here's the simplified carousel's component:

<button {{action "nextItem"}}>Next</button>
{{carousel-item item=selectedItem}}

Clicking next changes the selectedItem property so the next item is shown.

What I've realized is that the carousel-item component isn't re-initialized every time I move to a previous/next item. The DOM is reused each time, and the component's properties are shared since it's all one instance, which means I can have leaking state.

The alternative I see is to render all the items initially, so each has its own instance:

{{#each items as |item|}}
  {{carousel-item item=item}}
{{/each}}

and to hide all but the selected item using CSS. However, this option kind of feels like a jQuery hack -- seems like Ember would have a better way. And I'm only ever showing one item at a time, so I hate to have so many extra DOM nodes when I don't need them.

What is the recommended way to handle this kind of a UI, where you only need one item shown at a time but don't want to share state between items? I'd imagine I should have one instance of the carousel-item component per item, instead of sharing an instance across all of them. But it doesn't feel right to instantiate every single carousel-item at first either. And I can't imagine the Ember way is to worry too much about the DOM details myself (determining which one is shown/hidden based on a class and some CSS).

2
I don't understand what is the actual state you are leaking? Isn't the state derived from item anyway?locks

2 Answers

0
votes

Firstly, whatever framework or library you are using, jQuery, ember, angular, react, they are just a pack of JS/HTML/CSS right? So you should think in it's way, there is no magic!

So of course 1 component will only create 1 instance. If you just changed it's property(item in your demo), it just changed the property of an instance, other properties of it will remain as it is and triggered re-render. You cannot expect more. You have to manually reset other properties.

And yes, rendering everything by {{each}} looks stupid, but think about it, how could you create a smooth carousel animation by render only one DOM? At least you need to render 3 (current, previous and next) right?

Since carousel is a common UI, I recommend you to check existing ember addons fist before you write by yourself: https://emberobserver.com/?query=carousel

0
votes

If I understood your problem correctly, the willUpdate hook in Ember.Component class should help you out. I this hook you can clear up the attributes, remove DOM objects, or anything at all. This will be called each time the component is about to re-render itself.

A simple example is of form:

willUpdate() {
    Ember.$(this.get('element')).empty();
  },

This will clear the DOM on each re-render forcing it to redraw elements.

You can try out other hooks too and see which event will serve your need. All of them are very helpful and serve different purpose.