Skip to content

Instantly share code, notes, and snippets.

@sjmiles
Last active August 29, 2015 14:27
Show Gist options
  • Save sjmiles/d5638a75eecde02ff929 to your computer and use it in GitHub Desktop.
Save sjmiles/d5638a75eecde02ff929 to your computer and use it in GitHub Desktop.

Given a host containing:

<template is="dom-repeat" items="{{obj.strings}}">

When the host observes a change, it will pass the value obj.strings to the repeat. The effect is something like:

dom-repeat.items = obj.strings;

From the repeat's perspective, the value is a simple array. The repeat has no knowledge of obj.

Why No Updates?

In the dan-demo application, the value of obj.strings is not actually changing. That is to say, even though the strings inside are changing, the value itself always references the same Array object.

To be fast, Polymer uses simple equality to dirty-check property values. Since the value of obj.strings isn't changing, that means dom-repeat.items doesn't change, so the repeat does not update.

Solution one: clone strings in _computeObject. The string data themselves are not copied, so it's not as expensive as it sounds. Now the value of items will change and the repeat will re-render.

Example:

_computeObject: function(strings, foo) {
  console.log("_computeObject was called");
  return {
    strings: strings.slice(),
    foo: foo
  };
},

Isn't Simple Equality Bad?

Normally, Polymer makes up for the simple equality check by observing specific alterations to structured objects (either via bindings, or the set or push|pop|... methods). Correctly, pk-string-input-array uses these APIs to manipulate it's strings property. However, Polymer is not aware of the relationship between strings and computedObject.strings because that equivalence is done imperatively. Iow, this code

return {
  strings: strings,
  foo: foo
};

assigns strings to computedObject.strings in a way Polymer does not see.

Solution two: directly notify Polymer about the linkage between strings and _computedObject.strings. This solution yields the best performance as Poymer can update the DOM based on specific changes.

Example:

_computeObject: function(strings, foo) {
  this.linkPaths('computedObject.strings', 'strings');`
  return {
    strings: strings,
    foo: foo
  };
},

Postscript

Other possible solutions:

  • set _computedObject to null before recomputing the value (clearing the caches)
  • observe object.strings.* in the main view and call render on the repeat

It's possible Polymer could avoid this problem in the future by ignoring the persistence of _computedObject.strings when _computedObject itself changes, although this will de-optimize some scenarios. The Polymer Core team is considering options.

@teamdandelion
Copy link

Thank you for this explanation; that makes sense that Polymer was just assigning the object subcomponents and checking them for equality directly.

Based on advice from Rob, I've since refactored my app so that rather than depending on changes from the computed property, I have the subcomponent manually fire events with the relevant changes. Hopefully by avoiding 2way bindings across element boundaries, it will become easier to reason about how the data gets used to perform updates.

I am curious to learn more about the linkPath call in solution 2; are there any tutorials or documents you can point me to that show where it is most appropriately used? Since the call allows one to make new couplings between state that could be hard to track down later, it seems like it has the potential to make the codebase hard to maintain.

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