googlearchive / TemplateBinding

TemplateBinding Prolyfill
290 stars 61 forks source link

Should be able to <template repeat> on object values just like we do on array values. #116

Closed krisnye closed 10 years ago

krisnye commented 11 years ago

It would also be nice for both arrays and objects to be able to have an index value available for binding. For instance:

<template repeat="{{ myarray }}" index="i"> {{ i }} {{ somevalue }}</template>

Demonstration of object repeat template not working:

<!DOCTYPE html>
<html>
  <head>
    <script src="polymer.min.js" debug></script>
    <element name="v-bug">
        <template>
            Array Template:
            <template repeat="{{arraydata}}">
                {{}}
            </template><br>
            Object Template:
            <template repeat="{{objectdata}}">
                {{}}
            </template>
        </template>
        <script>
        Polymer.register(this, {
            objectdata: { alpha: 1, beta: 2, charlie: 3 },
            arraydata: [1,2,3]
        });
        </script>
    </element>
  </head>
  <body>
    <v-bug></v-bug><br>
  </body>
</html>
rafaelw commented 11 years ago

I think the filters implemented by the syntax can handle this. I don't know what the right syntax is but something like

<template repeat="{{ myObj | someFilterThatObservesObjectAndExposesConsistentArray }}">

The access to index is a separate issue. Opened another bug here: https://github.com/Polymer/mdv/issues/117

krisnye commented 11 years ago

How you implement this does relate to the index issue. If you use a filter that maps the object to an array, then you will lose the key/index.

I think of the index as the key for the value in the array or object.

If I iterate over { "kris": 1, "bob": 2 } then I would expect the index, value pairs to be [["kris",1],["bob",2]]. If you map this to an array though then we would get [[0,1],[1,2]].

The MDV templating syntax should ideally not require developers to structure our data in any particular way. If it does, then we have to go through an intermediate step of converting our data to a MDV friendly model before using it. This is difficult, and means we must also manually watch our original data for changes. Not good.

I would like to use my data in whatever format I keep it, and we don't always keep our items in arrays. We use objects as hashtables very frequently to store items.

arv commented 11 years ago

I've said it before and I'll say it again. I think the right solution is to follow the for-of path of ES6. Here are some examples from ES6.

var array = ['Hi'];
for (let value of array) {
  print(value);  // 'Hi'
}

for (let key of array.keys()) {
  print(key);  // '0'
}

for (let [key, value] of array.enties()) {
  print(key);  // '0'
  print(value);  // 'Hi'
}

So applying this to MDV repeat:

<template repeat="{{value of array}}">
  {{value}}
</template>
<template repeat="{{key of array.keys()}}">
  {{key}}
</template>
<template repeat="{{[value, key] of array.entries()}}">
  {{key}} {{value}}
</template>
rafaelw commented 11 years ago

@arv I like that alot.

rafaelw commented 11 years ago

Strawman:

-Support "ES6-like simple destructuring syntax for named scopes". e.g.

<template repeat="{{ [index, user] of entries(user) }}">

Where this isn't really full destructuring (you can't do [index, [id, name]] of...), but it does work for a accessing the first two items of an element array.

The entries() is a filter which observer the underlying array (and keeps the key values correct if it mutates). (Note that we're still not settled on how filters work -- the right side of the above expression may change syntax).

This has the benefit of:

-not having to choose between picking a magical index/key name that may collide with user data...or picking some weirdo identifier. -it allows multiple key/index values to be in scope, e.g.

<template repeat="{{ [userIndex, user] of entries(user) }}">
  <template repeat="{{ [projectIndex, project] of entries(user.projects) }}">
    <!-- userIndex and projectIndex are both accessible here -->
  </template>
</template>

I think this is implementable, though I can imagine wrinkles arising. Thoughts?

rafaelw commented 11 years ago

@krisnye, I guess I've proved your point that these issues are related =-).

justinfagnani commented 11 years ago

FWIW, I've been implement comprehensions in the Dart custom syntax I've been working on (with the goal of compatibility with Polymer). So far I've been using Dart-like "in" syntax, with some additional sugar. For example:

<template repeat="{{ a, b in myMap }}">

or

<template repeat="{{ x*x as y for x in range(0, 10) where x % 2 == 0 }}">

So far so good. I prefer "in", like Python comprehensions and for..in loops, but JS and I presume Polymer will use "of" so that seems a clear way to go. I like brackets for introducing identifiers as it allows multiple identifiers to be added with "as" even for lists:

<template repeat="{{ [x*x as y, x] for x of range(0, 10) }}">

or

<template repeat="{{ [index, user.name as name, user.id as id] for entry of enumerate(users) }}">

(where enumerate() is like Python's)

krisnye commented 11 years ago

I like the ES6 compatible syntax as well.

arv commented 11 years ago

@justinfagnani I don't think we can really do iterables like what you showed with the comprehension example. To support iterables we would have to reiterate using dirty checking because iterables have no state and no notifications about changes.

Maybe you are just suggesting to use the same syntax and implement all of these as filters?

FWIW, the comprehension syntax in ES6 is:

[for (x of range(0, 10)) if (x % 2 == 0)  x * x]  // array
(for (x of range(0, 10)) if (x % 2 == 0)  x * x)  // iterator with next()
rafaelw commented 10 years ago

moved to: https://github.com/Polymer/polymer-expressions/issues/11