Commit 4e8c05f6707dd93d6b8c7e3da4772319f14d44ca
Committed by
GitHub
Merge pull request #175 from thingsboard/feature/TB-62
TB-62: Implement Entities table widget
Showing
21 changed files
with
725 additions
and
24 deletions
... | ... | @@ -20,6 +20,7 @@ import tinycolor from 'tinycolor2'; |
20 | 20 | import thingsboardLedLight from '../components/led-light.directive'; |
21 | 21 | import thingsboardTimeseriesTableWidget from '../widget/lib/timeseries-table-widget'; |
22 | 22 | import thingsboardAlarmsTableWidget from '../widget/lib/alarms-table-widget'; |
23 | +import thingsboardEntitiesTableWidget from '../widget/lib/entities-table-widget'; | |
23 | 24 | |
24 | 25 | import TbFlot from '../widget/lib/flot-widget'; |
25 | 26 | import TbAnalogueLinearGauge from '../widget/lib/analogue-linear-gauge'; |
... | ... | @@ -34,7 +35,7 @@ import thingsboardTypes from '../common/types.constant'; |
34 | 35 | import thingsboardUtils from '../common/utils.service'; |
35 | 36 | |
36 | 37 | export default angular.module('thingsboard.api.widget', ['oc.lazyLoad', thingsboardLedLight, thingsboardTimeseriesTableWidget, |
37 | - thingsboardAlarmsTableWidget, thingsboardTypes, thingsboardUtils]) | |
38 | + thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget, thingsboardTypes, thingsboardUtils]) | |
38 | 39 | .factory('widgetService', WidgetService) |
39 | 40 | .name; |
40 | 41 | |
... | ... | @@ -546,6 +547,14 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, typ |
546 | 547 | |
547 | 548 | ' }\n\n' + |
548 | 549 | |
550 | + ' self.typeParameters = function() {\n\n' + | |
551 | + { | |
552 | + useCustomDatasources: false, | |
553 | + maxDatasources: -1 //unlimited | |
554 | + maxDataKeys: -1 //unlimited | |
555 | + } | |
556 | + ' }\n\n' + | |
557 | + | |
549 | 558 | ' self.onResize = function() {\n\n' + |
550 | 559 | |
551 | 560 | ' }\n\n' + |
... | ... | @@ -586,10 +595,21 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, typ |
586 | 595 | if (angular.isFunction(widgetTypeInstance.getDataKeySettingsSchema)) { |
587 | 596 | result.dataKeySettingsSchema = widgetTypeInstance.getDataKeySettingsSchema(); |
588 | 597 | } |
598 | + if (angular.isFunction(widgetTypeInstance.typeParameters)) { | |
599 | + result.typeParameters = widgetTypeInstance.typeParameters(); | |
600 | + } else { | |
601 | + result.typeParameters = {}; | |
602 | + } | |
589 | 603 | if (angular.isFunction(widgetTypeInstance.useCustomDatasources)) { |
590 | - result.useCustomDatasources = widgetTypeInstance.useCustomDatasources(); | |
604 | + result.typeParameters.useCustomDatasources = widgetTypeInstance.useCustomDatasources(); | |
591 | 605 | } else { |
592 | - result.useCustomDatasources = false; | |
606 | + result.typeParameters.useCustomDatasources = false; | |
607 | + } | |
608 | + if (angular.isUndefined(result.typeParameters.maxDatasources)) { | |
609 | + result.typeParameters.maxDatasources = -1; | |
610 | + } | |
611 | + if (angular.isUndefined(result.typeParameters.maxDataKeys)) { | |
612 | + result.typeParameters.maxDataKeys = -1; | |
593 | 613 | } |
594 | 614 | return result; |
595 | 615 | } catch (e) { |
... | ... | @@ -629,7 +649,7 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, typ |
629 | 649 | if (widgetType.dataKeySettingsSchema) { |
630 | 650 | widgetInfo.typeDataKeySettingsSchema = widgetType.dataKeySettingsSchema; |
631 | 651 | } |
632 | - widgetInfo.useCustomDatasources = widgetType.useCustomDatasources; | |
652 | + widgetInfo.typeParameters = widgetType.typeParameters; | |
633 | 653 | putWidgetInfoToCache(widgetInfo, bundleAlias, widgetInfo.alias, isSystem); |
634 | 654 | putWidgetTypeFunctionToCache(widgetType.widgetTypeFunction, bundleAlias, widgetInfo.alias, isSystem); |
635 | 655 | deferred.resolve(widgetInfo); | ... | ... |
... | ... | @@ -156,11 +156,19 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc |
156 | 156 | }; |
157 | 157 | |
158 | 158 | scope.transformTimeseriesDataKeyChip = function (chip) { |
159 | - return scope.generateDataKey({chip: chip, type: types.dataKeyType.timeseries}); | |
159 | + if (scope.maxDataKeys > 0 && ngModelCtrl.$viewValue.dataKeys.length >= scope.maxDataKeys ) { | |
160 | + return null; | |
161 | + } else { | |
162 | + return scope.generateDataKey({chip: chip, type: types.dataKeyType.timeseries}); | |
163 | + } | |
160 | 164 | }; |
161 | 165 | |
162 | 166 | scope.transformAttributeDataKeyChip = function (chip) { |
163 | - return scope.generateDataKey({chip: chip, type: types.dataKeyType.attribute}); | |
167 | + if (scope.maxDataKeys > 0 && ngModelCtrl.$viewValue.dataKeys.length >= scope.maxDataKeys ) { | |
168 | + return null; | |
169 | + } else { | |
170 | + return scope.generateDataKey({chip: chip, type: types.dataKeyType.attribute}); | |
171 | + } | |
164 | 172 | }; |
165 | 173 | |
166 | 174 | scope.transformAlarmDataKeyChip = function (chip) { |
... | ... | @@ -272,6 +280,7 @@ function DatasourceEntity($compile, $templateCache, $q, $mdDialog, $window, $doc |
272 | 280 | require: "^ngModel", |
273 | 281 | scope: { |
274 | 282 | widgetType: '=', |
283 | + maxDataKeys: '=', | |
275 | 284 | aliasController: '=', |
276 | 285 | datakeySettingsSchema: '=', |
277 | 286 | generateDataKey: '&', | ... | ... |
... | ... | @@ -186,5 +186,10 @@ |
186 | 186 | <div translate ng-message="entityKeys" ng-if="widgetType === types.widgetType.latest.value" class="tb-error-message">datakey.timeseries-or-attributes-required</div> |
187 | 187 | <div translate ng-message="entityKeys" ng-if="widgetType === types.widgetType.alarm.value" class="tb-error-message">datakey.alarm-fields-required</div> |
188 | 188 | </div> |
189 | + <div class="md-caption" style="color: rgba(0,0,0,0.57);" ng-if="maxDataKeys != -1" | |
190 | + translate="datakey.maximum-timeseries-or-attributes" | |
191 | + translate-values="{count: maxDataKeys}" | |
192 | + translate-interpolation="messageformat" | |
193 | + ></div> | |
189 | 194 | </section> |
190 | 195 | </section> | ... | ... |
... | ... | @@ -117,7 +117,11 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document, |
117 | 117 | }; |
118 | 118 | |
119 | 119 | scope.transformFuncDataKeyChip = function (chip) { |
120 | - return scope.generateDataKey({chip: chip, type: types.dataKeyType.function}); | |
120 | + if (scope.maxDataKeys > 0 && ngModelCtrl.$viewValue.dataKeys.length >= scope.maxDataKeys ) { | |
121 | + return null; | |
122 | + } else { | |
123 | + return scope.generateDataKey({chip: chip, type: types.dataKeyType.function}); | |
124 | + } | |
121 | 125 | }; |
122 | 126 | |
123 | 127 | scope.transformAlarmDataKeyChip = function (chip) { |
... | ... | @@ -217,6 +221,7 @@ function DatasourceFunc($compile, $templateCache, $mdDialog, $window, $document, |
217 | 221 | require: "^ngModel", |
218 | 222 | scope: { |
219 | 223 | widgetType: '=', |
224 | + maxDataKeys: '=', | |
220 | 225 | generateDataKey: '&', |
221 | 226 | datakeySettingsSchema: '=' |
222 | 227 | }, | ... | ... |
... | ... | @@ -17,7 +17,7 @@ |
17 | 17 | --> |
18 | 18 | <section class="tb-datasource-func" flex layout='column' |
19 | 19 | layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center"> |
20 | - <md-input-container ng-if="widgetType != types.widgetType.alarm.value" | |
20 | + <md-input-container ng-show="widgetType != types.widgetType.alarm.value" | |
21 | 21 | class="tb-datasource-name" md-no-float style="min-width: 200px;"> |
22 | 22 | <input name="datasourceName" |
23 | 23 | placeholder="{{ 'datasource.name' | translate }}" |
... | ... | @@ -132,5 +132,10 @@ |
132 | 132 | <div translate ng-message="datasourceKeys" ng-if="widgetType !== types.widgetType.alarm.value" class="tb-error-message">datakey.function-types-required</div> |
133 | 133 | <div translate ng-message="datasourceKeys" ng-if="widgetType === types.widgetType.alarm.value" class="tb-error-message">datakey.alarm-fields-required</div> |
134 | 134 | </div> |
135 | + <div class="md-caption" style="color: rgba(0,0,0,0.57);" ng-if="maxDataKeys != -1" | |
136 | + translate="datakey.maximum-function-types" | |
137 | + translate-values="{count: maxDataKeys}" | |
138 | + translate-interpolation="messageformat" | |
139 | + ></div> | |
135 | 140 | </section> |
136 | 141 | </section> | ... | ... |
... | ... | @@ -27,6 +27,7 @@ |
27 | 27 | <tb-datasource-func flex |
28 | 28 | ng-switch-default |
29 | 29 | ng-model="model" |
30 | + max-data-keys="maxDataKeys" | |
30 | 31 | datakey-settings-schema="datakeySettingsSchema" |
31 | 32 | ng-required="model.type === types.datasourceType.function" |
32 | 33 | widget-type="widgetType" |
... | ... | @@ -34,6 +35,7 @@ |
34 | 35 | </tb-datasource-func> |
35 | 36 | <tb-datasource-entity flex |
36 | 37 | ng-model="model" |
38 | + max-data-keys="maxDataKeys" | |
37 | 39 | datakey-settings-schema="datakeySettingsSchema" |
38 | 40 | ng-switch-when="entity" |
39 | 41 | ng-required="model.type === types.datasourceType.entity" | ... | ... |
... | ... | @@ -442,6 +442,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout |
442 | 442 | forceExpandDatasources: '=?', |
443 | 443 | isDataEnabled: '=?', |
444 | 444 | widgetType: '=', |
445 | + typeParameters: '=', | |
445 | 446 | widgetSettingsSchema: '=', |
446 | 447 | datakeySettingsSchema: '=', |
447 | 448 | aliasController: '=', | ... | ... |
... | ... | @@ -62,7 +62,14 @@ |
62 | 62 | && isDataEnabled"> |
63 | 63 | <v-pane id="datasources-pane" expanded="true"> |
64 | 64 | <v-pane-header> |
65 | - {{ 'widget-config.datasources' | translate }} | |
65 | + <div layout="column"> | |
66 | + <div>{{ 'widget-config.datasources' | translate }}</div> | |
67 | + <div class="md-caption" style="color: rgba(0,0,0,0.57);" ng-if="typeParameters.maxDatasources != -1" | |
68 | + translate="widget-config.maximum-datasources" | |
69 | + translate-values="{count: typeParameters.maxDatasources}" | |
70 | + translate-interpolation="messageformat" | |
71 | + ></div> | |
72 | + </div> | |
66 | 73 | </v-pane-header> |
67 | 74 | <v-pane-content> |
68 | 75 | <div ng-if="datasources.length === 0"> |
... | ... | @@ -88,6 +95,7 @@ |
88 | 95 | style="padding: 0 0 0 10px; margin: 5px;"> |
89 | 96 | <tb-datasource flex ng-model="datasource.value" |
90 | 97 | widget-type="widgetType" |
98 | + max-data-keys="typeParameters.maxDataKeys" | |
91 | 99 | alias-controller="aliasController" |
92 | 100 | functions-only="functionsOnly" |
93 | 101 | datakey-settings-schema="datakeySettingsSchema" |
... | ... | @@ -111,7 +119,7 @@ |
111 | 119 | </div> |
112 | 120 | </div> |
113 | 121 | <div flex layout="row" layout-align="start center"> |
114 | - <md-button ng-disabled="loading" class="md-primary md-raised" | |
122 | + <md-button ng-show="typeParameters.maxDatasources == -1 || datasources.length < typeParameters.maxDatasources" ng-disabled="loading" class="md-primary md-raised" | |
115 | 123 | ng-click="addDatasource($event)" aria-label="{{ 'action.add' | translate }}"> |
116 | 124 | <md-tooltip md-direction="top"> |
117 | 125 | {{ 'widget-config.add-datasource' | translate }} |
... | ... | @@ -140,7 +148,7 @@ |
140 | 148 | </v-pane-content> |
141 | 149 | </v-pane> |
142 | 150 | </v-accordion> |
143 | - <v-accordion id="alarn-source-accordion" control="alarmSourceAccordion" class="vAccordion--default" | |
151 | + <v-accordion id="alarm-source-accordion" control="alarmSourceAccordion" class="vAccordion--default" | |
144 | 152 | ng-if="widgetType === types.widgetType.alarm.value && isDataEnabled"> |
145 | 153 | <v-pane id="alarm-source-pane" expanded="true"> |
146 | 154 | <v-pane-header> | ... | ... |
... | ... | @@ -128,7 +128,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
128 | 128 | |
129 | 129 | var widgetTypeInstance; |
130 | 130 | |
131 | - vm.useCustomDatasources = false; | |
131 | + vm.typeParameters = widgetInfo.typeParameters; | |
132 | 132 | |
133 | 133 | try { |
134 | 134 | widgetTypeInstance = new widgetType(widgetContext); |
... | ... | @@ -154,9 +154,6 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
154 | 154 | if (!widgetTypeInstance.onDestroy) { |
155 | 155 | widgetTypeInstance.onDestroy = function() {}; |
156 | 156 | } |
157 | - if (widgetTypeInstance.useCustomDatasources) { | |
158 | - vm.useCustomDatasources = widgetTypeInstance.useCustomDatasources(); | |
159 | - } | |
160 | 157 | |
161 | 158 | //TODO: widgets visibility |
162 | 159 | |
... | ... | @@ -502,7 +499,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
502 | 499 | var subscription = widgetContext.subscriptions[id]; |
503 | 500 | subscriptionChanged = subscriptionChanged || subscription.onAliasesChanged(aliasIds); |
504 | 501 | } |
505 | - if (subscriptionChanged && !vm.useCustomDatasources) { | |
502 | + if (subscriptionChanged && !vm.typeParameters.useCustomDatasources) { | |
506 | 503 | reInit(); |
507 | 504 | } |
508 | 505 | }); |
... | ... | @@ -513,7 +510,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
513 | 510 | |
514 | 511 | configureWidgetElement(); |
515 | 512 | var deferred = $q.defer(); |
516 | - if (!vm.useCustomDatasources) { | |
513 | + if (!vm.typeParameters.useCustomDatasources) { | |
517 | 514 | createDefaultSubscription().then( |
518 | 515 | function success() { |
519 | 516 | subscriptionInited = true; |
... | ... | @@ -535,7 +532,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
535 | 532 | function reInit() { |
536 | 533 | onDestroy(); |
537 | 534 | configureWidgetElement(); |
538 | - if (!vm.useCustomDatasources) { | |
535 | + if (!vm.typeParameters.useCustomDatasources) { | |
539 | 536 | createDefaultSubscription().then( |
540 | 537 | function success() { |
541 | 538 | subscriptionInited = true; |
... | ... | @@ -575,7 +572,7 @@ export default function WidgetController($scope, $timeout, $window, $element, $q |
575 | 572 | } catch (e) { |
576 | 573 | handleWidgetException(e); |
577 | 574 | } |
578 | - if (!vm.useCustomDatasources && widgetContext.defaultSubscription) { | |
575 | + if (!vm.typeParameters.useCustomDatasources && widgetContext.defaultSubscription) { | |
579 | 576 | widgetContext.defaultSubscription.subscribe(); |
580 | 577 | } |
581 | 578 | } | ... | ... |
... | ... | @@ -33,6 +33,7 @@ |
33 | 33 | <div class="md-dialog-content" style="padding-top: 0px;"> |
34 | 34 | <fieldset ng-disabled="loading" style="position: relative; height: 600px;"> |
35 | 35 | <tb-widget-config widget-type="vm.widget.type" |
36 | + type-parameters="vm.widgetInfo.typeParameters" | |
36 | 37 | force-expand-datasources="true" |
37 | 38 | ng-model="vm.widgetConfig" |
38 | 39 | widget-settings-schema="vm.settingsSchema" | ... | ... |
... | ... | @@ -918,7 +918,7 @@ export default function DashboardController(types, utils, dashboardUtils, widget |
918 | 918 | } |
919 | 919 | } |
920 | 920 | |
921 | - if (widgetTypeInfo.useCustomDatasources) { | |
921 | + if (widgetTypeInfo.typeParameters.useCustomDatasources) { | |
922 | 922 | addWidget(newWidget); |
923 | 923 | } else { |
924 | 924 | $mdDialog.show({ | ... | ... |
... | ... | @@ -212,6 +212,7 @@ |
212 | 212 | class="tb-absolute-fill" md-border-bottom> |
213 | 213 | <md-tab ng-if="vm.timeseriesWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.timeseries' | translate }}"> |
214 | 214 | <tb-dashboard |
215 | + alias-controller="vm.dashboardCtx.aliasController" | |
215 | 216 | widgets="vm.timeseriesWidgetTypes" |
216 | 217 | is-edit="false" |
217 | 218 | is-mobile="true" |
... | ... | @@ -222,6 +223,7 @@ |
222 | 223 | </md-tab> |
223 | 224 | <md-tab ng-if="vm.latestWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.latest-values' | translate }}"> |
224 | 225 | <tb-dashboard |
226 | + alias-controller="vm.dashboardCtx.aliasController" | |
225 | 227 | widgets="vm.latestWidgetTypes" |
226 | 228 | is-edit="false" |
227 | 229 | is-mobile="true" |
... | ... | @@ -232,6 +234,7 @@ |
232 | 234 | </md-tab> |
233 | 235 | <md-tab ng-if="vm.rpcWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.rpc' | translate }}"> |
234 | 236 | <tb-dashboard |
237 | + alias-controller="vm.dashboardCtx.aliasController" | |
235 | 238 | widgets="vm.rpcWidgetTypes" |
236 | 239 | is-edit="false" |
237 | 240 | is-mobile="true" |
... | ... | @@ -242,6 +245,7 @@ |
242 | 245 | </md-tab> |
243 | 246 | <md-tab ng-if="vm.alarmWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.alarm' | translate }}"> |
244 | 247 | <tb-dashboard |
248 | + alias-controller="vm.dashboardCtx.aliasController" | |
245 | 249 | widgets="vm.alarmWidgetTypes" |
246 | 250 | is-edit="false" |
247 | 251 | is-mobile="true" |
... | ... | @@ -252,6 +256,7 @@ |
252 | 256 | </md-tab> |
253 | 257 | <md-tab ng-if="vm.staticWidgetTypes.length > 0" style="height: 100%;" label="{{ 'widget.static' | translate }}"> |
254 | 258 | <tb-dashboard |
259 | + alias-controller="vm.dashboardCtx.aliasController" | |
255 | 260 | widgets="vm.staticWidgetTypes" |
256 | 261 | is-edit="false" |
257 | 262 | is-mobile="true" | ... | ... |
... | ... | @@ -40,7 +40,8 @@ export default function EditWidgetDirective($compile, $templateCache, types, wid |
40 | 40 | }; |
41 | 41 | var settingsSchema = widgetInfo.typeSettingsSchema || widgetInfo.settingsSchema; |
42 | 42 | var dataKeySettingsSchema = widgetInfo.typeDataKeySettingsSchema || widgetInfo.dataKeySettingsSchema; |
43 | - scope.isDataEnabled = !widgetInfo.useCustomDatasources; | |
43 | + scope.typeParameters = widgetInfo.typeParameters; | |
44 | + scope.isDataEnabled = !widgetInfo.typeParameters.useCustomDatasources; | |
44 | 45 | if (!settingsSchema || settingsSchema === '') { |
45 | 46 | scope.settingsSchema = {}; |
46 | 47 | } else { | ... | ... |
... | ... | @@ -499,9 +499,11 @@ export default angular.module('thingsboard.locale', []) |
499 | 499 | "alarm": "Alarm fields", |
500 | 500 | "timeseries-required": "Entity timeseries are required.", |
501 | 501 | "timeseries-or-attributes-required": "Entity timeseries/attributes are required.", |
502 | + "maximum-timeseries-or-attributes": "Maximum { count, select, 1 {1 timeseries/attribute is allowed.} other {# timeseries/attributes are allowed} }", | |
502 | 503 | "alarm-fields-required": "Alarm fields are required.", |
503 | 504 | "function-types": "Function types", |
504 | - "function-types-required": "Function types are required." | |
505 | + "function-types-required": "Function types are required.", | |
506 | + "maximum-function-types": "Maximum { count, select, 1 {1 function type is allowed.} other {# function types are allowed} }" | |
505 | 507 | }, |
506 | 508 | "datasource": { |
507 | 509 | "type": "Datasource type", |
... | ... | @@ -691,7 +693,13 @@ export default angular.module('thingsboard.locale', []) |
691 | 693 | "type-alarm": "Alarm", |
692 | 694 | "type-alarms": "Alarms", |
693 | 695 | "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }", |
694 | - "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'" | |
696 | + "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'", | |
697 | + "search": "Search entities", | |
698 | + "selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected", | |
699 | + "entity-name": "Entity name", | |
700 | + "details": "Entity details", | |
701 | + "no-entities-prompt": "No entities found", | |
702 | + "no-data": "No data to display" | |
695 | 703 | }, |
696 | 704 | "event": { |
697 | 705 | "event-type": "Event type", |
... | ... | @@ -1144,6 +1152,7 @@ export default angular.module('thingsboard.locale', []) |
1144 | 1152 | "use-dashboard-timewindow": "Use dashboard timewindow", |
1145 | 1153 | "display-legend": "Display legend", |
1146 | 1154 | "datasources": "Datasources", |
1155 | + "maximum-datasources": "Maximum { count, select, 1 {1 datasource is allowed.} other {# datasources are allowed} }", | |
1147 | 1156 | "datasource-type": "Type", |
1148 | 1157 | "datasource-parameters": "Parameters", |
1149 | 1158 | "remove-datasource": "Remove datasource", | ... | ... |
1 | +/* | |
2 | + * Copyright © 2016-2017 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +import './entities-table-widget.scss'; | |
18 | + | |
19 | +/* eslint-disable import/no-unresolved, import/default */ | |
20 | + | |
21 | +import entitiesTableWidgetTemplate from './entities-table-widget.tpl.html'; | |
22 | +//import entityDetailsDialogTemplate from './entitiy-details-dialog.tpl.html'; | |
23 | + | |
24 | +/* eslint-enable import/no-unresolved, import/default */ | |
25 | + | |
26 | +import tinycolor from 'tinycolor2'; | |
27 | +import cssjs from '../../../vendor/css.js/css'; | |
28 | + | |
29 | +export default angular.module('thingsboard.widgets.entitiesTableWidget', []) | |
30 | + .directive('tbEntitiesTableWidget', EntitiesTableWidget) | |
31 | + .name; | |
32 | + | |
33 | +/*@ngInject*/ | |
34 | +function EntitiesTableWidget() { | |
35 | + return { | |
36 | + restrict: "E", | |
37 | + scope: true, | |
38 | + bindToController: { | |
39 | + tableId: '=', | |
40 | + ctx: '=' | |
41 | + }, | |
42 | + controller: EntitiesTableWidgetController, | |
43 | + controllerAs: 'vm', | |
44 | + templateUrl: entitiesTableWidgetTemplate | |
45 | + }; | |
46 | +} | |
47 | + | |
48 | +/*@ngInject*/ | |
49 | +function EntitiesTableWidgetController($element, $scope, $filter, $mdMedia, $translate, utils, types) { | |
50 | + var vm = this; | |
51 | + | |
52 | + vm.stylesInfo = {}; | |
53 | + vm.contentsInfo = {}; | |
54 | + vm.columnWidth = {}; | |
55 | + | |
56 | + vm.showData = true; | |
57 | + vm.hasData = false; | |
58 | + | |
59 | + vm.entities = []; | |
60 | + vm.entitiesCount = 0; | |
61 | + | |
62 | + vm.datasources = null; | |
63 | + vm.allEntities = null; | |
64 | + | |
65 | + vm.currentEntity = null; | |
66 | + | |
67 | + vm.displayEntityName = true; | |
68 | + vm.displayEntityType = true; | |
69 | + vm.displayActions = false; //TODO: Widget actions | |
70 | + vm.displayPagination = true; | |
71 | + vm.defaultPageSize = 10; | |
72 | + vm.defaultSortOrder = 'entityName'; | |
73 | + | |
74 | + vm.query = { | |
75 | + order: vm.defaultSortOrder, | |
76 | + limit: vm.defaultPageSize, | |
77 | + page: 1, | |
78 | + search: null | |
79 | + }; | |
80 | + | |
81 | + vm.searchAction = { | |
82 | + name: 'action.search', | |
83 | + show: true, | |
84 | + onAction: function() { | |
85 | + vm.enterFilterMode(); | |
86 | + }, | |
87 | + icon: 'search' | |
88 | + }; | |
89 | + | |
90 | + vm.enterFilterMode = enterFilterMode; | |
91 | + vm.exitFilterMode = exitFilterMode; | |
92 | + vm.onReorder = onReorder; | |
93 | + vm.onPaginate = onPaginate; | |
94 | + vm.onRowClick = onRowClick; | |
95 | + vm.isCurrent = isCurrent; | |
96 | + | |
97 | + vm.cellStyle = cellStyle; | |
98 | + vm.cellContent = cellContent; | |
99 | + | |
100 | + $scope.$watch('vm.ctx', function() { | |
101 | + if (vm.ctx) { | |
102 | + vm.settings = vm.ctx.settings; | |
103 | + vm.widgetConfig = vm.ctx.widgetConfig; | |
104 | + vm.subscription = vm.ctx.defaultSubscription; | |
105 | + vm.datasources = vm.subscription.datasources; | |
106 | + initializeConfig(); | |
107 | + updateDatasources(); | |
108 | + } | |
109 | + }); | |
110 | + | |
111 | + $scope.$watch("vm.query.search", function(newVal, prevVal) { | |
112 | + if (!angular.equals(newVal, prevVal) && vm.query.search != null) { | |
113 | + updateEntities(); | |
114 | + } | |
115 | + }); | |
116 | + | |
117 | + $scope.$on('entities-table-data-updated', function(event, tableId) { | |
118 | + if (vm.tableId == tableId) { | |
119 | + if (vm.subscription) { | |
120 | + updateEntitiesData(vm.subscription.data); | |
121 | + updateEntities(); | |
122 | + $scope.$digest(); | |
123 | + } | |
124 | + } | |
125 | + }); | |
126 | + | |
127 | + $scope.$watch(function() { return $mdMedia('gt-xs'); }, function(isGtXs) { | |
128 | + vm.isGtXs = isGtXs; | |
129 | + }); | |
130 | + | |
131 | + $scope.$watch(function() { return $mdMedia('gt-md'); }, function(isGtMd) { | |
132 | + vm.isGtMd = isGtMd; | |
133 | + if (vm.isGtMd) { | |
134 | + vm.limitOptions = [vm.defaultPageSize, vm.defaultPageSize*2, vm.defaultPageSize*3]; | |
135 | + } else { | |
136 | + vm.limitOptions = null; | |
137 | + } | |
138 | + }); | |
139 | + | |
140 | + function initializeConfig() { | |
141 | + | |
142 | + vm.ctx.widgetActions = [ vm.searchAction ]; | |
143 | + | |
144 | + if (vm.settings.entitiesTitle && vm.settings.entitiesTitle.length) { | |
145 | + var translationId = types.translate.customTranslationsPrefix + vm.settings.entitiesTitle; | |
146 | + var translation = $translate.instant(translationId); | |
147 | + if (translation != translationId) { | |
148 | + vm.entitiesTitle = translation + ''; | |
149 | + } else { | |
150 | + vm.entitiesTitle = vm.settings.entitiesTitle; | |
151 | + } | |
152 | + } else { | |
153 | + vm.entitiesTitle = $translate.instant('entity.entities'); | |
154 | + } | |
155 | + | |
156 | + vm.ctx.widgetTitle = vm.entitiesTitle; | |
157 | + | |
158 | + vm.searchAction.show = angular.isDefined(vm.settings.enableSearch) ? vm.settings.enableSearch : true; | |
159 | + vm.displayEntityName = angular.isDefined(vm.settings.displayEntityName) ? vm.settings.displayEntityName : true; | |
160 | + vm.displayEntityType = angular.isDefined(vm.settings.displayEntityType) ? vm.settings.displayEntityType : true; | |
161 | + vm.displayPagination = angular.isDefined(vm.settings.displayPagination) ? vm.settings.displayPagination : true; | |
162 | + | |
163 | + var pageSize = vm.settings.defaultPageSize; | |
164 | + if (angular.isDefined(pageSize) && Number.isInteger(pageSize) && pageSize > 0) { | |
165 | + vm.defaultPageSize = pageSize; | |
166 | + } | |
167 | + | |
168 | + if (vm.settings.defaultSortOrder && vm.settings.defaultSortOrder.length) { | |
169 | + vm.defaultSortOrder = vm.settings.defaultSortOrder; | |
170 | + } | |
171 | + | |
172 | + vm.query.order = vm.defaultSortOrder; | |
173 | + vm.query.limit = vm.defaultPageSize; | |
174 | + if (vm.isGtMd) { | |
175 | + vm.limitOptions = [vm.defaultPageSize, vm.defaultPageSize*2, vm.defaultPageSize*3]; | |
176 | + } else { | |
177 | + vm.limitOptions = null; | |
178 | + } | |
179 | + | |
180 | + var origColor = vm.widgetConfig.color || 'rgba(0, 0, 0, 0.87)'; | |
181 | + var defaultColor = tinycolor(origColor); | |
182 | + var mdDark = defaultColor.setAlpha(0.87).toRgbString(); | |
183 | + var mdDarkSecondary = defaultColor.setAlpha(0.54).toRgbString(); | |
184 | + var mdDarkDisabled = defaultColor.setAlpha(0.26).toRgbString(); | |
185 | + //var mdDarkIcon = mdDarkSecondary; | |
186 | + var mdDarkDivider = defaultColor.setAlpha(0.12).toRgbString(); | |
187 | + | |
188 | + var cssString = 'table.md-table th.md-column {\n'+ | |
189 | + 'color: ' + mdDarkSecondary + ';\n'+ | |
190 | + '}\n'+ | |
191 | + 'table.md-table th.md-column.md-checkbox-column md-checkbox:not(.md-checked) .md-icon {\n'+ | |
192 | + 'border-color: ' + mdDarkSecondary + ';\n'+ | |
193 | + '}\n'+ | |
194 | + 'table.md-table th.md-column md-icon.md-sort-icon {\n'+ | |
195 | + 'color: ' + mdDarkDisabled + ';\n'+ | |
196 | + '}\n'+ | |
197 | + 'table.md-table th.md-column.md-active, table.md-table th.md-column.md-active md-icon {\n'+ | |
198 | + 'color: ' + mdDark + ';\n'+ | |
199 | + '}\n'+ | |
200 | + 'table.md-table td.md-cell {\n'+ | |
201 | + 'color: ' + mdDark + ';\n'+ | |
202 | + 'border-top: 1px '+mdDarkDivider+' solid;\n'+ | |
203 | + '}\n'+ | |
204 | + 'table.md-table td.md-cell.md-checkbox-cell md-checkbox:not(.md-checked) .md-icon {\n'+ | |
205 | + 'border-color: ' + mdDarkSecondary + ';\n'+ | |
206 | + '}\n'+ | |
207 | + 'table.md-table td.md-cell.md-placeholder {\n'+ | |
208 | + 'color: ' + mdDarkDisabled + ';\n'+ | |
209 | + '}\n'+ | |
210 | + 'table.md-table td.md-cell md-select > .md-select-value > span.md-select-icon {\n'+ | |
211 | + 'color: ' + mdDarkSecondary + ';\n'+ | |
212 | + '}\n'+ | |
213 | + '.md-table-pagination {\n'+ | |
214 | + 'color: ' + mdDarkSecondary + ';\n'+ | |
215 | + 'border-top: 1px '+mdDarkDivider+' solid;\n'+ | |
216 | + '}\n'+ | |
217 | + '.md-table-pagination .buttons md-icon {\n'+ | |
218 | + 'color: ' + mdDarkSecondary + ';\n'+ | |
219 | + '}\n'+ | |
220 | + '.md-table-pagination md-select:not([disabled]):focus .md-select-value {\n'+ | |
221 | + 'color: ' + mdDarkSecondary + ';\n'+ | |
222 | + '}'; | |
223 | + | |
224 | + var cssParser = new cssjs(); | |
225 | + cssParser.testMode = false; | |
226 | + var namespace = 'entities-table-' + hashCode(cssString); | |
227 | + cssParser.cssPreviewNamespace = namespace; | |
228 | + cssParser.createStyleElement(namespace, cssString); | |
229 | + $element.addClass(namespace); | |
230 | + | |
231 | + function hashCode(str) { | |
232 | + var hash = 0; | |
233 | + var i, char; | |
234 | + if (str.length === 0) return hash; | |
235 | + for (i = 0; i < str.length; i++) { | |
236 | + char = str.charCodeAt(i); | |
237 | + hash = ((hash << 5) - hash) + char; | |
238 | + hash = hash & hash; | |
239 | + } | |
240 | + return hash; | |
241 | + } | |
242 | + } | |
243 | + | |
244 | + function enterFilterMode () { | |
245 | + vm.query.search = ''; | |
246 | + vm.ctx.hideTitlePanel = true; | |
247 | + } | |
248 | + | |
249 | + function exitFilterMode () { | |
250 | + vm.query.search = null; | |
251 | + updateEntities(); | |
252 | + vm.ctx.hideTitlePanel = false; | |
253 | + } | |
254 | + | |
255 | + function onReorder () { | |
256 | + updateEntities(); | |
257 | + } | |
258 | + | |
259 | + function onPaginate () { | |
260 | + updateEntities(); | |
261 | + } | |
262 | + | |
263 | + function onRowClick($event, entity) { | |
264 | + if (vm.currentEntity != entity) { | |
265 | + vm.currentEntity = entity; | |
266 | + } | |
267 | + } | |
268 | + | |
269 | + function isCurrent(entity) { | |
270 | + return (vm.currentEntity && entity && vm.currentEntity.id && entity.id) && | |
271 | + (vm.currentEntity.id.id === entity.id.id); | |
272 | + } | |
273 | + | |
274 | + function updateEntities() { | |
275 | + var result = $filter('orderBy')(vm.allEntities, vm.query.order); | |
276 | + if (vm.query.search != null) { | |
277 | + result = $filter('filter')(result, {$: vm.query.search}); | |
278 | + } | |
279 | + vm.entitiesCount = result.length; | |
280 | + | |
281 | + if (vm.displayPagination) { | |
282 | + var startIndex = vm.query.limit * (vm.query.page - 1); | |
283 | + vm.entities = result.slice(startIndex, startIndex + vm.query.limit); | |
284 | + } else { | |
285 | + vm.entities = result; | |
286 | + } | |
287 | + } | |
288 | + | |
289 | + function cellStyle(entity, key) { | |
290 | + var style = {}; | |
291 | + if (entity && key) { | |
292 | + var styleInfo = vm.stylesInfo[key.label]; | |
293 | + var value = getEntityValue(entity, key); | |
294 | + if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) { | |
295 | + try { | |
296 | + style = styleInfo.cellStyleFunction(value); | |
297 | + } catch (e) { | |
298 | + style = {}; | |
299 | + } | |
300 | + } else { | |
301 | + style = defaultStyle(key, value); | |
302 | + } | |
303 | + } | |
304 | + if (!style.width) { | |
305 | + var columnWidth = vm.columnWidth[key.label]; | |
306 | + style.width = columnWidth; | |
307 | + } | |
308 | + return style; | |
309 | + } | |
310 | + | |
311 | + function cellContent(entity, key) { | |
312 | + var strContent = ''; | |
313 | + if (entity && key) { | |
314 | + var contentInfo = vm.contentsInfo[key.label]; | |
315 | + var value = getEntityValue(entity, key); | |
316 | + if (contentInfo.useCellContentFunction && contentInfo.cellContentFunction) { | |
317 | + if (angular.isDefined(value)) { | |
318 | + strContent = '' + value; | |
319 | + } | |
320 | + var content = strContent; | |
321 | + try { | |
322 | + content = contentInfo.cellContentFunction(value, entity, $filter); | |
323 | + } catch (e) { | |
324 | + content = strContent; | |
325 | + } | |
326 | + } else { | |
327 | + content = defaultContent(key, value); | |
328 | + } | |
329 | + return content; | |
330 | + } else { | |
331 | + return strContent; | |
332 | + } | |
333 | + } | |
334 | + | |
335 | + function defaultContent(key, value) { | |
336 | + if (angular.isDefined(value)) { | |
337 | + return value; | |
338 | + } else { | |
339 | + return ''; | |
340 | + } | |
341 | + } | |
342 | + | |
343 | + function defaultStyle(/*key, value*/) { | |
344 | + return {}; | |
345 | + } | |
346 | + | |
347 | + const getDescendantProp = (obj, path) => ( | |
348 | + path.split('.').reduce((acc, part) => acc && acc[part], obj) | |
349 | + ); | |
350 | + | |
351 | + function getEntityValue(entity, key) { | |
352 | + return getDescendantProp(entity, key.name); | |
353 | + } | |
354 | + | |
355 | + function updateEntitiesData(data) { | |
356 | + if (vm.allEntities) { | |
357 | + for (var i=0;i<vm.allEntities.length;i++) { | |
358 | + var entity = vm.allEntities[i]; | |
359 | + for (var a=0;a<vm.dataKeys.length;a++) { | |
360 | + var dataKey = vm.dataKeys[a]; | |
361 | + var index = i * vm.dataKeys.length + a; | |
362 | + var keyData = data[index].data; | |
363 | + if (keyData && keyData.length && keyData[0].length > 1) { | |
364 | + var value = keyData[0][1]; | |
365 | + entity[dataKey.name] = value; | |
366 | + } else { | |
367 | + entity[dataKey.name] = ''; | |
368 | + } | |
369 | + } | |
370 | + } | |
371 | + } | |
372 | + } | |
373 | + | |
374 | + function updateDatasources() { | |
375 | + | |
376 | + vm.stylesInfo = {}; | |
377 | + vm.contentsInfo = {}; | |
378 | + vm.columnWidth = {}; | |
379 | + vm.dataKeys = []; | |
380 | + vm.allEntities = []; | |
381 | + | |
382 | + var datasource; | |
383 | + var dataKey; | |
384 | + | |
385 | + datasource = vm.datasources[0]; | |
386 | + | |
387 | + vm.ctx.widgetTitle = utils.createLabelFromDatasource(datasource, vm.entitiesTitle); | |
388 | + | |
389 | + for (var d = 0; d < datasource.dataKeys.length; d++ ) { | |
390 | + dataKey = angular.copy(datasource.dataKeys[d]); | |
391 | + if (dataKey.type == types.dataKeyType.function) { | |
392 | + dataKey.name = dataKey.label; | |
393 | + } | |
394 | + vm.dataKeys.push(dataKey); | |
395 | + | |
396 | + var translationId = types.translate.customTranslationsPrefix + dataKey.label; | |
397 | + var translation = $translate.instant(translationId); | |
398 | + if (translation != translationId) { | |
399 | + dataKey.title = translation + ''; | |
400 | + } else { | |
401 | + dataKey.title = dataKey.label; | |
402 | + } | |
403 | + | |
404 | + var keySettings = dataKey.settings; | |
405 | + | |
406 | + var cellStyleFunction = null; | |
407 | + var useCellStyleFunction = false; | |
408 | + | |
409 | + if (keySettings.useCellStyleFunction === true) { | |
410 | + if (angular.isDefined(keySettings.cellStyleFunction) && keySettings.cellStyleFunction.length > 0) { | |
411 | + try { | |
412 | + cellStyleFunction = new Function('value', keySettings.cellStyleFunction); | |
413 | + useCellStyleFunction = true; | |
414 | + } catch (e) { | |
415 | + cellStyleFunction = null; | |
416 | + useCellStyleFunction = false; | |
417 | + } | |
418 | + } | |
419 | + } | |
420 | + | |
421 | + vm.stylesInfo[dataKey.label] = { | |
422 | + useCellStyleFunction: useCellStyleFunction, | |
423 | + cellStyleFunction: cellStyleFunction | |
424 | + }; | |
425 | + | |
426 | + var cellContentFunction = null; | |
427 | + var useCellContentFunction = false; | |
428 | + | |
429 | + if (keySettings.useCellContentFunction === true) { | |
430 | + if (angular.isDefined(keySettings.cellContentFunction) && keySettings.cellContentFunction.length > 0) { | |
431 | + try { | |
432 | + cellContentFunction = new Function('value, entity, filter', keySettings.cellContentFunction); | |
433 | + useCellContentFunction = true; | |
434 | + } catch (e) { | |
435 | + cellContentFunction = null; | |
436 | + useCellContentFunction = false; | |
437 | + } | |
438 | + } | |
439 | + } | |
440 | + | |
441 | + vm.contentsInfo[dataKey.label] = { | |
442 | + useCellContentFunction: useCellContentFunction, | |
443 | + cellContentFunction: cellContentFunction | |
444 | + }; | |
445 | + | |
446 | + var columnWidth = angular.isDefined(keySettings.columnWidth) ? keySettings.columnWidth : '0px'; | |
447 | + vm.columnWidth[dataKey.label] = columnWidth; | |
448 | + } | |
449 | + | |
450 | + for (var i=0;i<vm.datasources.length;i++) { | |
451 | + datasource = vm.datasources[i]; | |
452 | + var entity = { | |
453 | + id: {} | |
454 | + }; | |
455 | + entity.entityName = datasource.entityName; | |
456 | + if (datasource.entityId) { | |
457 | + entity.id.id = datasource.entityId; | |
458 | + } | |
459 | + if (datasource.entityType) { | |
460 | + entity.id.entityType = datasource.entityType; | |
461 | + entity.entityType = $translate.instant(types.entityTypeTranslations[datasource.entityType].type) + ''; | |
462 | + } else { | |
463 | + entity.entityType = ''; | |
464 | + } | |
465 | + for (d = 0; d < vm.dataKeys.length; d++) { | |
466 | + dataKey = vm.dataKeys[d]; | |
467 | + entity[dataKey.name] = ''; | |
468 | + } | |
469 | + vm.allEntities.push(entity); | |
470 | + } | |
471 | + | |
472 | + } | |
473 | + | |
474 | +} | |
\ No newline at end of file | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2017 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +.tb-has-timewindow { | |
18 | + .tb-entities-table { | |
19 | + md-toolbar { | |
20 | + min-height: 60px; | |
21 | + max-height: 60px; | |
22 | + &.md-table-toolbar { | |
23 | + .md-toolbar-tools { | |
24 | + max-height: 60px; | |
25 | + } | |
26 | + } | |
27 | + } | |
28 | + } | |
29 | +} | |
30 | + | |
31 | +.tb-entities-table { | |
32 | + | |
33 | + md-toolbar { | |
34 | + min-height: 39px; | |
35 | + max-height: 39px; | |
36 | + &.md-table-toolbar { | |
37 | + .md-toolbar-tools { | |
38 | + max-height: 39px; | |
39 | + } | |
40 | + } | |
41 | + } | |
42 | + | |
43 | + &.tb-data-table { | |
44 | + table.md-table, table.md-table.md-row-select { | |
45 | + tbody { | |
46 | + tr { | |
47 | + td { | |
48 | + &.tb-action-cell { | |
49 | + min-width: 36px; | |
50 | + max-width: 36px; | |
51 | + width: 36px; | |
52 | + } | |
53 | + } | |
54 | + } | |
55 | + } | |
56 | + } | |
57 | + } | |
58 | +} | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2017 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<div class="tb-absolute-fill tb-entities-table tb-data-table" layout="column"> | |
19 | + <div ng-show="vm.showData" flex class="tb-absolute-fill" layout="column"> | |
20 | + <md-toolbar class="md-table-toolbar md-default" ng-show="vm.query.search != null"> | |
21 | + <div class="md-toolbar-tools"> | |
22 | + <md-button class="md-icon-button" aria-label="{{ 'action.search' | translate }}"> | |
23 | + <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon> | |
24 | + <md-tooltip md-direction="top"> | |
25 | + {{'entity.search' | translate}} | |
26 | + </md-tooltip> | |
27 | + </md-button> | |
28 | + <md-input-container flex> | |
29 | + <label> </label> | |
30 | + <input ng-model="vm.query.search" placeholder="{{'entity.search' | translate}}"/> | |
31 | + </md-input-container> | |
32 | + <md-button class="md-icon-button" aria-label="Close" ng-click="vm.exitFilterMode()"> | |
33 | + <md-icon aria-label="Close" class="material-icons">close</md-icon> | |
34 | + <md-tooltip md-direction="top"> | |
35 | + {{ 'action.close' | translate }} | |
36 | + </md-tooltip> | |
37 | + </md-button> | |
38 | + </div> | |
39 | + </md-toolbar> | |
40 | + <md-table-container flex> | |
41 | + <table md-table> | |
42 | + <thead md-head md-order="vm.query.order" md-on-reorder="vm.onReorder"> | |
43 | + <tr md-row> | |
44 | + <th md-column ng-if="vm.displayEntityName" md-order-by="entityName"><span translate>entity.entity-name</span></th> | |
45 | + <th md-column ng-if="vm.displayEntityType" md-order-by="entityType"><span translate>entity.entity-type</span></th> | |
46 | + <th md-column md-order-by="{{ key.name }}" ng-repeat="key in vm.dataKeys"><span>{{ key.title }}</span></th> | |
47 | + <th md-column ng-if="vm.displayActions"><span> </span></th> | |
48 | + </tr> | |
49 | + </thead> | |
50 | + <tbody md-body> | |
51 | + <tr ng-show="vm.entities.length" md-row md-select="entity" | |
52 | + md-select-id="id.id" md-auto-select="false" ng-repeat="entity in vm.entities" | |
53 | + ng-click="vm.onRowClick($event, entity)" ng-class="{'tb-current': vm.isCurrent(entity)}"> | |
54 | + <td md-cell flex ng-if="vm.displayEntityName">{{entity.entityName}}</td> | |
55 | + <td md-cell flex ng-if="vm.displayEntityType">{{entity.entityType}}</td> | |
56 | + <td md-cell flex ng-repeat="key in vm.dataKeys" | |
57 | + ng-style="vm.cellStyle(entity, key)" | |
58 | + ng-bind-html="vm.cellContent(entity, key)"> | |
59 | + </td> | |
60 | + <td md-cell ng-if="vm.displayActions" class="tb-action-cell"> | |
61 | + <!--md-button class="md-icon-button" aria-label="{{ 'entity.details' | translate }}" | |
62 | + ng-click="vm.openEntityDetails($event, entity)"> | |
63 | + <md-icon aria-label="{{ 'entity.details' | translate }}" class="material-icons">more_horiz</md-icon> | |
64 | + <md-tooltip md-direction="top"> | |
65 | + {{ 'entity.details' | translate }} | |
66 | + </md-tooltip> | |
67 | + </md-button--> | |
68 | + </td> | |
69 | + </tr> | |
70 | + </tbody> | |
71 | + </table> | |
72 | + <md-divider></md-divider> | |
73 | + <span ng-show="!vm.entities.length" | |
74 | + layout-align="center center" | |
75 | + class="no-data-found" translate>entity.no-entities-prompt</span> | |
76 | + </md-table-container> | |
77 | + <md-table-pagination ng-if="vm.displayPagination" md-boundary-links md-limit="vm.query.limit" md-limit-options="vm.limitOptions" | |
78 | + md-page="vm.query.page" md-total="{{vm.entitiesCount}}" | |
79 | + md-on-paginate="vm.onPaginate" md-page-select="vm.isGtMd"> | |
80 | + </md-table-pagination> | |
81 | + </div> | |
82 | + <span ng-show="!vm.showData" | |
83 | + layout-align="center center" | |
84 | + style="text-transform: uppercase; display: flex;" | |
85 | + class="tb-absolute-fill" translate>entity.no-data</span> | |
86 | +</div> | ... | ... |
... | ... | @@ -13,6 +13,9 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
17 | +import AliasController from '../api/alias-controller'; | |
18 | + | |
16 | 19 | /* eslint-disable import/no-unresolved, import/default */ |
17 | 20 | |
18 | 21 | import selectWidgetTypeTemplate from './select-widget-type.tpl.html'; |
... | ... | @@ -21,7 +24,8 @@ import selectWidgetTypeTemplate from './select-widget-type.tpl.html'; |
21 | 24 | |
22 | 25 | /*@ngInject*/ |
23 | 26 | export default function WidgetLibraryController($scope, $rootScope, $q, widgetService, userService, importExport, |
24 | - $state, $stateParams, $document, $mdDialog, $translate, $filter, types) { | |
27 | + $state, $stateParams, $document, $mdDialog, $translate, $filter, | |
28 | + utils, types, entityService) { | |
25 | 29 | |
26 | 30 | var vm = this; |
27 | 31 | |
... | ... | @@ -31,6 +35,14 @@ export default function WidgetLibraryController($scope, $rootScope, $q, widgetSe |
31 | 35 | vm.widgetTypes = []; |
32 | 36 | vm.dashboardInitComplete = false; |
33 | 37 | |
38 | + var stateController = { | |
39 | + getStateParams: function() { | |
40 | + return {}; | |
41 | + } | |
42 | + }; | |
43 | + vm.aliasController = new AliasController($scope, $q, $filter, utils, | |
44 | + types, entityService, stateController, {}); | |
45 | + | |
34 | 46 | vm.noData = noData; |
35 | 47 | vm.dashboardInited = dashboardInited; |
36 | 48 | vm.dashboardInitFailed = dashboardInitFailed; | ... | ... |