Search code examples
javascriptpolymerpolymer-1.0web-component

Data is lost when passing it down more than two components


I've got three Polymer-components (Polymer 1.2). They all sit in their own files therefore is-logged-in and login-name have to be passed from one component to another.

I put them together here so you can understand my problem more easily:

<component1 is-logged-in="true" login-name="Cool Cat">
  <component2 is-logged-in="{{isLoggedIn}}" login-name="{{loginName}}">
    <component3 is-logged-in="{{isLoggedIn}}" login-name="{{loginName}}"></component3>
  </component2>
</component1>

All 3 components have these properties:

properties: {
  isLoggedIn: {
    type: Boolean,
    value: false
  },
  loginName: {
    type: String,
    value: ""
  }
}

document.querySelector("component2").loginName is Cool Cat but document.querySelector("component3").loginName is just an empty string.

When checking the DOM is-logged-in and login-name don't appear anymore starting at <component2>

How can I pass the data on to component3 ?


Solution

  • All {{ }} bindings have to live in template, and it's the template which identifies the scope of the values. Other parent-child relationships do not define scope.

    In your example, all the component-1/2/3 are in the same template, and therefore in the same scope. Setting properties of component-1 has no effect on component-2 and component-3, they are not bound together.

    In other words, the {{isLoggedIn}} and {{loginName}} macros are binding to properties in the scope identified by the containing template (the scope is usually an element, but can also be a dom-repeat or other specialized template).

    I don't expect this is actually want you want, but for clarity, something like this would work:

    <dom-module id="component-0">
      <template>
        <component-1 is-logged-in="{{isLoggedIn}}" login-name="{{loginName}}">
          <component-2 is-logged-in="{{isLoggedIn}}" login-name="{{loginName}}">
            <component-3 is-logged-in="{{isLoggedIn}}" login-name="{{loginName}}"></component-3>
          </component-2>
        </component-1>
    ...
    <script>
      Polymer({
        is: 'component-0',
        properties {
          isLoggedIn: {value: true},
          loginName: {value: "cool-cat"}
        }
    </script>
    

    All the {{ }} bindings are in the component-0 scope, so setting the values in that scope sets the values to all the bindings.

    Fwiw, you will probably also have an easier time if you aggregate the shared data into an object.

        <component-1 login="{{login}}">
          <component-2 login="{{login}}">
            <component-3 login="{{login}}"></component-3>
    

    Where e.g. login = {isLoggedIn: true, loginName: "cool-cat"}.

    The idea that the data must be passed from one component to another is not true in this construction. If your goal is just to get the data to component-3, you can bind the data directly and ignore 1 and 2.

    The only time data must be passed from one component to another is when crossing scopes (a scope defines a boundary for data, so hopefully this make sense).