ONOS-2325 - GUI -- Rewrite / bug fix for table row flashing. Multi-rows act like…
… a unit and row flashes when a single new element is added. Change-Id: I7be876281c0c86b927366223fc87372ea21034ec
Showing
15 changed files
with
81 additions
and
55 deletions
... | @@ -15,7 +15,7 @@ | ... | @@ -15,7 +15,7 @@ |
15 | */ | 15 | */ |
16 | 16 | ||
17 | /* | 17 | /* |
18 | - ONOS GUI -- Our own Angular directives defined here. | 18 | + ONOS GUI -- General Purpose Angular directives defined here. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | (function () { | 21 | (function () { |
... | @@ -59,5 +59,14 @@ | ... | @@ -59,5 +59,14 @@ |
59 | }); | 59 | }); |
60 | } | 60 | } |
61 | }; | 61 | }; |
62 | + }]) | ||
63 | + | ||
64 | + .directive('ngRepeatComplete', [function () { | ||
65 | + return function (scope) { | ||
66 | + if (scope.$last) { | ||
67 | + scope.$emit('ngRepeatComplete'); | ||
68 | + scope.$broadcast('ngRepeatComplete'); | ||
69 | + } | ||
70 | + }; | ||
62 | }]); | 71 | }]); |
63 | }()); | 72 | }()); | ... | ... |
... | @@ -189,8 +189,9 @@ | ... | @@ -189,8 +189,9 @@ |
189 | // does NOT use strict object reference equality, | 189 | // does NOT use strict object reference equality, |
190 | // instead checks each property individually for equality | 190 | // instead checks each property individually for equality |
191 | function containsObj(arr, obj) { | 191 | function containsObj(arr, obj) { |
192 | - var i; | 192 | + var i, |
193 | - for (i = 0; i < arr.length; i++) { | 193 | + len = arr.length; |
194 | + for (i = 0; i < len; i++) { | ||
194 | if (sameObjProps(arr[i], obj)) { | 195 | if (sameObjProps(arr[i], obj)) { |
195 | return true; | 196 | return true; |
196 | } | 197 | } | ... | ... |
... | @@ -217,37 +217,49 @@ | ... | @@ -217,37 +217,49 @@ |
217 | function ($log, $parse, $timeout, fs) { | 217 | function ($log, $parse, $timeout, fs) { |
218 | 218 | ||
219 | return function (scope, element, attrs) { | 219 | return function (scope, element, attrs) { |
220 | - var rowData = $parse(attrs.row)(scope), | 220 | + var idProp = attrs.idProp, |
221 | - id = attrs.rowId, | 221 | + table = d3.select(element[0]), |
222 | - tr = d3.select(element[0]), | 222 | + trs, promise; |
223 | - multi = 'multiRow' in attrs, | ||
224 | - promise; | ||
225 | - | ||
226 | - scope.$watchCollection('changedData', function (newData) { | ||
227 | - var multiRows = null; | ||
228 | - if (multi) { | ||
229 | - // This is a way to identify which rows need to be | ||
230 | - // highlighted with the first one. It uses the unique | ||
231 | - // identifier as a class selection. If the unique ID | ||
232 | - // has invalid characters (like ':') then it won't work. | ||
233 | - multiRows = d3.selectAll('.multi-row-' + rowData[id]); | ||
234 | - } | ||
235 | 223 | ||
224 | + function highlightRows() { | ||
225 | + var changedRows = []; | ||
236 | function classRows(b) { | 226 | function classRows(b) { |
237 | - tr.classed('data-change', b); | 227 | + if (changedRows.length) { |
238 | - if (multiRows) { | 228 | + angular.forEach(changedRows, function (tr) { |
239 | - multiRows.classed('data-change', b); | 229 | + tr.classed('data-change', b); |
230 | + }); | ||
240 | } | 231 | } |
241 | } | 232 | } |
233 | + // timeout because 'row-id' was the un-interpolated value | ||
234 | + // "{{link.one}}" for example, instead of link.one evaluated | ||
235 | + // timeout executes on the next digest -- after evaluation | ||
236 | + $timeout(function () { | ||
237 | + if (scope.tableData.length) { | ||
238 | + trs = table.selectAll('tr'); | ||
239 | + } | ||
242 | 240 | ||
243 | - if (fs.find(rowData[id], newData, id) > -1) { | 241 | + if (trs && !trs.empty()) { |
244 | - classRows(true); | 242 | + trs.each(function () { |
243 | + var tr = d3.select(this); | ||
244 | + if (fs.find(tr.attr('row-id'), | ||
245 | + scope.changedData, | ||
246 | + idProp) > -1) { | ||
247 | + changedRows.push(tr); | ||
248 | + } | ||
249 | + }); | ||
250 | + classRows(true); | ||
251 | + promise = $timeout(function () { | ||
252 | + classRows(false); | ||
253 | + }, flashTime); | ||
254 | + trs = undefined; | ||
255 | + } | ||
256 | + }); | ||
257 | + } | ||
245 | 258 | ||
246 | - promise = $timeout(function () { | 259 | + // new items added: |
247 | - classRows(false); | 260 | + scope.$on('ngRepeatComplete', highlightRows); |
248 | - }, flashTime); | 261 | + // items changed in existing set: |
249 | - } | 262 | + scope.$watchCollection('changedData', highlightRows); |
250 | - }); | ||
251 | 263 | ||
252 | scope.$on('$destroy', function () { | 264 | scope.$on('$destroy', function () { |
253 | if (promise) { | 265 | if (promise) { | ... | ... |
... | @@ -60,7 +60,6 @@ | ... | @@ -60,7 +60,6 @@ |
60 | function respCb(data) { | 60 | function respCb(data) { |
61 | o.scope.tableData = data[root]; | 61 | o.scope.tableData = data[root]; |
62 | onResp && onResp(); | 62 | onResp && onResp(); |
63 | - o.scope.$apply(); | ||
64 | 63 | ||
65 | // checks if data changed for row flashing | 64 | // checks if data changed for row flashing |
66 | if (!angular.equals(o.scope.tableData, oldTableData)) { | 65 | if (!angular.equals(o.scope.tableData, oldTableData)) { |
... | @@ -75,6 +74,7 @@ | ... | @@ -75,6 +74,7 @@ |
75 | } | 74 | } |
76 | angular.copy(o.scope.tableData, oldTableData); | 75 | angular.copy(o.scope.tableData, oldTableData); |
77 | } | 76 | } |
77 | + o.scope.$apply(); | ||
78 | } | 78 | } |
79 | handlers[resp] = respCb; | 79 | handlers[resp] = respCb; |
80 | wss.bindHandlers(handlers); | 80 | wss.bindHandlers(handlers); | ... | ... |
... | @@ -51,7 +51,7 @@ | ... | @@ -51,7 +51,7 @@ |
51 | </div> | 51 | </div> |
52 | 52 | ||
53 | <div class="table-body"> | 53 | <div class="table-body"> |
54 | - <table> | 54 | + <table onos-flash-changes id-prop="id"> |
55 | <tr ng-if="!tableData.length" class="no-data"> | 55 | <tr ng-if="!tableData.length" class="no-data"> |
56 | <td colspan="5"> | 56 | <td colspan="5"> |
57 | No Applications found | 57 | No Applications found |
... | @@ -61,7 +61,7 @@ | ... | @@ -61,7 +61,7 @@ |
61 | <tr ng-repeat="app in tableData track by $index" | 61 | <tr ng-repeat="app in tableData track by $index" |
62 | ng-click="selectCallback($event, app)" | 62 | ng-click="selectCallback($event, app)" |
63 | ng-class="{selected: app.id === selId}" | 63 | ng-class="{selected: app.id === selId}" |
64 | - onos-flash-changes row="{{app}}" row-id="id"> | 64 | + ng-repeat-complete row-id="{{app.id}}"> |
65 | <td class="table-icon"> | 65 | <td class="table-icon"> |
66 | <div icon icon-id="{{app._iconid_state}}"></div> | 66 | <div icon icon-id="{{app._iconid_state}}"></div> |
67 | </td> | 67 | </td> | ... | ... |
... | @@ -41,7 +41,7 @@ | ... | @@ -41,7 +41,7 @@ |
41 | </div> | 41 | </div> |
42 | 42 | ||
43 | <div class="table-body"> | 43 | <div class="table-body"> |
44 | - <table> | 44 | + <table onos-flash-changes id-prop="id"> |
45 | <tr ng-if="!tableData.length" class="no-data"> | 45 | <tr ng-if="!tableData.length" class="no-data"> |
46 | <td colspan="5"> | 46 | <td colspan="5"> |
47 | No Cluster Nodes found | 47 | No Cluster Nodes found |
... | @@ -49,7 +49,7 @@ | ... | @@ -49,7 +49,7 @@ |
49 | </tr> | 49 | </tr> |
50 | 50 | ||
51 | <tr ng-repeat="node in tableData track by $index" | 51 | <tr ng-repeat="node in tableData track by $index" |
52 | - onos-flash-changes row="{{node}}" row-id="id"> | 52 | + ng-repeat-complete row-id="{{node.id}}"> |
53 | <td class="table-icon"> | 53 | <td class="table-icon"> |
54 | <div icon icon-id="{{node._iconid_state}}"></div> | 54 | <div icon icon-id="{{node._iconid_state}}"></div> |
55 | </td> | 55 | </td> | ... | ... |
... | @@ -45,7 +45,7 @@ | ... | @@ -45,7 +45,7 @@ |
45 | </div> | 45 | </div> |
46 | 46 | ||
47 | <div class="table-body"> | 47 | <div class="table-body"> |
48 | - <table> | 48 | + <table onos-flash-changes id-prop="id"> |
49 | <tr ng-if="!tableData.length" class="no-data"> | 49 | <tr ng-if="!tableData.length" class="no-data"> |
50 | <td colspan="9"> | 50 | <td colspan="9"> |
51 | No Devices found | 51 | No Devices found |
... | @@ -55,7 +55,7 @@ | ... | @@ -55,7 +55,7 @@ |
55 | <tr ng-repeat="dev in tableData track by $index" | 55 | <tr ng-repeat="dev in tableData track by $index" |
56 | ng-click="selectCallback($event, dev)" | 56 | ng-click="selectCallback($event, dev)" |
57 | ng-class="{selected: dev.id === selId}" | 57 | ng-class="{selected: dev.id === selId}" |
58 | - onos-flash-changes row="{{dev}}" row-id="id"> | 58 | + ng-repeat-complete row-id="{{dev.id}}"> |
59 | <td class="table-icon"> | 59 | <td class="table-icon"> |
60 | <div icon icon-id="{{dev._iconid_available}}"></div> | 60 | <div icon icon-id="{{dev._iconid_available}}"></div> |
61 | </td> | 61 | </td> | ... | ... |
... | @@ -48,7 +48,7 @@ | ... | @@ -48,7 +48,7 @@ |
48 | </div> | 48 | </div> |
49 | 49 | ||
50 | <div class="table-body"> | 50 | <div class="table-body"> |
51 | - <table> | 51 | + <table onos-flash-changes id-prop="id"> |
52 | <tr ng-if="!tableData.length" class="no-data"> | 52 | <tr ng-if="!tableData.length" class="no-data"> |
53 | <td colspan="10"> | 53 | <td colspan="10"> |
54 | No Flows found | 54 | No Flows found |
... | @@ -56,7 +56,7 @@ | ... | @@ -56,7 +56,7 @@ |
56 | </tr> | 56 | </tr> |
57 | 57 | ||
58 | <tr ng-repeat-start="flow in tableData track by $index" | 58 | <tr ng-repeat-start="flow in tableData track by $index" |
59 | - onos-flash-changes row="{{flow}}" row-id="id" multi-row> | 59 | + ng-repeat-complete row-id="{{flow.id}}"> |
60 | <td>{{flow.id}}</td> | 60 | <td>{{flow.id}}</td> |
61 | <td>{{flow.appId}}</td> | 61 | <td>{{flow.appId}}</td> |
62 | <td>{{flow.groupId}}</td> | 62 | <td>{{flow.groupId}}</td> |
... | @@ -68,10 +68,10 @@ | ... | @@ -68,10 +68,10 @@ |
68 | <td>{{flow.packets}}</td> | 68 | <td>{{flow.packets}}</td> |
69 | <td>{{flow.bytes}}</td> | 69 | <td>{{flow.bytes}}</td> |
70 | </tr> | 70 | </tr> |
71 | - <tr class="multi-row-{{flow.id}}"> | 71 | + <tr row-id="{{flow.id}}"> |
72 | <td class="selector" colspan="10">{{flow.selector}}</td> | 72 | <td class="selector" colspan="10">{{flow.selector}}</td> |
73 | </tr> | 73 | </tr> |
74 | - <tr class="multi-row-{{flow.id}}" ng-repeat-end> | 74 | + <tr row-id="{{flow.id}}" ng-repeat-end> |
75 | <td class="treatment" colspan="10">{{flow.treatment}}</td> | 75 | <td class="treatment" colspan="10">{{flow.treatment}}</td> |
76 | </tr> | 76 | </tr> |
77 | </table> | 77 | </table> | ... | ... |
... | @@ -60,7 +60,7 @@ | ... | @@ -60,7 +60,7 @@ |
60 | </div> | 60 | </div> |
61 | 61 | ||
62 | <div class="table-body"> | 62 | <div class="table-body"> |
63 | - <table> | 63 | + <table onos-flash-changes id-prop="id"> |
64 | <tr ng-if="!tableData.length" class="no-data"> | 64 | <tr ng-if="!tableData.length" class="no-data"> |
65 | <td colspan="6"> | 65 | <td colspan="6"> |
66 | No Groups found | 66 | No Groups found |
... | @@ -68,7 +68,7 @@ | ... | @@ -68,7 +68,7 @@ |
68 | </tr> | 68 | </tr> |
69 | 69 | ||
70 | <tr ng-repeat-start="group in tableData track by $index" | 70 | <tr ng-repeat-start="group in tableData track by $index" |
71 | - onos-flash-changes row="{{group}}" row-id="id" multi-row> | 71 | + ng-repeat-complete row-id="{{group.id}}"> |
72 | <td>{{group.id}}</td> | 72 | <td>{{group.id}}</td> |
73 | <td>{{group.app_id}}</td> | 73 | <td>{{group.app_id}}</td> |
74 | <td>{{group.state}}</td> | 74 | <td>{{group.state}}</td> |
... | @@ -76,7 +76,7 @@ | ... | @@ -76,7 +76,7 @@ |
76 | <td>{{group.packets}}</td> | 76 | <td>{{group.packets}}</td> |
77 | <td>{{group.bytes}}</td> | 77 | <td>{{group.bytes}}</td> |
78 | </tr> | 78 | </tr> |
79 | - <tr class="multi-row-{{group.id}}" ng-repeat-end> | 79 | + <tr row-id="{{group.id}}" ng-repeat-end> |
80 | <td class="buckets" colspan="6" | 80 | <td class="buckets" colspan="6" |
81 | ng-bind-html="group.buckets"></td> | 81 | ng-bind-html="group.buckets"></td> |
82 | </tr> | 82 | </tr> | ... | ... |
... | @@ -26,7 +26,7 @@ | ... | @@ -26,7 +26,7 @@ |
26 | </div> | 26 | </div> |
27 | 27 | ||
28 | <div class="table-body"> | 28 | <div class="table-body"> |
29 | - <table> | 29 | + <table onos-flash-changes id-prop="id"> |
30 | <tr ng-if="!tableData.length" class="no-data"> | 30 | <tr ng-if="!tableData.length" class="no-data"> |
31 | <td colspan="6"> | 31 | <td colspan="6"> |
32 | No Hosts found | 32 | No Hosts found |
... | @@ -34,7 +34,7 @@ | ... | @@ -34,7 +34,7 @@ |
34 | </tr> | 34 | </tr> |
35 | 35 | ||
36 | <tr ng-repeat="host in tableData track by $index" | 36 | <tr ng-repeat="host in tableData track by $index" |
37 | - onos-flash-changes row="{{host}}" row-id="id"> | 37 | + ng-repeat-complete row-id="{{host.id}}"> |
38 | <td class="table-icon"> | 38 | <td class="table-icon"> |
39 | <div icon icon-id="{{host._iconid_type}}"></div> | 39 | <div icon icon-id="{{host._iconid_type}}"></div> |
40 | </td> | 40 | </td> | ... | ... |
... | @@ -41,7 +41,7 @@ | ... | @@ -41,7 +41,7 @@ |
41 | </div> | 41 | </div> |
42 | 42 | ||
43 | <div class="table-body"> | 43 | <div class="table-body"> |
44 | - <table> | 44 | + <table onos-flash-changes id-prop="key"> |
45 | <tr ng-if="!tableData.length" class="no-data"> | 45 | <tr ng-if="!tableData.length" class="no-data"> |
46 | <td colspan="5"> | 46 | <td colspan="5"> |
47 | No Intents found | 47 | No Intents found |
... | @@ -49,17 +49,17 @@ | ... | @@ -49,17 +49,17 @@ |
49 | </tr> | 49 | </tr> |
50 | 50 | ||
51 | <tr ng-repeat-start="intent in tableData track by $index" | 51 | <tr ng-repeat-start="intent in tableData track by $index" |
52 | - onos-flash-changes row="{{intent}}" row-id="key" multi-row> | 52 | + ng-repeat-complete row-id="{{intent.key}}"> |
53 | <td>{{intent.appId}}</td> | 53 | <td>{{intent.appId}}</td> |
54 | <td>{{intent.key}}</td> | 54 | <td>{{intent.key}}</td> |
55 | <td>{{intent.type}}</td> | 55 | <td>{{intent.type}}</td> |
56 | <td>{{intent.priority}}</td> | 56 | <td>{{intent.priority}}</td> |
57 | <td>{{intent.state}}</td> | 57 | <td>{{intent.state}}</td> |
58 | </tr> | 58 | </tr> |
59 | - <tr class="multi-row-{{intent.key}}"> | 59 | + <tr row-id="{{intent.key}}"> |
60 | <td class="resources" colspan="5">{{intent.resources}}</td> | 60 | <td class="resources" colspan="5">{{intent.resources}}</td> |
61 | </tr> | 61 | </tr> |
62 | - <tr class="multi-row-{{intent.key}}" ng-repeat-end> | 62 | + <tr row-id="{{intent.key}}" ng-repeat-end> |
63 | <td class="details" colspan="5">{{intent.details}}</td> | 63 | <td class="details" colspan="5">{{intent.details}}</td> |
64 | </tr> | 64 | </tr> |
65 | </table> | 65 | </table> | ... | ... |
... | @@ -42,7 +42,7 @@ | ... | @@ -42,7 +42,7 @@ |
42 | </div> | 42 | </div> |
43 | 43 | ||
44 | <div class="table-body"> | 44 | <div class="table-body"> |
45 | - <table> | 45 | + <table onos-flash-changes id-prop="one"> |
46 | <tr ng-if="!tableData.length" class="no-data"> | 46 | <tr ng-if="!tableData.length" class="no-data"> |
47 | <td colspan="6"> | 47 | <td colspan="6"> |
48 | No Links found | 48 | No Links found |
... | @@ -50,7 +50,7 @@ | ... | @@ -50,7 +50,7 @@ |
50 | </tr> | 50 | </tr> |
51 | 51 | ||
52 | <tr ng-repeat="link in tableData track by $index" | 52 | <tr ng-repeat="link in tableData track by $index" |
53 | - onos-flash-changes row="{{link}}" row-id="one"> | 53 | + ng-repeat-complete row-id="{{link.one}}"> |
54 | <td class="table-icon"> | 54 | <td class="table-icon"> |
55 | <div icon icon-id="{{link._iconid_state}}"></div> | 55 | <div icon icon-id="{{link._iconid_state}}"></div> |
56 | </td> | 56 | </td> | ... | ... |
... | @@ -62,7 +62,7 @@ | ... | @@ -62,7 +62,7 @@ |
62 | </div> | 62 | </div> |
63 | 63 | ||
64 | <div class="table-body"> | 64 | <div class="table-body"> |
65 | - <table> | 65 | + <table onos-flash-changes id-prop="id"> |
66 | <tr ng-if="!tableData.length" class="no-data"> | 66 | <tr ng-if="!tableData.length" class="no-data"> |
67 | <td colspan="8"> | 67 | <td colspan="8"> |
68 | No Ports found | 68 | No Ports found |
... | @@ -70,7 +70,7 @@ | ... | @@ -70,7 +70,7 @@ |
70 | </tr> | 70 | </tr> |
71 | 71 | ||
72 | <tr ng-repeat="port in tableData track by $index" | 72 | <tr ng-repeat="port in tableData track by $index" |
73 | - onos-flash-changes row="{{port}}" row-id="id"> | 73 | + ng-repeat-complete row-id="{{port.id}}"> |
74 | <td>{{port.id}}</td> | 74 | <td>{{port.id}}</td> |
75 | <td>{{port.pkt_rx}}</td> | 75 | <td>{{port.pkt_rx}}</td> |
76 | <td>{{port.pkt_tx}}</td> | 76 | <td>{{port.pkt_tx}}</td> | ... | ... |
... | @@ -26,7 +26,7 @@ | ... | @@ -26,7 +26,7 @@ |
26 | </div> | 26 | </div> |
27 | 27 | ||
28 | <div class="table-body"> | 28 | <div class="table-body"> |
29 | - <table> | 29 | + <table onos-flash-changes id-prop="id"> |
30 | <tr ng-if="!tableData.length" class="no-data"> | 30 | <tr ng-if="!tableData.length" class="no-data"> |
31 | <td colspan="6"> | 31 | <td colspan="6"> |
32 | No Settings found | 32 | No Settings found |
... | @@ -34,7 +34,7 @@ | ... | @@ -34,7 +34,7 @@ |
34 | </tr> | 34 | </tr> |
35 | 35 | ||
36 | <tr ng-repeat="prop in tableData track by $index" | 36 | <tr ng-repeat="prop in tableData track by $index" |
37 | - onos-flash-changes row="{{prop}}" row-id="id"> | 37 | + ng-repeat-complete row-id="{{prop.id}}"> |
38 | <td>{{prop.component}}</td> | 38 | <td>{{prop.component}}</td> |
39 | <td>{{prop.id}}</td> | 39 | <td>{{prop.id}}</td> |
40 | <td>{{prop.type}}</td> | 40 | <td>{{prop.type}}</td> | ... | ... |
-
Please register or login to post a comment