plugin_component.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. ///<reference path="../../headers/common.d.ts" />
  2. import angular from 'angular';
  3. import _ from 'lodash';
  4. import config from 'app/core/config';
  5. import coreModule from 'app/core/core_module';
  6. import {UnknownPanelCtrl} from 'app/plugins/panel/unknown/module';
  7. /** @ngInject **/
  8. function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $templateCache) {
  9. function getTemplate(component) {
  10. if (component.template) {
  11. return $q.when(component.template);
  12. }
  13. var cached = $templateCache.get(component.templateUrl);
  14. if (cached) {
  15. return $q.when(cached);
  16. }
  17. return $http.get(component.templateUrl).then(res => {
  18. return res.data;
  19. });
  20. }
  21. function getPluginComponentDirective(options) {
  22. return function() {
  23. return {
  24. templateUrl: options.Component.templateUrl,
  25. template: options.Component.template,
  26. restrict: 'E',
  27. controller: options.Component,
  28. controllerAs: 'ctrl',
  29. bindToController: true,
  30. scope: options.bindings,
  31. link: (scope, elem, attrs, ctrl) => {
  32. if (ctrl.link) {
  33. ctrl.link(scope, elem, attrs, ctrl);
  34. }
  35. if (ctrl.init) {
  36. ctrl.init();
  37. }
  38. }
  39. };
  40. };
  41. }
  42. function loadPanelComponentInfo(scope, attrs) {
  43. var componentInfo: any = {
  44. name: 'panel-plugin-' + scope.panel.type,
  45. bindings: {dashboard: "=", panel: "=", row: "="},
  46. attrs: {dashboard: "dashboard", panel: "panel", row: "row"},
  47. };
  48. var panelElemName = 'panel-' + scope.panel.type;
  49. let panelInfo = config.panels[scope.panel.type];
  50. var panelCtrlPromise = Promise.resolve(UnknownPanelCtrl);
  51. if (panelInfo) {
  52. panelCtrlPromise = System.import(panelInfo.module).then(function(panelModule) {
  53. return panelModule.PanelCtrl;
  54. });
  55. }
  56. return panelCtrlPromise.then(function(PanelCtrl: any) {
  57. componentInfo.Component = PanelCtrl;
  58. if (!PanelCtrl || PanelCtrl.registered) {
  59. return componentInfo;
  60. };
  61. if (PanelCtrl.templatePromise) {
  62. return PanelCtrl.templatePromise.then(res => {
  63. return componentInfo;
  64. });
  65. }
  66. PanelCtrl.templatePromise = getTemplate(PanelCtrl).then(template => {
  67. PanelCtrl.templateUrl = null;
  68. PanelCtrl.template = `<grafana-panel ctrl="ctrl">${template}</grafana-panel>`;
  69. return componentInfo;
  70. });
  71. return PanelCtrl.templatePromise;
  72. });
  73. }
  74. function getModule(scope, attrs) {
  75. switch (attrs.type) {
  76. // QueryCtrl
  77. case "query-ctrl": {
  78. let datasource = scope.target.datasource || scope.ctrl.panel.datasource;
  79. return datasourceSrv.get(datasource).then(ds => {
  80. scope.datasource = ds;
  81. return System.import(ds.meta.module).then(dsModule => {
  82. return {
  83. name: 'query-ctrl-' + ds.meta.id,
  84. bindings: {target: "=", panelCtrl: "=", datasource: "="},
  85. attrs: {"target": "target", "panel-ctrl": "ctrl", datasource: "datasource"},
  86. Component: dsModule.QueryCtrl
  87. };
  88. });
  89. });
  90. }
  91. // QueryOptionsCtrl
  92. case "query-options-ctrl": {
  93. return datasourceSrv.get(scope.ctrl.panel.datasource).then(ds => {
  94. return System.import(ds.meta.module).then((dsModule): any => {
  95. if (!dsModule.QueryOptionsCtrl) {
  96. return {notFound: true};
  97. }
  98. return {
  99. name: 'query-options-ctrl-' + ds.meta.id,
  100. bindings: {panelCtrl: "="},
  101. attrs: {"panel-ctrl": "ctrl"},
  102. Component: dsModule.QueryOptionsCtrl
  103. };
  104. });
  105. });
  106. }
  107. // Annotations
  108. case "annotations-query-ctrl": {
  109. return System.import(scope.currentDatasource.meta.module).then(function(dsModule) {
  110. return {
  111. name: 'annotations-query-ctrl-' + scope.currentDatasource.meta.id,
  112. bindings: {annotation: "=", datasource: "="},
  113. attrs: {"annotation": "currentAnnotation", datasource: "currentDatasource"},
  114. Component: dsModule.AnnotationsQueryCtrl,
  115. };
  116. });
  117. }
  118. // ConfigCtrl
  119. case 'datasource-config-ctrl': {
  120. return System.import(scope.datasourceMeta.module).then(function(dsModule) {
  121. return {
  122. name: 'ds-config-' + scope.datasourceMeta.id,
  123. bindings: {meta: "=", current: "="},
  124. attrs: {meta: "datasourceMeta", current: "current"},
  125. Component: dsModule.ConfigCtrl,
  126. };
  127. });
  128. }
  129. // AppConfigCtrl
  130. case 'app-config-ctrl': {
  131. let appModel = scope.ctrl.appModel;
  132. return System.import(appModel.module).then(function(appModule) {
  133. return {
  134. name: 'app-config-' + appModel.appId,
  135. bindings: {appModel: "=", appEditCtrl: "="},
  136. attrs: {"app-model": "ctrl.appModel", "app-edit-ctrl": "ctrl"},
  137. Component: appModule.ConfigCtrl,
  138. };
  139. });
  140. }
  141. // App Page
  142. case 'app-page': {
  143. let appModel = scope.ctrl.appModel;
  144. return System.import(appModel.module).then(function(appModule) {
  145. return {
  146. name: 'app-page-' + appModel.appId + '-' + scope.ctrl.page.slug,
  147. bindings: {appModel: "="},
  148. attrs: {"app-model": "ctrl.appModel"},
  149. Component: appModule[scope.ctrl.page.component],
  150. };
  151. });
  152. }
  153. // Panel
  154. case 'panel': {
  155. return loadPanelComponentInfo(scope, attrs);
  156. }
  157. default: {
  158. return $q.reject({message: "Could not find component type: " + attrs.type });
  159. }
  160. }
  161. }
  162. function appendAndCompile(scope, elem, componentInfo) {
  163. var child = angular.element(document.createElement(componentInfo.name));
  164. _.each(componentInfo.attrs, (value, key) => {
  165. child.attr(key, value);
  166. });
  167. $compile(child)(scope);
  168. elem.empty();
  169. elem.append(child);
  170. }
  171. function registerPluginComponent(scope, elem, attrs, componentInfo) {
  172. if (componentInfo.notFound) {
  173. elem.empty();
  174. return;
  175. }
  176. if (!componentInfo.Component) {
  177. throw {message: 'Failed to find exported plugin component for ' + componentInfo.name};
  178. }
  179. if (!componentInfo.Component.registered) {
  180. var directiveName = attrs.$normalize(componentInfo.name);
  181. var directiveFn = getPluginComponentDirective(componentInfo);
  182. coreModule.directive(directiveName, directiveFn);
  183. componentInfo.Component.registered = true;
  184. }
  185. appendAndCompile(scope, elem, componentInfo);
  186. }
  187. return {
  188. restrict: 'E',
  189. link: function(scope, elem, attrs) {
  190. getModule(scope, attrs).then(function (componentInfo) {
  191. registerPluginComponent(scope, elem, attrs, componentInfo);
  192. }).catch(err => {
  193. $rootScope.appEvent('alert-error', ['Plugin Error', err.message || err]);
  194. console.log('Plugin componnet error', err);
  195. });
  196. }
  197. };
  198. }
  199. coreModule.directive('pluginComponent', pluginDirectiveLoader);