I'm trying to sync some of my web component properties between instances of the same element so if one of this properties changes then the same property gets updated in all the instances with the corresponding binding and events.
Note: I want to use the Polymer Data System Concepts for the communications between instances.
Example
my-element.html
<dom-module id="my-element">
<script>
Polymer({
is: 'my-element',
properties: {
myProp: {
type: String,
notify: true
}
});
</script>
</dom-module>
my-other-element.html
<dom-module id="my-other-element">
<template>
<my-element my-prop="{{otherProp}}"></my-element>
</template>
<script>
Polymer({
is: 'my-other-element',
properties: {
otherProp: {
type: String,
notify: true,
readOnly: true
}
}
})
</script>
</dom-module>
my-app.html
<dom-module id="my-app">
<template>
<my-element id="element"></my-element>
<my-other-element id="otherElement"
on-other-prop-changed="onPropChanged"
></my-other-element>
</template>
<script>
Polymer({
is: 'my-app',
attached: function () {
// should set 'myProp' to 'test' and trigger
// the event 'my-prop-changed' in all my-element instances
this.$.element.myProp = 'test'
},
onPropChanged: function (ev, detail) {
console.log(detail.value); // should print 'test'
console.log(this.$.element.myProp); // should print 'test'
console.log(this.$.otherElement.otherProp); // should print 'test'
}
});
</script>
</dom-module>
PD: Would be good to use standard like patterns and good practices.
My personal opinion on problems like this is to use flux architecture.
you create a wrapper Element which is distributing all the information to the children. All changes a going via the main component.
the
component-x
is firing an change value event onapp-wrapper
and theapp-wrapper
is updatingsomeValue
, note it's a one-way-binding.There is a component for this, which is implementing the
redux
architecture, but its also possible to code your own. It's more or less the observer patternWe have created a component to synchronize data among different instances. Our component is:
And we use it in a component like this:
And in another component like this:
In this way we get the native behavior of polymer for events and bindings
Try this for my-app.html. I don't see any reason to not use two-way bindings here.
Although it's probably a better practice to give
myProp
a default value by using theproperties
object rather than theready
callback. Example:tl;dr
I have created a custom behaviour that syncs all elements' properties that have
notify: true
. Working prototype: JSBin.Currently, this prototype does not distinguish between different kinds of elements, meaning that it can only sync instances of the same custom element - but this can be changed without much effort.
You could also tailor the behaviour so that is syncs only the desired properties and not just all with
notify: true
. However, if you take this path, be advised that all the properties you want to sync must havenotify: true
, since the behaviour listens to the<property-name>-changed
event, which is fired only if the property hasnotify: true
.The details
Let's start with the custom
SyncBehavior
behaviour:Notice that I have used the IIFE pattern to hide the variable that holds all instances of the custom element
my-element
. This is essential, so don't change it.As you can see, the behaviour consists of six functions, namely:
attached
, which adds the current instance to the list of instances and registers listeners for all properties withnotify: true
.detached
, which removes the current instance from the list of instances and removes listeners for all properties withnotify: true
._eventHandlerForPropertyType
, which returns the name of one of the functions 4-6, depending on the property type.__syncArray
, which syncs the Array type properties between the instances. Notice that I ignore the current target and implement a simple locking mechanism in order to avoid cycles. The method handles two scenarios: assigning a new Array, and mutating an existing Array.__syncObject
, which syncs the Object type properties between the instances. Notice that I ignore the current target and implement a simple locking mechanism in order to avoid cycles. The method handles two scenarios: assigning a new Object, and changing a property of an existing Object.__syncPrimitive
, which syncs the primitive values of properties between the instances. Notice that I ignore the current target in order to avoid cycles.In order to test-drive my new behaviour, I have created a sample custom element:
It has one String property, one Array property, and one Object property; all with
notify: true
. The custom element also implements theSyncBehavior
behaviour.To combine all of the above in a working prototype, you simply do this:
In this prototype, I have created four instances of
my-element
. One haspropString
bound to an input, while the others don't have any bindings at all. I have created a simple form, that covers every scenario I could think of:EDIT
I have updated my post and the prototype in order to address the following issues: