12

I'm working on a Polymer app, which is pulling data from a RESTful API and using it to construct the interface. A specific area I'm stuck on conceptually is the implementation of the Monostate Pattern described at http://www.polymer-project.org/docs/polymer/polymer.html#global. Effectively, I can add declarative attributes into a new component, app-globals, and then access this reasonably straightforwardly.

Here's the key question: if I'm pulling (and, potentially, resubmitting) data back and forth via core-ajax to the API within the app-globals component, how do I ensure that all consumers of the app-globals component have the same data? Seems like I've lost my monostatism if I use the suggested pattern:

<polymer-element name="my-component">
  <template>
    <app-globals id="globals"></app-globals>
    <div id="firstname"></div>
    <div id="lastname"></div>
  </template>
  <script>
    Polymer('my-component', {
      ready: function() { this.globals = this.$.globals; }
     });
  </script>
</polymer-element>    

because each of the components that consume app-globals will be pulling their own version of the API data. Am I missing something? Is there another way to ensure that the app constantly has only one version of the truth?

Greg Johnson
  • 377
  • 1
  • 4
  • 13
  • Can you provide a better link to the specific portion of the documentation relevant to the question? – Pointy Jul 21 '14 at 14:41
  • Yes - editing to reflect: http://www.polymer-project.org/docs/polymer/polymer.html#global – Greg Johnson Jul 21 '14 at 14:43
  • Thanks. The documentation is kind-of thin, but it looks like the point is that the closure around the "app-globals" script only runs once, when the "app-globals" component is defined. Thereafter, separate instances can do whatever they want in the "ready" handler. The second example, with the attributes, seems weird because each *use* of `` could change the shared state! – Pointy Jul 21 '14 at 14:53
  • Thanks, @Pointy. I agree - the documentation's a bit thin. It looks like the only real solve here is to create one instance of globals and and a series of getter/setter APIs that ensure uniqueness. Will keep fingers crossed that there's a more elegant solve here... – Greg Johnson Jul 21 '14 at 16:37
  • It's not true that `each of the components that consume app-globals will be pulling their own version of the API data`. There is only one source of truth. I don't think there is a real problem here, if you still believe there is, please provide an example. – Scott Miles Jul 21 '14 at 18:18
  • @ScottMiles, thanks for the reply. Still at the design stage, and trying to wrap my head around the implications of the shadow DOM and independence. To play this back: if I instantiate app-globals within a range of components each will be, effectively, shared instances? – Greg Johnson Jul 21 '14 at 19:44

2 Answers2

12

Each time you reference app-globals in a custom component, a new instance of app-globals is created. But each of these instances can share a number of properties (think "static" vars in Java or "class" vars in ObjC / Swift).

The Script within app-globals element (or indeed any Polymer element) runs only once - think of it as running when the component definition is loaded. But the Polymer function within that script declares a configuration object, with properties and lifecycle event-handlers, that will be created separately for each instance. The app-globals script in the document you reference (copied below UPDATE: this version is modified as described later) uses an anonymous closure (a function that is run immediately), containing both the shared variables and the Polymer function declaring the config object which in turn references the shared variables. So each instance of that config object - and in turn each instance of app-globals - will reference the same set of shared variables.

 <polymer-element name="app-globals">
  <script>
  (function() {
    var data = {
      firstName: 'John',
      lastName: 'Smith'
    }

    Polymer('app-globals', {
       ready: function() {
         this.data = data;
       }
    });
  })();
  </script>
</polymer-element>

If one custom component instance changes a value on its app-globals instance (or they are changed internally, as the results of an AJAX call in your case) all other component instances with a reference to app-globals will see the changed value.

UPDATE: The original, as copied from:

http://www.polymer-project.org/docs/polymer/polymer.html#global

had a deficiency, as described by @zreptil, if you change the data values, the new values are NOT available to all other instances - because the instance variables are just copies of the referenced strings. By using an object with data properties, as in the edited version above, and only ever reading from and assigning to the data properties of that object rather than overwriting the object itself, changed values are shareable between instances.

Tim Stewart
  • 759
  • 6
  • 8
  • 2
    Unfortunately this doesn't work as expected. The variables are global accessible, but the values in the instances dont change when a variable is changed. But i could be wrong. Could anybody point out, how i would change the variable firstName from 'John' to 'Mary' in the above example? The change should be done when a button is clicked. And every Element on the page that uses this variable should immediatly show Mary instead of John. – zreptil Aug 29 '14 at 12:05
  • 1
    Thanks zreptil, I copied the example from the Google docs, turns out it doesn't work for genuine sharing any changes in the data, I've fixed it up as shown above so it works. – Tim Stewart Sep 17 '14 at 12:40
  • exist a diffence with polymer 1.0? – ljofre Jul 01 '15 at 02:09
5

@Zreptil has asked for an example above, and since I had to experiment with this anyway I built one based on Tim Stewart's answer and the Polymer documentation.

Complete Example: http://jsbin.com/figizaxihe/1/edit?html,output

I had some troubles with dashes in the id (id="global-variable") so I also added an example for that.

The element definition

<polymer-element name="app-globals">
<script>
  (function() {
    var data = {
      firstName: 'John'
    }

    Polymer({
       ready: function() {
         this.data = data;
       }
    });
  })();

</script>
</polymer-element>

Use it inside a polymer element

<polymer-element name="output-element" noscript>
<template>
    <app-globals id="globalvars"></app-globals>
    <h4>Output-Element</h4>
    <div>First Name: {{$.globalvars.data.firstName}}</div>
</template>
</polymer-element>

Use it outside a polymer element

<template is="auto-binding">
    <app-globals id="topglobals"></app-globals>
    <h3>First Name in Title: {{$.topglobals.data.firstName}}</h3>
</template>

Pay attention with dashes/hyphens

  <h3>and since that took me a while to realize - dashes are not directly supported...</h3>
<template is="auto-binding">
    <!-- can use different id's -->
    <app-globals id="global-variables"></app-globals>
    <div>This does not work: {{$.global-variables.data.firstName}}</div>
    <div>Correct syntax: {{$['global-variables'].data.firstName}}</div>

</template>

Polymer 1.0

See Polymer 1.0 Global Variables for a polymer 1.0 solution if interested.

Community
  • 1
  • 1
ootwch
  • 930
  • 11
  • 32