Skip to content
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

Polymer.create() - binding issues #2131

Closed
hawkett opened this issue Jul 22, 2015 · 13 comments
Closed

Polymer.create() - binding issues #2131

hawkett opened this issue Jul 22, 2015 · 13 comments

Comments

@hawkett
Copy link

hawkett commented Jul 22, 2015

I have a fairly large single page app, and consequently only render elements to the DOM if the user navigates to them.

I'm using Polymer.create() to achieve this, however I'm having issues setting up bindings using this method. Here is a jsbin demonstrating the approach and issue. It gives the following console output:

runner-3.34.1.min.js:1 test-elem Ready!
runner-3.34.1.min.js:1 list-elem Ready!
runner-3.34.1.min.js:1 [dom-repeat::dom-repeat]: expected array for `items`, found {{otherThings}}e.(anonymous function) @ runner-3.34.1.min.js:1Polymer._itemsChanged @ polymer.html:3352Polymer.Base.extend._complexObserverEffect @ polymer.html:1282(anonymous function) @ polymer.html:1131Polymer.Bind._modelApi._effectEffects @ polymer.html:1128Polymer.Bind._modelApi._propertySet @ polymer.html:1122setter @ polymer.html:1189Polymer.Base._addFeature._applyEffectValue @ polymer.html:1531Polymer.Base.extend._annotationEffect @ polymer.html:1257(anonymous function) @ polymer.html:1131Polymer.Bind._modelApi._effectEffects @ polymer.html:1128Polymer.Bind._modelApi._propertySet @ polymer.html:1122setter @ polymer.html:1189Polymer.Base._addFeature.create @ polymer.html:1086Polymer.ready @ runner:12Polymer.Base._addFeature._invokeBehavior @ polymer-micro.html:275Polymer.Base._addFeature._doBehavior @ polymer-micro.html:270Polymer.Base._addFeature._readySelf @ polymer-mini.html:76Polymer.Base._addFeature._ready @ polymer-mini.html:63Polymer.Base._addFeature._tryReady @ polymer-mini.html:52Polymer.Base._addFeature._initFeatures @ polymer.html:2906Polymer.Base.createdCallback @ polymer-micro.html:106
runner-3.34.1.min.js:1 error TypeError: Cannot read property 'getKey' of undefined
    at dom-repeat.Polymer._sortAndFilter (http://milestech.net/components/polymer/polymer.html:3434:25)
    at dom-repeat.Polymer._render (http://milestech.net/components/polymer/polymer.html:3402:6)
    at dom-repeat.Polymer.Templatizer._flushTemplates (http://milestech.net/components/polymer/polymer.html:3016:8)
    at Object.Polymer.Debounce.Debouncer.complete (http://milestech.net/components/polymer/polymer.html:960:15)
    at MutationObserver.atEndOfMicrotask (http://milestech.net/components/polymer/polymer.html:924:1)
polymer.html:3434 Uncaught TypeError: Cannot read property 'getKey' of undefinedPolymer._sortAndFilter @ polymer.html:3434Polymer._render @ polymer.html:3402Polymer.Templatizer._flushTemplates @ polymer.html:3016Polymer.Debounce.Debouncer.complete @ polymer.html:960atEndOfMicrotask @ polymer.html:924

The meat of the problem is with: this.create('list-elem', {things: '{{otherThings}}'});, where otherThings is a property of the parent element.

At the moment the only workaround I have is to set up observers in the parent element and explicitly set the values in the child elements, which is quite clunky.

Am I missing something, or is it only possible to bind correctly by rendering everything into the DOM at the same time? If that is the case, is it possible to add support for this in Polymer.create()?

@zerodevx
Copy link

You are essentially data-binding imperatively, which unfortunately is not currently supported in Polymer 1.0.

Quoting @kevinpschaaf ,

No, we don't currently support this, outside of dom-bind, which is the only template implementation that late-binds instance children. You can document.createElement('template', 'dom-bind'), then you can dynamically append children with binding annotations to its content, and the bindings will only be evaluated once the dom-bind is attached to the document. See tests here that show this usage of it: https://github.com/Polymer/polymer/blob/master/test/unit/dom-bind.html#L95

Note that dom-bind does not currently allow binding to outer scope, so it has limited use in custom element templates (it's main use case is for binding between elements in the main document), and that's not likely to change short-term.

We are achieving a lot of performance optimization by baking the binding connections into the prototype at registration time for an element (rather than at instance time), and we haven't built up enough of the machinery to easily allow runtime addition/removal of bindings.

I did find a workaround that allows imperative bindings but it involves defining a wrapper element through javascript and manually invoking the createdCallback() to force a late registration. I think it's a bit hackish and might be an anti-pattern, so I'll suggest waiting for the project owners to actually release a proper API/pattern for that.

That being said, let me know if you're interested the stop-gap workaround and I'll publish the gist.

@hawkett
Copy link
Author

hawkett commented Jul 24, 2015

@zerodevx - would be great to see the gist - thx!

@zerodevx
Copy link

My workaround, though it does allow creating <my-element data="{{data}}"> through javascript, does not truly create bindings on the fly. My use case might be pretty specific, so your mileage might vary.

In my case, I needed to place an unknown custom element inside a dom-repeat template in my main element. I know exactly how my bindings will be wired beforehand; however, I will only know which element to repeat during runtime.

So the idea is to declaratively bind into an un-upgraded "placeholder" tag, and then imperatively upgrade that tag to act as a proxy between my target (the unknown element) and my main element.

For example,

<dom-module id="main-element">
  <template>
    ...
    <template is="dom-repeat" items="{{myList}}">
      <!-- this tag does nothing for now -->
      <dummy-tag foo$="{{item}}"></dummy-tag>
    </template>
    ...
  </template>
  <script>
    Polymer({
      is: "main-element",
      ...
      upgradeElement: function () {
        // now that I know my target element is called `foo-bar`
        Polymer.importHref("foo-bar.html", function () {
          tplstr = '<foo-bar myData="{{foo}}"></foo-bar>';
          proto = {
            is: "dummy-tag",
            properties: {
              foo: { type: Object, value: function () { return {}; } }
            }
          };
          var dm = document.createElement("dom-module");
          dm.id = "dummy-tag";
          var temp = document.implementation.createHTMLDocument("temp");
          temp.body.innerHTML = tplstr;
          var tpl = document.createElement("template");
          tpl.content.appendChild(temp.body.firstChild);
          dm.appendChild(tpl);
          dm.createdCallback();
          Polymer(proto);
        });
      },
      ...
    });
  </script>
</dom-module>

As you can see, I'm still adhering to what @kevinpschaaf explained - that bindings are set during registration time. I won't recommend this pattern since in most cases (where you absolutely need to data-bind imperatively), it is much more trivial to simply wire up observers to propagate changes to; listeners to receive changes from; the target element that you've imperatively inserted into light DOM.

@WoodyWoodsta
Copy link

Our company also has a large (large) single page style app and cannot do without dynamically managing element creation, or else the creation time is simply unacceptable. This is becoming almost impossible without being able to late-bind as part of pushing and pulling elements in and out of the DOM.

I see that #1796 has p3 whereas this issue has p2. Could we have some sort of insight as to where in the dev team's sights this functionality actually is, if even in the pipeline?

Being able to spread creation overhead smartly across the lifetime of the app is arguably a must when apps become large-scale (like, larger than the PSK 😁).

@WoodyWoodsta
Copy link

I don't think the observer propagation approach is acceptably efficient when you want two way binding.

@TimvdLippe
Copy link
Contributor

@WoodyWoodsta FWIW I solved this problem with a combination of iron-pages with dom-if. The dom-if also checks the route with a computed binding. This way, the elements are only created when the route/page matches. Maybe this would be a feasible solution?

@WoodyWoodsta
Copy link

@TimvdLippe We have that tactic employed for page level creation, and in fact have not come across the need to bind across the dom-if yet, so can't comment on that.

Within some of the pages, there are elements with local DOM that is populated with a varying type of content depending on the context, sort-of like a contextual UI builder. The builders create the resultant DOM by matching context against a json template config, and then use a bunch of this.create() and Polymer.dom().appendChild() calls to build the tree. We need binding on the newly created elements, within the builder's DOM (binding to outside of the builder could be done through the builder element itself).

@mkazlauskas
Copy link

I've implemented a generic workaround that updates template on element's registration. See https://github.com/firmfirm/f-di, it may help some of you.

@kaste
Copy link
Contributor

kaste commented Mar 4, 2016

Can you take a look at #3456 (and its related PR #3460) and perhaps comment if that would fix your issue.

@safizn
Copy link

safizn commented Aug 11, 2016

Starting to loose hope in the Polymer project. So many months passed and javascript binding is still not implemented !

@AliMD
Copy link
Contributor

AliMD commented Aug 18, 2016

I hope this code useful: #1778 (comment)

@tjsavage tjsavage added the 1.x label Sep 8, 2016
@nicholaswmin
Copy link

/sub

@TimvdLippe
Copy link
Contributor

Closing per #3460 (comment) Runtime databinding API shipped in Polymer 2.

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

Successfully merging a pull request may close this issue.