Skip to content
This repository has been archived by the owner on Mar 13, 2018. It is now read-only.

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

Open
rafaelw opened this issue Nov 28, 2013 · 33 comments

Comments

@rafaelw
Copy link
Contributor

rafaelw commented Nov 28, 2013

Moved from googlearchive/TemplateBinding#116

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>
@dfreedm
Copy link
Contributor

dfreedm commented Feb 28, 2014

Ping! @timoxley

@warpech
Copy link

warpech commented May 12, 2014

+1

warpech added a commit to Juicy/juicy-tile-editor that referenced this issue May 14, 2014
@tomalec
Copy link

tomalec commented Jun 10, 2014

+1

2 similar comments
@alyssaq
Copy link

alyssaq commented Jul 1, 2014

+1

@erikringsmuth
Copy link

+1

@zoechi
Copy link

zoechi commented Jul 14, 2014

Update: I just found: http://www.polymer-project.org/docs/polymer/binding-types.html

When using named scopes with the repeat attribute, the index value for each item in the array is also available by using the following syntax:

<template repeat="{{ user, userIndex in users }}">
  <template repeat="{{ userFile, userFileIndex in user }}">
    {{ userIndex }}:{{ userFileIndex }}.{{ userFile }}
  </template>
</template>

Polymer.dart has an enumerable filter to get the index value. I don't know if this exists in Polymer.js as well see http://stackoverflow.com/questions/19431835 (repeat over objects won't be possible in Polymer.dart though I suppose).

@jmesserly jmesserly self-assigned this Aug 7, 2014
@jmesserly jmesserly added the p1 label Aug 14, 2014
@jethro91
Copy link

+1

@sorvell sorvell removed the p1 label Aug 28, 2014
@jmesserly jmesserly removed the p1 label Aug 28, 2014
@jmesserly jmesserly changed the title Should be able to <template repeat> on object values just like we do on array values. Should be able to <template repeat> on object values, Maps just like we do on array values. Sep 10, 2014
@jmesserly
Copy link
Contributor

edited the subject to include ES6 Map as well, as noted in googlearchive/TemplateBinding#188 ... thanks for suggesting that @jyasskin

@cayblood
Copy link

cayblood commented Oct 6, 2014

+1

2 similar comments
@jshortall
Copy link

+1

@chuckh
Copy link

chuckh commented Oct 31, 2014

+1

@egyptianbman
Copy link

+1

@timcash
Copy link

timcash commented Nov 24, 2014

They ability to give the repeater a hint about what is new like in react.js would be nice as well. For example when a list of search results gets reordered it would not need to destroy a bunch of DOM but could just swap them around by a unique key

Here is the example from react. Could this be translated to Polymer?

  render: function() {
    var results = this.props.results;
    return (
      <ol>
        {results.map(function(result) {
          return <li key={result.id}>{result.text}</li>;
        })}
      </ol>
    );
  }

When React reconciles the keyed children, it will ensure that any child with key will be reordered (instead of clobbered) or destroyed (instead of reused).

@rafaelw
Copy link
Contributor Author

rafaelw commented Nov 24, 2014

@timcash TemplateBinding already does this, except that its notion of identity is ===. IOW, if you Array.sort() an array it will retain the DOM which corresponds to each array element.

@ghost
Copy link

ghost commented Dec 6, 2014

+1

1 similar comment
@arodic
Copy link

arodic commented Dec 9, 2014

+1

@jmesserly jmesserly removed their assignment Dec 9, 2014
@jmesserly
Copy link
Contributor

fwiw, I'm not actively working on this, so unassigned. Someone eager should feel free to tackle. Happy to review a pull request for this feature.

@jmesserly
Copy link
Contributor

also happy to give pointers. I'm not super familiar with polymer-expressions but I can try and help interpret the code :)

My guess is this feature is a lot like repeat="{{item in list}}" so I'd start there, (search for "createInExpression" and you'll see scopeIdent is used to signal that), and see if there's a way to make objects pretend to be "array like". Where it gets a little funny, is we might want to make a virtual array so TemplateBinding can repeat over it, where the "index" is the map key. Then you'd have to be able to observe the original object, and signal a change so template repeat will refresh.

Overall it's probably very few lines of code, but figuring out where to put those lines will be the trick

@delebash
Copy link

+1

@arodic
Copy link

arodic commented Dec 18, 2014

@jmesserly I tried looking into polymer-expressions but I'm afraid I'm completely unfamiliar with Polymer source and I couldn't make any progress with it. However, I managed to make a custom filter (hack) that did the trick.

PolymerExpressions.prototype.observedObjectToArray = function(object) {
  var array = [];
  if (_.isObject(object)) {
    array.push.apply(array, _.map(object, function(value, key) {
      return {'value': value, 'key': key};
    }));
    var observer = new ObjectObserver(object);
    observer.open(function(added, removed, changed, getOldValueFn) {
      _.each(added, function(value, key) {
        array.push({'value': value, 'key': key});
      });
      _.each(removed, function(value, key) {
        _.remove(array, {'key': key});
      });
      // TODO: observe change
    }, this);
  }
  return array;
};

With this filter I was then able to:

<template repeat="{{item, i in someObject | observedObjectToArray}}">
  {{i}}, {{item.key}}, {{item.value}}
</template>

Once I had this working, I was able to remove a lot of code from our code base. It made me realize how much complexity we generated due to inability to iterate on object properties in Polymer. If anyone can find time to implement object iteration in template, it would make a lot of people very happy.

@Sherlock92
Copy link

+1 please god

@ryantheleach
Copy link

Does adding +1 's really make a difference?

But anyway, +1 from me.

@evtaranov
Copy link

+1 vote

@moderndeveloperllc
Copy link

👍

1 similar comment
@lgersman
Copy link

👍

@manustays
Copy link

I solved the problem by using a small custom filter:

filterKeys: function(object)
{
    return Object.keys(object);
}

Then use it in the template like this:

<template  repeat="{{key, index in someObject | filterKeys}}">
    {{ index }}. {{ key }} = {{ someObject[key] }}
</template>

@proteneer
Copy link

+1

@7nights
Copy link

7nights commented Mar 26, 2015

@manustays Does it still work after you deleted or modified a property of someObject? For me, unless I set someObject to another object, modifying its property won't update the UI.

@alecbz
Copy link

alecbz commented Apr 3, 2015

+1

@arodic
Copy link

arodic commented Apr 3, 2015

We have +21 so far. @jmesserly @pflannery what's the status of PR #211? Looks like it's ready to merge. No?

@jmesserly
Copy link
Contributor

We have +21 so far. @jmesserly @pflannery what's the status of PR #211? Looks like it's ready to merge. No?

Regarding googlearchive/TemplateBinding#211. If the fix was to polymer-expressions, I'd feel okay merging it.

I don't personally feel comfortable merging a new feature into TemplateBinding. This bug was moved to polymer-expressions, by the author of TemplateBinding. It's a lower level part of the system, and @rafaelw took care to keep it very minimal. polymer-expressions is where the higher level APIs live.

Maybe someone on the Polymer team could comment if they think adding new features to TemplateBinding is okay (@kevinpschaaf maybe? I know you've done data binding stuff). If it is, then we're perhaps close to merge. The code changes looked okay (other than the Object.prototype.toString.call(value) === '[object Object]'), but I worried more about whether the fix was in the right repository.

@pflannery
Copy link
Contributor

@jmesserly

Object.prototype.toString.call(value) === '[object Object]'

This ensures the value is an Object (dictionary) and not a Date, String, Number or an Array. If someone wants to provide a better way of determining this then I can rectify but this works well otherwise.

I'm not convinced polymer-expressions is the place to do this as we need to be able to determine if the value is to be repeated and if it's an Array or an Object (dictionary). When it's an Object (dictionary) it's converted to an array and observed for changes and when a change occurs it updates the array that the repeat is bound to.

@kevinpschaaf do you think this syntax is to be carried over to 0.8? from what I see it looks like its being dropped and replaced with x-repeat?

@kevinpschaaf
Copy link

We don't want to introduce additional risk in to the 0.5 codebase, as it will be transitioning to maintenance mode soon. This is good feedback though, and the intent is to support a flavor of this in the 0.8+ codebase.

Issue for that opened here:
Polymer/polymer#1385

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

No branches or pull requests