diff --git a/conf/defaults.ini b/conf/defaults.ini
index a852dee4e75..1f48c9718d2 100644
--- a/conf/defaults.ini
+++ b/conf/defaults.ini
@@ -292,6 +292,9 @@ content_security_policy = false
# $ROOT_PATH is server.root_url without the protocol.
content_security_policy_template = """script-src 'self' 'unsafe-eval' 'unsafe-inline' 'strict-dynamic' $NONCE;object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline' blob:;img-src * data:;base-uri 'self';connect-src 'self' grafana.com ws://$ROOT_PATH wss://$ROOT_PATH;manifest-src 'self';media-src 'none';form-action 'self';"""
+# Controls if old angular plugins are supported or not. This will be disabled by default in Grafana v9.
+angular_support_enabled = true
+
#################################### Snapshots ###########################
[snapshots]
# snapshot sharing options
diff --git a/conf/sample.ini b/conf/sample.ini
index 710a8ea4810..759fd56af35 100644
--- a/conf/sample.ini
+++ b/conf/sample.ini
@@ -292,6 +292,9 @@
# $ROOT_PATH is server.root_url without the protocol.
;content_security_policy_template = """script-src 'self' 'unsafe-eval' 'unsafe-inline' 'strict-dynamic' $NONCE;object-src 'none';font-src 'self';style-src 'self' 'unsafe-inline' blob:;img-src * data:;base-uri 'self';connect-src 'self' grafana.com ws://$ROOT_PATH wss://$ROOT_PATH;manifest-src 'self';media-src 'none';form-action 'self';"""
+# Controls if old angular plugins are supported or not. This will be disabled by default in Grafana v9.
+;angular_support_enabled = true
+
#################################### Snapshots ###########################
[snapshots]
# snapshot sharing options
diff --git a/docs/sources/administration/configuration.md b/docs/sources/administration/configuration.md
index ecdc40b5386..1600c0420b5 100644
--- a/docs/sources/administration/configuration.md
+++ b/docs/sources/administration/configuration.md
@@ -582,6 +582,21 @@ Set Content Security Policy template used when adding the Content-Security-Polic
+### angular_support_enabled
+
+This currently defaults to `true` but will in Grafana v9 default to `false`. When set to false the angular framework and support components will not be loaded. This means that
+all plugins and core features that depend on angular support will stop working.
+
+Current core features that will stop working:
+
+- Heatmap panel
+- Old graph panel
+- Old table panel
+- Postgres, MySQL and MSSQL data source query editors
+- Legacy alerting edit rule UI
+
+Before we disable angular support by default we plan to migrate these remaining areas to React.
+
## [snapshots]
### external_enabled
diff --git a/packages/grafana-data/src/types/config.ts b/packages/grafana-data/src/types/config.ts
index c07e212e9cd..f92c856c223 100644
--- a/packages/grafana-data/src/types/config.ts
+++ b/packages/grafana-data/src/types/config.ts
@@ -138,4 +138,5 @@ export interface GrafanaConfig {
geomapDefaultBaseLayer?: MapLayerOptions;
geomapDisableCustomBaseLayer?: boolean;
unifiedAlertingEnabled: boolean;
+ angularSupportEnabled: boolean;
}
diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts
index d9258c84c71..4cf1813b984 100644
--- a/packages/grafana-runtime/src/config.ts
+++ b/packages/grafana-runtime/src/config.ts
@@ -41,6 +41,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
alertingErrorOrTimeout = '';
alertingNoDataOrNullValues = '';
alertingMinInterval = 1;
+ angularSupportEnabled = false;
authProxyEnabled = false;
exploreEnabled = false;
ldapEnabled = false;
diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go
index f1ea4edaef1..f59fe8ffbeb 100644
--- a/pkg/api/frontendsettings.go
+++ b/pkg/api/frontendsettings.go
@@ -229,6 +229,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
"externalUserMngLinkUrl": setting.ExternalUserMngLinkUrl,
"externalUserMngLinkName": setting.ExternalUserMngLinkName,
"viewersCanEdit": setting.ViewersCanEdit,
+ "angularSupportEnabled": hs.Cfg.AngularSupportEnabled,
"editorsCanAdmin": hs.Cfg.EditorsCanAdmin,
"disableSanitizeHtml": hs.Cfg.DisableSanitizeHtml,
"pluginsToPreload": pluginsToPreload,
diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go
index b4999997dd3..51639e6748c 100644
--- a/pkg/setting/setting.go
+++ b/pkg/setting/setting.go
@@ -254,7 +254,8 @@ type Cfg struct {
// CSPEnabled toggles Content Security Policy support.
CSPEnabled bool
// CSPTemplate contains the Content Security Policy template.
- CSPTemplate string
+ CSPTemplate string
+ AngularSupportEnabled bool
TempDataLifetime time.Duration
PluginsEnableAlpha bool
@@ -1191,6 +1192,7 @@ func readSecuritySettings(iniFile *ini.File, cfg *Cfg) error {
cfg.StrictTransportSecuritySubDomains = security.Key("strict_transport_security_subdomains").MustBool(false)
cfg.CSPEnabled = security.Key("content_security_policy").MustBool(false)
cfg.CSPTemplate = security.Key("content_security_policy_template").MustString("")
+ cfg.AngularSupportEnabled = security.Key("angular_support_enabled").MustBool(true)
// read data source proxy whitelist
DataProxyWhiteList = make(map[string]bool)
diff --git a/public/app/AppWrapper.tsx b/public/app/AppWrapper.tsx
index 4bb5b60ce7f..72eecdee141 100644
--- a/public/app/AppWrapper.tsx
+++ b/public/app/AppWrapper.tsx
@@ -15,15 +15,16 @@ import { GrafanaRoute } from './core/navigation/GrafanaRoute';
import { AppNotificationList } from './core/components/AppNotifications/AppNotificationList';
import { SearchWrapper } from 'app/features/search';
import { LiveConnectionWarning } from './features/live/LiveConnectionWarning';
-import { AngularRoot } from './angular/AngularRoot';
import { I18nProvider } from './core/localisation';
+import { AngularRoot } from './angular/AngularRoot';
+import { loadAndInitAngularIfEnabled } from './angular/loadAndInitAngularIfEnabled';
interface AppWrapperProps {
app: GrafanaApp;
}
interface AppWrapperState {
- ngInjector: any;
+ ready?: boolean;
}
/** Used by enterprise */
@@ -39,27 +40,14 @@ export function addPageBanner(fn: ComponentType) {
}
export class AppWrapper extends React.Component {
- container = React.createRef();
-
constructor(props: AppWrapperProps) {
super(props);
-
- this.state = {
- ngInjector: null,
- };
+ this.state = {};
}
- componentDidMount() {
- if (this.container) {
- this.bootstrapNgApp();
- } else {
- throw new Error('Failed to boot angular app, no container to attach to');
- }
- }
-
- bootstrapNgApp() {
- const injector = this.props.app.angularApp.bootstrap();
- this.setState({ ngInjector: injector });
+ async componentDidMount() {
+ await loadAndInitAngularIfEnabled();
+ this.setState({ ready: true });
$('.preloader').remove();
}
@@ -91,6 +79,8 @@ export class AppWrapper extends React.Component
))}
-
+
- {this.state.ngInjector && this.renderRoutes()}
+ {ready && this.renderRoutes()}
{bodyRenderHooks.map((Hook, index) => (
))}
diff --git a/public/app/angular/AngularApp.ts b/public/app/angular/AngularApp.ts
index a746e355e1b..29977589ea9 100644
--- a/public/app/angular/AngularApp.ts
+++ b/public/app/angular/AngularApp.ts
@@ -14,6 +14,11 @@ import { extend } from 'lodash';
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { getTemplateSrv } from '@grafana/runtime';
import { registerComponents } from './registerComponents';
+import { exposeToPlugin } from 'app/features/plugins/plugin_loader';
+import appEvents from 'app/core/app_events';
+import { contextSrv } from 'app/core/services/context_srv';
+import * as sdk from 'app/plugins/sdk';
+import { promiseToDigest } from './promiseToDigest';
export class AngularApp {
ngModuleDependencies: any[];
@@ -93,6 +98,18 @@ export class AngularApp {
registerComponents();
initAngularRoutingBridge();
+ // Angular plugins import this
+ exposeToPlugin('angular', angular);
+ exposeToPlugin('app/core/utils/promiseToDigest', { promiseToDigest, __esModule: true });
+ exposeToPlugin('app/plugins/sdk', sdk);
+ exposeToPlugin('app/core/core_module', coreModule);
+ exposeToPlugin('app/core/core', {
+ coreModule: coreModule,
+ appEvents: appEvents,
+ contextSrv: contextSrv,
+ __esModule: true,
+ });
+
// disable tool tip animation
$.fn.tooltip.defaults.animation = false;
}
diff --git a/public/app/angular/index.ts b/public/app/angular/index.ts
index 2dcaee03cab..ada6fe8f6a5 100644
--- a/public/app/angular/index.ts
+++ b/public/app/angular/index.ts
@@ -9,6 +9,7 @@ import './services/popover_srv';
import './services/timer';
import './services/AngularLoader';
+import '../angular/jquery_extended';
import './dropdown_typeahead';
import './autofill_event_fix';
import './metric_segment';
@@ -37,3 +38,4 @@ import './components/plugin_component';
import './GrafanaCtrl';
export { AngularApp } from './AngularApp';
+export { coreModule } from './core_module';
diff --git a/public/app/core/jquery_extended.ts b/public/app/angular/jquery_extended.ts
similarity index 100%
rename from public/app/core/jquery_extended.ts
rename to public/app/angular/jquery_extended.ts
diff --git a/public/app/angular/loadAndInitAngularIfEnabled.ts b/public/app/angular/loadAndInitAngularIfEnabled.ts
new file mode 100644
index 00000000000..211f44ae3b8
--- /dev/null
+++ b/public/app/angular/loadAndInitAngularIfEnabled.ts
@@ -0,0 +1,22 @@
+import { config, setAngularLoader } from '@grafana/runtime';
+
+export async function loadAndInitAngularIfEnabled() {
+ if (config.angularSupportEnabled) {
+ const { AngularApp } = await import(/* webpackChunkName: "AngularApp" */ './index');
+ const app = new AngularApp();
+ app.init();
+ app.bootstrap();
+ } else {
+ setAngularLoader({
+ load: (elem, scopeProps, template) => {
+ return {
+ destroy: () => {},
+ digest: () => {},
+ getScope: () => {
+ return {};
+ },
+ };
+ },
+ });
+ }
+}
diff --git a/public/app/app.ts b/public/app/app.ts
index 7f5bcb17836..48167320b11 100644
--- a/public/app/app.ts
+++ b/public/app/app.ts
@@ -62,7 +62,6 @@ import { setPanelRenderer } from '@grafana/runtime/src/components/PanelRenderer'
import { PanelDataErrorView } from './features/panel/components/PanelDataErrorView';
import { setPanelDataErrorView } from '@grafana/runtime/src/components/PanelDataErrorView';
import { DatasourceSrv } from './features/plugins/datasource_srv';
-import { AngularApp } from './angular';
import { ModalManager } from './core/services/ModalManager';
import { initWindowRuntime } from './features/runtime/init';
import { createQueryVariableAdapter } from './features/variables/query/adapter';
@@ -89,12 +88,6 @@ if (process.env.NODE_ENV === 'development') {
}
export class GrafanaApp {
- angularApp: AngularApp;
-
- constructor() {
- this.angularApp = new AngularApp();
- }
-
async init() {
try {
setBackendSrv(backendSrv);
@@ -148,9 +141,6 @@ export class GrafanaApp {
const modalManager = new ModalManager();
modalManager.init();
- // Init angular
- this.angularApp.init();
-
// Preload selected app plugins
await preloadPlugins(config.pluginsToPreload);
diff --git a/public/app/core/core.ts b/public/app/core/core.ts
index 04cf6dbbbd8..38e3808619d 100644
--- a/public/app/core/core.ts
+++ b/public/app/core/core.ts
@@ -1,5 +1,3 @@
-import './jquery_extended';
-import './services/search_srv';
import { colors, JsonExplorer } from '@grafana/ui/';
import appEvents from './app_events';
import { assignModelProperties } from './utils/model_utils';
diff --git a/public/app/features/alerting/AlertTab.tsx b/public/app/features/alerting/AlertTab.tsx
index 3348293a19e..2c75d68fc49 100644
--- a/public/app/features/alerting/AlertTab.tsx
+++ b/public/app/features/alerting/AlertTab.tsx
@@ -2,13 +2,10 @@ import React, { PureComponent } from 'react';
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux';
import { Alert, Button, ConfirmModal, Container, CustomScrollbar, HorizontalGroup, IconName, Modal } from '@grafana/ui';
import { selectors } from '@grafana/e2e-selectors';
-import { AngularComponent, getAngularLoader, getDataSourceSrv } from '@grafana/runtime';
+import { AngularComponent, config, getAngularLoader, getDataSourceSrv } from '@grafana/runtime';
import { getAlertingValidationMessage } from './getAlertingValidationMessage';
-
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
import StateHistory from './StateHistory';
-import 'app/features/alerting/AlertTabCtrl';
-
import { DashboardModel } from '../dashboard/state/DashboardModel';
import { PanelModel } from '../dashboard/state/PanelModel';
import { TestRuleResult } from './TestRuleResult';
@@ -57,8 +54,14 @@ class UnConnectedAlertTab extends PureComponent {
showTestRule: false,
};
- componentDidMount() {
- this.loadAlertTab();
+ async componentDidMount() {
+ if (config.angularSupportEnabled) {
+ await import(/* webpackChunkName: "AlertTabCtrl" */ 'app/features/alerting/AlertTabCtrl');
+ this.loadAlertTab();
+ } else {
+ // TODO probably need to migrate AlertTab to react
+ alert('Angular support disabled, legacy alerting cannot function without angular support');
+ }
}
onAngularPanelUpdated = () => {
diff --git a/public/app/features/alerting/unified/utils/receiver-form.ts b/public/app/features/alerting/unified/utils/receiver-form.ts
index 801e5bfc997..f4aa3886fa6 100644
--- a/public/app/features/alerting/unified/utils/receiver-form.ts
+++ b/public/app/features/alerting/unified/utils/receiver-form.ts
@@ -1,4 +1,3 @@
-import { isArray } from 'angular';
import {
AlertManagerCortexConfig,
GrafanaManagedReceiverConfig,
@@ -6,6 +5,7 @@ import {
Route,
} from 'app/plugins/datasource/alertmanager/types';
import { CloudNotifierType, NotifierDTO, NotifierType } from 'app/types';
+import { isArray } from 'lodash';
import {
CloudChannelConfig,
CloudChannelMap,
diff --git a/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx b/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx
index 24095f3320d..1a273bab2e4 100644
--- a/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx
+++ b/public/app/features/dashboard/components/DashboardPrompt/DashboardPrompt.tsx
@@ -5,7 +5,6 @@ import React, { useEffect, useState } from 'react';
import { Prompt } from 'react-router-dom';
import { DashboardModel } from '../../state/DashboardModel';
import { each, filter, find } from 'lodash';
-import angular from 'angular';
import { UnsavedChangesModal } from '../SaveDashboard/UnsavedChangesModal';
import * as H from 'history';
import { SaveLibraryPanelModal } from 'app/features/library-panels/components/SaveLibraryPanelModal/SaveLibraryPanelModal';
@@ -248,8 +247,8 @@ export function hasChanges(current: DashboardModel, original: any) {
currentTimepicker.now = originalTimepicker.now;
}
- const currentJson = angular.toJson(currentClean);
- const originalJson = angular.toJson(originalClean);
+ const currentJson = JSON.stringify(currentClean, null);
+ const originalJson = JSON.stringify(originalClean, null);
return currentJson !== originalJson;
}
diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts
index 66cfd1d6bb0..c542aa7dbb4 100644
--- a/public/app/features/plugins/built_in_plugins.ts
+++ b/public/app/features/plugins/built_in_plugins.ts
@@ -45,15 +45,12 @@ import * as timeseriesPanel from 'app/plugins/panel/timeseries/module';
import * as stateTimelinePanel from 'app/plugins/panel/state-timeline/module';
import * as statusHistoryPanel from 'app/plugins/panel/status-history/module';
import * as candlestickPanel from 'app/plugins/panel/candlestick/module';
-import * as graphPanel from 'app/plugins/panel/graph/module';
import * as xyChartPanel from 'app/plugins/panel/xychart/module';
import * as dashListPanel from 'app/plugins/panel/dashlist/module';
import * as pluginsListPanel from 'app/plugins/panel/pluginlist/module';
import * as alertListPanel from 'app/plugins/panel/alertlist/module';
import * as annoListPanel from 'app/plugins/panel/annolist/module';
-import * as heatmapPanel from 'app/plugins/panel/heatmap/module';
import * as tablePanel from 'app/plugins/panel/table/module';
-import * as oldTablePanel from 'app/plugins/panel/table-old/module';
import * as statPanel from 'app/plugins/panel/stat/module';
import * as gettingStartedPanel from 'app/plugins/panel/gettingstarted/module';
import * as gaugePanel from 'app/plugins/panel/gauge/module';
@@ -73,6 +70,11 @@ import * as alertGroupsPanel from 'app/plugins/panel/alertGroups/module';
const geomapPanel = async () => await import(/* webpackChunkName: "geomapPanel" */ 'app/plugins/panel/geomap/module');
const canvasPanel = async () => await import(/* webpackChunkName: "canvasPanel" */ 'app/plugins/panel/canvas/module');
const iconPanel = async () => await import(/* webpackChunkName: "iconPanel" */ 'app/plugins/panel/icon/module');
+const graphPanel = async () => await import(/* webpackChunkName: "graphPlugin" */ 'app/plugins/panel/graph/module');
+const heatmapPanel = async () =>
+ await import(/* webpackChunkName: "heatmapPlugin" */ 'app/plugins/panel/heatmap/module');
+const tableOldPanel = async () =>
+ await import(/* webpackChunkName: "tableOldPlugin" */ 'app/plugins/panel/table-old/module');
const builtInPlugins: any = {
'app/plugins/datasource/graphite/module': graphitePlugin,
@@ -112,7 +114,7 @@ const builtInPlugins: any = {
'app/plugins/panel/annolist/module': annoListPanel,
'app/plugins/panel/heatmap/module': heatmapPanel,
'app/plugins/panel/table/module': tablePanel,
- 'app/plugins/panel/table-old/module': oldTablePanel,
+ 'app/plugins/panel/table-old/module': tableOldPanel,
'app/plugins/panel/news/module': newsPanel,
'app/plugins/panel/live/module': livePanel,
'app/plugins/panel/stat/module': statPanel,
diff --git a/public/app/features/plugins/importPanelPlugin.ts b/public/app/features/plugins/importPanelPlugin.ts
index ccb3bdf9251..40d4dc3549d 100644
--- a/public/app/features/plugins/importPanelPlugin.ts
+++ b/public/app/features/plugins/importPanelPlugin.ts
@@ -28,25 +28,22 @@ export function importPanelPluginFromMeta(meta: grafanaData.PanelPluginMeta): Pr
return getPanelPlugin(meta);
}
-function getPanelPlugin(meta: grafanaData.PanelPluginMeta): Promise {
- return importPluginModule(meta.module, meta.info?.version)
- .then((pluginExports) => {
- if (pluginExports.plugin) {
- return pluginExports.plugin as grafanaData.PanelPlugin;
- } else if (pluginExports.PanelCtrl) {
- const plugin = new grafanaData.PanelPlugin(null);
- plugin.angularPanelCtrl = pluginExports.PanelCtrl;
- return plugin;
- }
- throw new Error('missing export: plugin or PanelCtrl');
- })
- .then((plugin) => {
- plugin.meta = meta;
- return plugin;
- })
- .catch((err) => {
- // TODO, maybe a different error plugin
- console.warn('Error loading panel plugin: ' + meta.id, err);
- return getPanelPluginLoadError(meta, err);
- });
+async function getPanelPlugin(meta: grafanaData.PanelPluginMeta): Promise {
+ try {
+ const pluginExports = await importPluginModule(meta.module, meta.info?.version);
+ let plugin = pluginExports.plugin;
+
+ if (!plugin && pluginExports.PanelCtrl) {
+ plugin = new grafanaData.PanelPlugin(null);
+ plugin.angularPanelCtrl = pluginExports.PanelCtrl;
+ }
+
+ plugin.meta = meta;
+
+ return plugin;
+ } catch (err) {
+ // TODO, maybe a different error plugin
+ console.warn('Error loading panel plugin: ' + meta.id, err);
+ return getPanelPluginLoadError(meta, err);
+ }
}
diff --git a/public/app/features/plugins/plugin_loader.ts b/public/app/features/plugins/plugin_loader.ts
index 8bd0ded2976..831a5947762 100644
--- a/public/app/features/plugins/plugin_loader.ts
+++ b/public/app/features/plugins/plugin_loader.ts
@@ -1,9 +1,7 @@
// eslint-disable-next-line lodash/import-scope
import _ from 'lodash';
-import * as sdk from 'app/plugins/sdk';
import kbn from 'app/core/utils/kbn';
import moment from 'moment'; // eslint-disable-line no-restricted-imports
-import angular from 'angular';
import jquery from 'jquery';
// Experimental module exports
@@ -21,12 +19,10 @@ import * as redux from 'redux';
import config from 'app/core/config';
import TimeSeries from 'app/core/time_series2';
import TableModel from 'app/core/table_model';
-import { coreModule } from 'app/angular/core_module';
import { appEvents, contextSrv } from 'app/core/core';
import * as flatten from 'app/core/utils/flatten';
import * as ticks from 'app/core/utils/ticks';
import { BackendSrv, getBackendSrv } from 'app/core/services/backend_srv';
-import { promiseToDigest } from 'app/angular/promiseToDigest';
import impressionSrv from 'app/core/services/impression_srv';
import builtInPlugins from './built_in_plugins';
import * as d3 from 'd3';
@@ -75,7 +71,7 @@ grafanaRuntime.SystemJS.config({
},
});
-function exposeToPlugin(name: string, component: any) {
+export function exposeToPlugin(name: string, component: any) {
grafanaRuntime.SystemJS.registerDynamic(name, [], true, (require: any, exports: any, module: { exports: any }) => {
module.exports = component;
});
@@ -87,7 +83,6 @@ exposeToPlugin('@grafana/runtime', grafanaRuntime);
exposeToPlugin('lodash', _);
exposeToPlugin('moment', moment);
exposeToPlugin('jquery', jquery);
-exposeToPlugin('angular', angular);
exposeToPlugin('d3', d3);
exposeToPlugin('rxjs', rxjs);
exposeToPlugin('rxjs/operators', rxjsOperators);
@@ -120,24 +115,16 @@ exposeToPlugin('app/core/services/backend_srv', {
getBackendSrv,
});
-exposeToPlugin('app/plugins/sdk', sdk);
exposeToPlugin('app/core/utils/datemath', grafanaData.dateMath);
exposeToPlugin('app/core/utils/flatten', flatten);
exposeToPlugin('app/core/utils/kbn', kbn);
exposeToPlugin('app/core/utils/ticks', ticks);
-exposeToPlugin('app/core/utils/promiseToDigest', {
- promiseToDigest: promiseToDigest,
- __esModule: true,
-});
-
exposeToPlugin('app/core/config', config);
exposeToPlugin('app/core/time_series', TimeSeries);
exposeToPlugin('app/core/time_series2', TimeSeries);
exposeToPlugin('app/core/table_model', TableModel);
exposeToPlugin('app/core/app_events', appEvents);
-exposeToPlugin('app/core/core_module', coreModule);
exposeToPlugin('app/core/core', {
- coreModule: coreModule,
appEvents: appEvents,
contextSrv: contextSrv,
__esModule: true,
diff --git a/public/app/features/variables/state/actions.ts b/public/app/features/variables/state/actions.ts
index 4a32665f3d6..902fbba742e 100644
--- a/public/app/features/variables/state/actions.ts
+++ b/public/app/features/variables/state/actions.ts
@@ -1,4 +1,3 @@
-import angular from 'angular';
import { castArray, isEqual } from 'lodash';
import {
DataQuery,
@@ -601,7 +600,7 @@ const timeRangeUpdated =
const updatedVariable = getVariable(identifier.id, getState());
const updatedOptions = updatedVariable.options;
- if (angular.toJson(previousOptions) !== angular.toJson(updatedOptions)) {
+ if (JSON.stringify(previousOptions) !== JSON.stringify(updatedOptions)) {
const dashboard = getState().dashboard.getModel();
dashboard?.templateVariableValueUpdated();
}
diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/query_ctrl.ts b/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/query_ctrl.ts
index f8885924173..7ae8bca81c0 100644
--- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/query_ctrl.ts
+++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/__mocks__/query_ctrl.ts
@@ -1,5 +1,3 @@
-import { auto } from 'angular';
-
export class QueryCtrl {
target: any;
datasource: any;
@@ -8,7 +6,7 @@ export class QueryCtrl {
hasRawMode = false;
error = '';
- constructor(public $scope: any, _$injector: auto.IInjectorService) {
+ constructor(public $scope: any) {
this.panelCtrl = this.panelCtrl || { panel: {} };
this.target = this.target || { target: '' };
this.panel = this.panelCtrl.panel;
diff --git a/public/app/types/angular.ts b/public/app/types/angular.ts
deleted file mode 100644
index 05af2828765..00000000000
--- a/public/app/types/angular.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { IScope } from 'angular';
-
-export interface Scope extends IScope {
- [key: string]: any;
-}
diff --git a/public/app/types/index.ts b/public/app/types/index.ts
index 275bafc09af..3ffade53998 100644
--- a/public/app/types/index.ts
+++ b/public/app/types/index.ts
@@ -15,7 +15,6 @@ export * from './explore';
export * from './store';
export * from './ldap';
export * from './appEvent';
-export * from './angular';
export * from './query';
export * from './preferences';
export * from './accessControl';