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

Support more collection data types in v-for #2410

Closed
wenLiangcan opened this Issue Feb 28, 2016 · 36 comments

Comments

Projects
None yet
@wenLiangcan

wenLiangcan commented Feb 28, 2016

At some situations, plain object isn't the best choise. I tried to render a Map object by v-for, but seems Vue does not support it currently. (Here's a post I created in the Help thread on the forum.)

Hope Vue can provide the for ... of syntax in v-for to iterate over data types like Map and Set.

For example:

const map = new Map();
map.set('key1', 'val1');
map.set('key2', 'val2');

and we can render map in this way:

<ul>
    <li v-for="[key, val] of map">{{key}} - {{val}}</li>
</ul>

@yyx990803 yyx990803 changed the title from [Feature Request] Support more collection data types in v-for to Support more collection data types in v-for Feb 28, 2016

@FadyMak FadyMak self-assigned this Mar 22, 2016

@nervgh

This comment has been minimized.

Show comment
Hide comment
@nervgh

nervgh Apr 28, 2016

Contributor

A duplicate of #1319

@wenLiangcan , you could use something like this:

<ul>
    <li v-for="[key, val] of get(map)">{{key}} - {{val}}</li>
</ul>

where get() is your function.

Contributor

nervgh commented Apr 28, 2016

A duplicate of #1319

@wenLiangcan , you could use something like this:

<ul>
    <li v-for="[key, val] of get(map)">{{key}} - {{val}}</li>
</ul>

where get() is your function.

@agalazis

This comment has been minimized.

Show comment
Hide comment
@agalazis

agalazis Aug 25, 2016

haha! similar issues open all the time and and people insist that they cannot justify implementation well people want to use it that's one justification I can also list the bazillion of issues closed that ask the same thing xD. I also found one that justifies the use-case really well and refers to es6 specification when it comes to map order ->still closed.

agalazis commented Aug 25, 2016

haha! similar issues open all the time and and people insist that they cannot justify implementation well people want to use it that's one justification I can also list the bazillion of issues closed that ask the same thing xD. I also found one that justifies the use-case really well and refers to es6 specification when it comes to map order ->still closed.

@posva

This comment has been minimized.

Show comment
Hide comment
@posva

posva Aug 25, 2016

Member

People wanting to use a feature is not, only by itself, an argument that can justify the need of having such feature, it's necessary to weigh the cost and benefits (what problem is being solved) of adding such feature

Member

posva commented Aug 25, 2016

People wanting to use a feature is not, only by itself, an argument that can justify the need of having such feature, it's necessary to weigh the cost and benefits (what problem is being solved) of adding such feature

@agalazis

This comment has been minimized.

Show comment
Hide comment
@agalazis

agalazis Aug 25, 2016

Yep but still the arguments that people use to close the issue are still not valid or at least not valid for all usecases eg the example of the elipen who justified his usecases very well as I mentioned above

agalazis commented Aug 25, 2016

Yep but still the arguments that people use to close the issue are still not valid or at least not valid for all usecases eg the example of the elipen who justified his usecases very well as I mentioned above

@LinusBorg

This comment has been minimized.

Show comment
Hide comment
@LinusBorg

LinusBorg Aug 25, 2016

Member

If you want to discuss a specific issue, link to it please.

Plus, this Feature issue is open. It does not make sense to have more than one issue open for the same request.

Member

LinusBorg commented Aug 25, 2016

If you want to discuss a specific issue, link to it please.

Plus, this Feature issue is open. It does not make sense to have more than one issue open for the same request.

@catsclaw

This comment has been minimized.

Show comment
Hide comment
@catsclaw

catsclaw Sep 7, 2016

It's important to be able to iterate over iterators in loops. That seems plainly obvious. It's a fundamental feature of the language.

The reasons for supporting it are:

  1. Iterators, Maps, and Sets are all valid ES6. Refusing to support them means limiting yourself to ES5, which is a decision becoming less and less justified over time.
  2. I'm building an application that has internal data stored in Maps and Sets. Instead of making them available to the UI, I now need to keep the data synced between the two manually, or write boilerplate and import it into my templates to do the conversion whenever the data is needed. This is exactly what Vue is intended to avoid.

catsclaw commented Sep 7, 2016

It's important to be able to iterate over iterators in loops. That seems plainly obvious. It's a fundamental feature of the language.

The reasons for supporting it are:

  1. Iterators, Maps, and Sets are all valid ES6. Refusing to support them means limiting yourself to ES5, which is a decision becoming less and less justified over time.
  2. I'm building an application that has internal data stored in Maps and Sets. Instead of making them available to the UI, I now need to keep the data synced between the two manually, or write boilerplate and import it into my templates to do the conversion whenever the data is needed. This is exactly what Vue is intended to avoid.
@inca

This comment has been minimized.

Show comment
Hide comment
@inca

inca Dec 21, 2016

Since #1319 is closed, it's worth reiterating on current decision here. In short, the feature is not trivial to implement (on observation mechanism level), so it's not about justifying use cases, it's about the amount of work and trade-offs.

I would really appreciate this feature, too. On the other hand, if observing ES6 data types becomes terribly hacky or, for example, compromise performance or other qualities, then people who do not currently use Maps and Sets with Vue might not appreciate this change.

inca commented Dec 21, 2016

Since #1319 is closed, it's worth reiterating on current decision here. In short, the feature is not trivial to implement (on observation mechanism level), so it's not about justifying use cases, it's about the amount of work and trade-offs.

I would really appreciate this feature, too. On the other hand, if observing ES6 data types becomes terribly hacky or, for example, compromise performance or other qualities, then people who do not currently use Maps and Sets with Vue might not appreciate this change.

@lmj0011

This comment has been minimized.

Show comment
Hide comment
@lmj0011

lmj0011 Jun 9, 2017

I suppose using Array.from() inside a computed function will have to be your best friend for now. 😞

lmj0011 commented Jun 9, 2017

I suppose using Array.from() inside a computed function will have to be your best friend for now. 😞

@alexsandro-xpt

This comment has been minimized.

Show comment
Hide comment
@alexsandro-xpt

alexsandro-xpt Jul 27, 2017

Any solution for that?

alexsandro-xpt commented Jul 27, 2017

Any solution for that?

@nickmessing

This comment has been minimized.

Show comment
Hide comment
@nickmessing

nickmessing Jul 27, 2017

Member

Small update, this will come if/when Vue decides to drop "legacy" browsers and will move to Evergreen ones with Proxy instead of set/get for reactivity.

@alexsandro-xpt, just use a computed function that returns Array.from(yourDataSet).

Member

nickmessing commented Jul 27, 2017

Small update, this will come if/when Vue decides to drop "legacy" browsers and will move to Evergreen ones with Proxy instead of set/get for reactivity.

@alexsandro-xpt, just use a computed function that returns Array.from(yourDataSet).

@alexsandro-xpt

This comment has been minimized.

Show comment
Hide comment
@alexsandro-xpt

alexsandro-xpt Jul 27, 2017

@nickmessing I try with Map, doesn't work.
The computed array length value is always 0.

alexsandro-xpt commented Jul 27, 2017

@nickmessing I try with Map, doesn't work.
The computed array length value is always 0.

@inca

This comment has been minimized.

Show comment
Hide comment
@inca

inca Jul 27, 2017

Just Array.from is probably not solution you want because of lack of reactivity (changes to yourDataSet will not get propagated to Vue).

As mentioned earlier, Sets and Maps are not observable by Vue. In order to use those — either in v-for, or in computed properties, methods, watchers, template expressions, etc. — you need to create a serializable replica of this structure and expose it to Vue. Here's a naive example which uses a simple counter for providing Vue with information that Set is updated:

data() {
  mySetChangeTracker: 1,
  mySet: new Set(),
},
  
computed: {
  mySetAsList() { 
    // By using `mySetChangeTracker` we tell Vue that this property depends on it,
    // so it gets re-evaluated whenever `mySetChangeTracker` changes
    return this.mySetChangeTracker && Array.from(this.mySet);
  },
},

methods: {
  add(item) {
    this.mySet.add(item);
    // Trigger Vue updates
    this.mySetChangeTracker += 1;
  }
}

This illustrates a kinda hacky but 100% working method for making non-observable data reactive. Still, in real world cases I ended up with serialized versions of Sets/Maps (e.g. you'd probably want to store the modified versions of sets/maps in localstorage and thus serialize them anyway), so no artificial counters/hacks were involved.

I personally think this is a fair solution to a problem, but it definitely deserves some official documentation — otherwise it's impossible to justify this as non-hacky way of dealing with Vue internals.

inca commented Jul 27, 2017

Just Array.from is probably not solution you want because of lack of reactivity (changes to yourDataSet will not get propagated to Vue).

As mentioned earlier, Sets and Maps are not observable by Vue. In order to use those — either in v-for, or in computed properties, methods, watchers, template expressions, etc. — you need to create a serializable replica of this structure and expose it to Vue. Here's a naive example which uses a simple counter for providing Vue with information that Set is updated:

data() {
  mySetChangeTracker: 1,
  mySet: new Set(),
},
  
computed: {
  mySetAsList() { 
    // By using `mySetChangeTracker` we tell Vue that this property depends on it,
    // so it gets re-evaluated whenever `mySetChangeTracker` changes
    return this.mySetChangeTracker && Array.from(this.mySet);
  },
},

methods: {
  add(item) {
    this.mySet.add(item);
    // Trigger Vue updates
    this.mySetChangeTracker += 1;
  }
}

This illustrates a kinda hacky but 100% working method for making non-observable data reactive. Still, in real world cases I ended up with serialized versions of Sets/Maps (e.g. you'd probably want to store the modified versions of sets/maps in localstorage and thus serialize them anyway), so no artificial counters/hacks were involved.

I personally think this is a fair solution to a problem, but it definitely deserves some official documentation — otherwise it's impossible to justify this as non-hacky way of dealing with Vue internals.

@nickmessing

This comment has been minimized.

Show comment
Hide comment
@nickmessing

nickmessing Jul 27, 2017

Member

@alexsandro-xpt, sorry, I was wrong, computed will be hacky as @inca said, another hacky solution would be using $forceUpdate with a method, here's an example fiddle

Member

nickmessing commented Jul 27, 2017

@alexsandro-xpt, sorry, I was wrong, computed will be hacky as @inca said, another hacky solution would be using $forceUpdate with a method, here's an example fiddle

@alexsandro-xpt

This comment has been minimized.

Show comment
Hide comment
@alexsandro-xpt

alexsandro-xpt Jul 28, 2017

Thank you @nickmessing and @inca, this work fine with my new Map()!!

alexsandro-xpt commented Jul 28, 2017

Thank you @nickmessing and @inca, this work fine with my new Map()!!

@dsandber

This comment has been minimized.

Show comment
Hide comment
@dsandber

dsandber Aug 13, 2017

Right now when you do a "v-for" over a "Map", the v-for acts as if it had received an empty array.

Regardless of the outcome of the extended discussion about whether/how to support Maps and Sets, it would save a lot of people a lot of debugging time if Vue simply warned "Maps and Sets are not yet supported -- see #2410".

dsandber commented Aug 13, 2017

Right now when you do a "v-for" over a "Map", the v-for acts as if it had received an empty array.

Regardless of the outcome of the extended discussion about whether/how to support Maps and Sets, it would save a lot of people a lot of debugging time if Vue simply warned "Maps and Sets are not yet supported -- see #2410".

@warent

This comment has been minimized.

Show comment
Hide comment
@warent

warent Sep 5, 2017

Yup, Google search for this feature brought me to this ticket (after a few annoying mixups with Vue.set)

👍 This should be in the v-for documentation!

warent commented Sep 5, 2017

Yup, Google search for this feature brought me to this ticket (after a few annoying mixups with Vue.set)

👍 This should be in the v-for documentation!

@alexsandro-xpt

This comment has been minimized.

Show comment
Hide comment
@alexsandro-xpt

alexsandro-xpt Sep 5, 2017

Truly, should be in v-for documentation!

alexsandro-xpt commented Sep 5, 2017

Truly, should be in v-for documentation!

@yyx990803

This comment has been minimized.

Show comment
Hide comment
@yyx990803

yyx990803 Sep 5, 2017

Member

/cc @chrisvfritz let's try to add a note about support for these types up in the docs for v-for (both API and the list rendering section) - I'll also take a look at them in 2.5.

Member

yyx990803 commented Sep 5, 2017

/cc @chrisvfritz let's try to add a note about support for these types up in the docs for v-for (both API and the list rendering section) - I'll also take a look at them in 2.5.

@yyx990803 yyx990803 closed this Sep 5, 2017

@chrisvfritz

This comment has been minimized.

Show comment
Hide comment
@chrisvfritz

chrisvfritz Sep 7, 2017

Member

@yyx990803 I wonder if a console warning would be better for this, since that would tell people what's wrong immediately, obviating the need to search for the solution.

We're also already very explicit in the docs about which types we do support, Map and Set not being among them. I can definitely see the argument for why one might hope all iterables would work with v-for, but I don't think we currently give readers any reason to expect they would.

Member

chrisvfritz commented Sep 7, 2017

@yyx990803 I wonder if a console warning would be better for this, since that would tell people what's wrong immediately, obviating the need to search for the solution.

We're also already very explicit in the docs about which types we do support, Map and Set not being among them. I can definitely see the argument for why one might hope all iterables would work with v-for, but I don't think we currently give readers any reason to expect they would.

@schmod

This comment has been minimized.

Show comment
Hide comment
@schmod

schmod Sep 11, 2017

I'm not quite seeing the argument against adding support for Set.

Set itself can be cleanly polyfilled, and unless I'm missing something, it seems like Vue's approach for adding reactivity to arrays could very easily be extended to sets. We'd only need to wrap .add(), .clear(), and .delete().

schmod commented Sep 11, 2017

I'm not quite seeing the argument against adding support for Set.

Set itself can be cleanly polyfilled, and unless I'm missing something, it seems like Vue's approach for adding reactivity to arrays could very easily be extended to sets. We'd only need to wrap .add(), .clear(), and .delete().

@inca

This comment has been minimized.

Show comment
Hide comment
@inca

inca Sep 12, 2017

My best guess (please correct/sorry if I'm wrong): the trickiest part is to wrap a Set constructor, which accepts an iterable. I don't see how iterable can be made observable, because in its general form it's just a function (i.e. next) with no referenceable state (think of generator-based iterator as an example).

inca commented Sep 12, 2017

My best guess (please correct/sorry if I'm wrong): the trickiest part is to wrap a Set constructor, which accepts an iterable. I don't see how iterable can be made observable, because in its general form it's just a function (i.e. next) with no referenceable state (think of generator-based iterator as an example).

@schmod

This comment has been minimized.

Show comment
Hide comment
@schmod

schmod Sep 12, 2017

Why do we need to wrap the constructor? Wouldn't we be passing pre-existing Sets into Vue?

According to the spec, the Set constructor immediately runs through the entire iterator, effectively retaining a shallow copy of the unique items returned by the iterator. Once you've got a Set instance, it shouldn't matter whether it was created from an iterator or not.

In this regard, a Set created from an iterator should be no different from an array created from an iterator (via Array.from()), which Vue already supports.

schmod commented Sep 12, 2017

Why do we need to wrap the constructor? Wouldn't we be passing pre-existing Sets into Vue?

According to the spec, the Set constructor immediately runs through the entire iterator, effectively retaining a shallow copy of the unique items returned by the iterator. Once you've got a Set instance, it shouldn't matter whether it was created from an iterator or not.

In this regard, a Set created from an iterator should be no different from an array created from an iterator (via Array.from()), which Vue already supports.

@Glidias

This comment has been minimized.

Show comment
Hide comment
@Glidias

Glidias Sep 19, 2017

You can use immutable Maps/sets/ whatever data structures and allow reactivity with them though, but simply because the whole chunk reference changes. You can render them through a render function or computed generated array (the earlier being better in performance as it skips the creation of an array..). But mutable data structures, not so unless you find a way to notify Vue of specific changes manually, which would be simply homebrewing yr own solution.

Glidias commented Sep 19, 2017

You can use immutable Maps/sets/ whatever data structures and allow reactivity with them though, but simply because the whole chunk reference changes. You can render them through a render function or computed generated array (the earlier being better in performance as it skips the creation of an array..). But mutable data structures, not so unless you find a way to notify Vue of specific changes manually, which would be simply homebrewing yr own solution.

@zDream

This comment has been minimized.

Show comment
Hide comment
@zDream

zDream Dec 27, 2017

That's no good. You can't do it

zDream commented Dec 27, 2017

That's no good. You can't do it

@zDream

This comment has been minimized.

Show comment
Hide comment
@zDream

zDream Dec 27, 2017

@wenLiangcan

var map = new Map()
  map.set('key1','Test1')
  map.set('key2','Test2')

<div class="info" v-for="(key,value) in dataArray">
        <div class="file-name">{{key}}</div>
        <div class="file-size">{{value}}</div>
 </div>

No, it doesn't show up on the page

zDream commented Dec 27, 2017

@wenLiangcan

var map = new Map()
  map.set('key1','Test1')
  map.set('key2','Test2')

<div class="info" v-for="(key,value) in dataArray">
        <div class="file-name">{{key}}</div>
        <div class="file-size">{{value}}</div>
 </div>

No, it doesn't show up on the page

@tomeightyeight

This comment has been minimized.

Show comment
Hide comment
@tomeightyeight

tomeightyeight Jan 19, 2018

Keen for support of this : )

tomeightyeight commented Jan 19, 2018

Keen for support of this : )

@alexsandro-xpt

This comment has been minimized.

Show comment
Hide comment
@alexsandro-xpt

alexsandro-xpt commented Jan 26, 2018

Me too

@lloydjatkinson

This comment has been minimized.

Show comment
Hide comment
@lloydjatkinson

lloydjatkinson Jan 30, 2018

Any future plans to add support? Is there a technical reason Vue could not support Map and Set?

lloydjatkinson commented Jan 30, 2018

Any future plans to add support? Is there a technical reason Vue could not support Map and Set?

@steappe

This comment has been minimized.

Show comment
Hide comment
@steappe

steappe Feb 10, 2018

The current problem with the Vue.set method on a plain object is that it triggers way too many subscribers when a property is added to the object. Actually, all the subscribers of all the properties are triggered when only one property is added.

The performance of the view is badly impacted when a map like collection contains hundredth of keys. For example, in my project thousands of subscribers are triggered when one element is added to the map using the Vue.set operation:

Vue.set(state.items, itemId, item); // where items is a plain object.

When I look deeply into the Vue.js code, I can see where the problem comes from. The dependencies that are triggered are those of the object, which means that if the object has one property for each key, then all the dependencies of all the keys are triggered when just adding one key.

So using plain objects to mimic a map does not look as the right solution, and therefore having support for a map in vue is more than welcomed for large collections of items.

steappe commented Feb 10, 2018

The current problem with the Vue.set method on a plain object is that it triggers way too many subscribers when a property is added to the object. Actually, all the subscribers of all the properties are triggered when only one property is added.

The performance of the view is badly impacted when a map like collection contains hundredth of keys. For example, in my project thousands of subscribers are triggered when one element is added to the map using the Vue.set operation:

Vue.set(state.items, itemId, item); // where items is a plain object.

When I look deeply into the Vue.js code, I can see where the problem comes from. The dependencies that are triggered are those of the object, which means that if the object has one property for each key, then all the dependencies of all the keys are triggered when just adding one key.

So using plain objects to mimic a map does not look as the right solution, and therefore having support for a map in vue is more than welcomed for large collections of items.

@cawa-93

This comment has been minimized.

Show comment
Hide comment
@cawa-93

cawa-93 Mar 16, 2018

Is there any news about future plans and possibly about native Map/Set support?

cawa-93 commented Mar 16, 2018

Is there any news about future plans and possibly about native Map/Set support?

@tomeightyeight

This comment has been minimized.

Show comment
Hide comment
@tomeightyeight

tomeightyeight Apr 3, 2018

This article details upcoming support in 2.6 - but there's nothing about that in the official roadmap from what I can see?

https://medium.com/@alberto.park/the-status-of-javascript-libraries-frameworks-2018-beyond-3a5a7cae7513

"The current latest of the core is 2.5.x. Next minor release(v2.6), will support native ESM import, improved async error handling, iterator on ‘v-for’ directive and more."

Not sure where they got that information from?

tomeightyeight commented Apr 3, 2018

This article details upcoming support in 2.6 - but there's nothing about that in the official roadmap from what I can see?

https://medium.com/@alberto.park/the-status-of-javascript-libraries-frameworks-2018-beyond-3a5a7cae7513

"The current latest of the core is 2.5.x. Next minor release(v2.6), will support native ESM import, improved async error handling, iterator on ‘v-for’ directive and more."

Not sure where they got that information from?

nd9600 added a commit to nd9600/flowyClone that referenced this issue May 30, 2018

@blackst0ne

This comment has been minimized.

Show comment
Hide comment
@blackst0ne

blackst0ne Aug 6, 2018

Found this issue while was debugging the heck Vue's behavior over Set data objects. 🤔

blackst0ne commented Aug 6, 2018

Found this issue while was debugging the heck Vue's behavior over Set data objects. 🤔

@OCJvanDijk

This comment has been minimized.

Show comment
Hide comment
@OCJvanDijk

OCJvanDijk Aug 7, 2018

For people like me that were wondering about the roadmap for this, Evan You says in this video that Map and Set support is "likely" to arrive in 2.6, but that was in May, so that's all I know.

OCJvanDijk commented Aug 7, 2018

For people like me that were wondering about the roadmap for this, Evan You says in this video that Map and Set support is "likely" to arrive in 2.6, but that was in May, so that's all I know.

@bradisbell

This comment has been minimized.

Show comment
Hide comment
@bradisbell

bradisbell Aug 14, 2018

@yyx990803 It's unfortunate that this issue in the tracker is marked as closed, especially if you're considering adding support in the near future. Where can we follow progress on this feature? Is there another issue somewhere?

bradisbell commented Aug 14, 2018

@yyx990803 It's unfortunate that this issue in the tracker is marked as closed, especially if you're considering adding support in the near future. Where can we follow progress on this feature? Is there another issue somewhere?

@steven87vt

This comment has been minimized.

Show comment
Hide comment
@steven87vt

steven87vt Aug 21, 2018

Just wondering for the sake of argument, and maybe I am doing it wrong, but since you can track array mutation using the Mutation Methods cant you just track an array of an object and be logically complete? you dont get all the same features implemented in Map but the ones you want would be pretty easily addressed especially if you are using something like _ or lodash.

steven87vt commented Aug 21, 2018

Just wondering for the sake of argument, and maybe I am doing it wrong, but since you can track array mutation using the Mutation Methods cant you just track an array of an object and be logically complete? you dont get all the same features implemented in Map but the ones you want would be pretty easily addressed especially if you are using something like _ or lodash.

@pkanane

This comment has been minimized.

Show comment
Hide comment
@pkanane

pkanane Sep 5, 2018

Its sad but until the team adds this we might have to use alternate data structures

pkanane commented Sep 5, 2018

Its sad but until the team adds this we might have to use alternate data structures

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment