window
, and we can put our state here. This works great as a namespace until you choose something that should be unique to your application (using “googleMaps” for your own library might not be a good idea). So why not use this method? Well, there is a synchronization problem - if we change the value, then how does someone somewhere else find out that it has changed? We definitely do not want to poll the status source by timer, it would be somewhat cumbersome to issue / subscribe to too many events.app-drawer-layout
from the Polymer Starter Kit (highlighted in red): <link rel="import" href="../bower_components/polymer/polymer-element.html"> <link rel="import" href="../bower_components/paper-checkbox/paper-checkbox.html"> <dom-module id="my-options"> <template> <style> :host { display: block; padding: 16px; } h3, p { margin: 8px 0; } </style> <h3>Options</h3> <p> <paper-checkbox checked="{{ options.subscribe }}">Send Notifications</paper-checkbox> </p> </template> <script> class MyOptions extends Polymer.Element { static get is() { return 'my-options'; } static get properties() { return { options: { type: Object, value: () => ({ subscribe: false }) } } } } window.customElements.define(MyOptions.is, MyOptions); </script> </dom-module>
options
property available from another place so that if someone needs access to its child property subscribe
(or any other we add), this someone could get it and it would be updated with any changes but we don’t want everything to be open - we want to control access to it. (function() { // existing code }());
MyOptions
class is MyOptions
from the outside world (this is not important, since the only thing important for us is that it calls window.customElements.define
).MyOptions
, and other elements will need access to it, so we will add a variable that refers to it and set its correct value in the constructor when the element is created: let optionsInstance = null; // in class definition: constructor() { super(); if (!optionsInstance) optionsInstance = this; }
optionsInstance
is still hidden inside our IIFE, but now so that we don’t put it in it, it will have access to the initialized MyOptions
instance. // in properties: subscribers: { type: Array, value: () => [] } // in class definition: register(subscriber) { this.subscribers.push(subscriber); subscriber.options = this.options; subscriber.notifyPath('options'); } unregister(subscriber) { var i = this.subscribers.indexOf(subscriber); if (i > -1) this.subscribers.splice(i, 1) }
options
object. Here we also encounter Polymer change detection — setting the property itself does not notify the subscriber that this has happened, so we need a notifyPath
call. We also want to notify all subscribers whenever any properties of the options
object change (for example, if 'subscribe' was called, not just when the object reference changes) and we use an observer with an asterisk to say that we are interested. ” all changes ”: static get observers() { return [ 'optionsChanged(options.*)' ] } optionsChanged(change) { for(var i = 0; i < this.subscribers.length; i++) { this.subscribers[i].notifyPath(change.path); } }
notifyPath
for each .optionsInstance
): class MyOptionsValue extends Polymer.Element { static get is() { return 'my-options-value'; } static get properties() { return { options: { type: Object, notify: true } } } connectedCallback() { super.connectedCallback(); optionsInstance.register(this); } disconnectedCallback() { super.disconnectedCallback(); optionsInstance.unregister(this); } } window.customElements.define(MyOptionsValue.is, MyOptionsValue);
<link rel="import" href="my-options.html">
<my-options-value options="{{ options }}"></my-options-value> <p>Send notifications option is: <b>[[ options.subscribe ]]</b></p>
notify: true
in the declaration of the properties of the element due to the bidirectional binding (child-to-parent), indicated by curly braces. The MyOptions
instance informs the instance (or instances) of MyOptionsValue
about the change, and they, in turn, need to notify the element in which they are located. class MyView extends Polymer.Element { static get is() { return 'my-view'; } static get properties() { return { options: { type: Object } } } }
class MyView extends MyOptionsMixin(Polymer.Element) { static get is() { return 'my-view'; } }
my-options.html
, but our view-element is simpler and does not require an intermediate access element: <p>Send notifications option is: <b>[[ options.subscribe ]]</b></p>
<link rel="import" href="../bower_components/polymer/polymer-element.html"> <link rel="import" href="../bower_components/paper-checkbox/paper-checkbox.html"> <dom-module id="my-options"> <template> <style> :host { display: block; padding: 16px; } h3, p { margin: 8px 0; } </style> <h3>Options</h3> <p> <paper-checkbox checked="{{ options.subscribe }}">Send Notifications</paper-checkbox> </p> </template> <script> (function() { let optionsInstance = null; class MyOptions extends Polymer.Element { static get is() { return 'my-options'; } static get properties() { return { options: { type: Object, value: () => ({ subscribe: false }) }, subscribers: { type: Array, value: () => [] } } } static get observers() { return [ 'optionsChanged(options.*)' ] } constructor() { super(); if (!optionsInstance) optionsInstance = this; } register(subscriber) { this.subscribers.push(subscriber); subscriber.options = this.options; subscriber.notifyPath('options'); } unregister(subscriber) { var i = this.subscribers.indexOf(subscriber); if (i > -1) this.subscribers.splice(i, 1) } optionsChanged(change) { for(var i = 0; i < this.subscribers.length; i++) { this.subscribers[i].notifyPath(change.path); } } } window.customElements.define(MyOptions.is, MyOptions); MyOptionsMixin = (superClass) => { return class extends superClass { static get properties() { return { options: { type: Object } } } connectedCallback() { super.connectedCallback(); optionsInstance.register(this); } disconnectedCallback() { super.disconnectedCallback(); optionsInstance.unregister(this); } } } }()); </script> </dom-module>
<link rel="import" href="../bower_components/polymer/polymer-element.html"> <link rel="import" href="my-options.html"> <link rel="import" href="shared-styles.html"> <dom-module id="my-view2"> <template> <style include="shared-styles"> :host { display: block; padding: 10px; } </style> <div class="card"> <div class="circle">2</div> <h1>View Two</h1> <p>Ea duis bonorum nec, falli paulo aliquid ei eum.</p> <p>Id nam odio natum malorum, tibique copiosae expetenda mel ea.Detracto suavitate repudiandae no eum. Id adhuc minim soluta nam.Id nam odio natum malorum, tibique copiosae expetenda mel ea.</p> <p>Send notifications option is: <b>[[ options.subscribe ]]</b></p> </div> </template> <script> class MyView2 extends MyOptionsMixin(Polymer.Element) { static get is() { return 'my-view2'; } } window.customElements.define(MyView2.is, MyView2); </script> </dom-module>
MyOptionsMixin
defined inside IIFE, it is actually in the scope of the window
, so other elements outside of IIFE can see and use it (if we wrote var MyOptionsMixin…
then it would did not work, it would be visible only inside IIFE). I should have used window.MyOptionsMixin
to make it clearer, or, as is more common, to use the global namespace (child object in the window
) as well as Polymer does. You may already have it - they are useful for storing configuration properties . The safe way to check and add something to it looks like this: MyApp = windows.MyApp || { } MyApp.MyOptionsMixin = ...
MyApp.MyOptionsMixin
, referring to it).Source: https://habr.com/ru/post/345808/
All Articles