This repository has been archived by the owner on Sep 10, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 21
/
application-state.html
163 lines (133 loc) · 5.51 KB
/
application-state.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
<!--
Copyright 2016 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<link rel="import" href="../polymer/polymer-element.html">
<link rel="import" href="action-dispatcher.html">
<link rel="import" href="state-aware.html">
<link rel="import" href="state-mutator.html">
<script type="text/javascript">
var UniFlow = window.UniFlow || {};
/**
Apply this mixin to your main application element. It provides global
state and functionality to maintain individual elements states. This mixin
is responsible for notifying all state-aware elements about their state
changes (provided those elements have `statePath` property defined).
Only one element in the application is supposed to have this mixin.
### Example:
#### HTML:
<template>
<!-- action dispatchers in the order of action processing -->
<action-dispatcher-a state="{{state}}"></action-dispatcher-a>
<action-dispatcher-b state="{{state}}"></action-dispatcher-b>
<!-- state-aware elements -->
<some-element state-path="state.someElement"></some-element>
</template>
#### JavaScript:
class MyApp extends UniFlow.ApplicationState(Polymer.Element) {
static get is() { return 'my-app'; }
connectedCallback() {
super.ConnectedCallback();
this.state = {
someElement: {}
}
}
}
customElements.define(MyApp.is,MyApp);
In the example above, `<some-element>` will receive notification of any changes to the state,
as if it was declared as follows:
<some-element state="[[state]]"></some-element>
Also, if `<some-element>` has `propertyA`, on element attach this property will be assigned
the value of `state.someElement.propertyA`, and receive all notification of the property change
whenever the corresponding data in state tree changes. This essentially translates to following
declaration:
<some-element state="[[state]]"
propertyA="[[state.someElement.propertyA]]">
</some-element>
Note that data binding is one-way in both cases. Although state-aware elements can modify their
own state, it is considered their private state and no other elements will be notified of those
changes.
@polymer
@mixinFunction
@appliesMixin UniFlow.ActionDispatcher
*/
UniFlow.ApplicationState = Polymer.dedupingMixin((base) =>
class extends UniFlow.ActionDispatcher(base) {
static get observers() {
return [
'stateChanged_(state.*)'
]
}
/**
* Application state listens to dispatch-action method and invokes
* dispatchAction method on itself (which in turn invokes dispatchAction
* on all action dispatchers declared within application element).
*
* @param {!Event} event
* @private
*/
onDispatchAction_(event) {
this.dispatchAction(event.detail);
}
/**
* Called when state.* changes. Notifies state-aware elements of their
* state changes, if applicable.
*
* @param {!Object} change the Polymer change event for the path
* @private
*/
stateChanged_(change) {
this.notifyStateAwareElements_(change);
}
/**
* Iterates through the array of state-aware elements in the application
* and notifies them about their state change, if applicable. Note that
* state-aware elements must be attached to DOM tree in order to receive
* notifications.
*
* @param change
* @private
*/
notifyStateAwareElements_(change) {
this.get('application.stateAwareElements').forEach(element => {
element.notifyPath(change.path, change.value, true);
if (element.statePath && change.path.startsWith(element.statePath)) {
let pathToNotify = change.path.slice(element.statePath.length + 1);
if (pathToNotify) {
if (element.get(pathToNotify) !== change.value &&
!change.path.endsWith('.splices')) {
element.set(pathToNotify, change.value);
} else {
element.notifyPath(pathToNotify, change.value, true);
}
} else {
// If pathToNotify empty, that means the whole element state need
// to be replaced. Iterating through the keys of new value (which
// has to be an object) and setting element properties with the same
// name.
Object.keys(change.value).forEach((key) => {
element.set(key, change.value[key]);
});
}
}
});
}
/**
* Sets application.element value to this element (so all state-aware elements
* have access to application element). Registers event listener to dispatch-action event.
*/
ready() {
super.ready();
this.set('application.element', this);
this.addEventListener('dispatch-action', (event) => this.onDispatchAction_(event));
}
});
</script>