mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
refactor(): dashboard row model and hunting down memory leak
This commit is contained in:
@@ -102,12 +102,7 @@ export class DashboardCtrl {
|
||||
};
|
||||
|
||||
$scope.addRowDefault = function() {
|
||||
$scope.dashboard.rows.push({
|
||||
title: 'New row',
|
||||
panels: [],
|
||||
height: '250px',
|
||||
isNew: true,
|
||||
});
|
||||
$scope.dashboard.addEmptyRow();
|
||||
};
|
||||
|
||||
$scope.showJsonEditor = function(evt, options) {
|
||||
@@ -122,8 +117,9 @@ export class DashboardCtrl {
|
||||
$timeout.cancel(resizeEventTimeout);
|
||||
resizeEventTimeout = $timeout(function() { $scope.$broadcast('render'); }, 200);
|
||||
});
|
||||
$scope.$on('$destroy', function() {
|
||||
var unbind = $scope.$on('$destroy', function() {
|
||||
angular.element(window).unbind('resize');
|
||||
unbind();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -11,8 +11,9 @@ function(angular, $) {
|
||||
|
||||
this.shortcuts = function(scope) {
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
var unbindDestroy = scope.$on('$destroy', function() {
|
||||
keyboardManager.unbindAll();
|
||||
unbindDestroy();
|
||||
});
|
||||
|
||||
var helpModalScope = null;
|
||||
@@ -28,7 +29,11 @@ function(angular, $) {
|
||||
keyboard: false
|
||||
});
|
||||
|
||||
helpModalScope.$on('$destroy', function() { helpModalScope = null; });
|
||||
var unbindModalDestroy = helpModalScope.$on('$destroy', function() {
|
||||
helpModalScope = null;
|
||||
unbindModalDestroy();
|
||||
});
|
||||
|
||||
$q.when(helpModal).then(function(modalEl) { modalEl.modal('show'); });
|
||||
|
||||
}, { inputDisabled: true });
|
||||
|
||||
@@ -6,8 +6,8 @@ import moment from 'moment';
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
|
||||
import {Emitter} from 'app/core/core';
|
||||
import {contextSrv} from 'app/core/services/context_srv';
|
||||
import {Emitter, contextSrv} from 'app/core/core';
|
||||
import {DashboardRow} from './row/row_model';
|
||||
|
||||
export class DashboardModel {
|
||||
id: any;
|
||||
@@ -19,7 +19,7 @@ export class DashboardModel {
|
||||
timezone: any;
|
||||
editable: any;
|
||||
sharedCrosshair: any;
|
||||
rows: any;
|
||||
rows: DashboardRow[];
|
||||
time: any;
|
||||
timepicker: any;
|
||||
templating: any;
|
||||
@@ -51,7 +51,6 @@ export class DashboardModel {
|
||||
this.timezone = data.timezone || '';
|
||||
this.editable = data.editable !== false;
|
||||
this.sharedCrosshair = data.sharedCrosshair || false;
|
||||
this.rows = data.rows || [];
|
||||
this.time = data.time || { from: 'now-6h', to: 'now' };
|
||||
this.timepicker = data.timepicker || {};
|
||||
this.templating = this.ensureListExist(data.templating);
|
||||
@@ -63,10 +62,15 @@ export class DashboardModel {
|
||||
this.links = data.links || [];
|
||||
this.gnetId = data.gnetId || null;
|
||||
|
||||
this.rows = [];
|
||||
if (data.rows) {
|
||||
for (let row of data.rows) {
|
||||
this.rows.push(new DashboardRow(row));
|
||||
}
|
||||
}
|
||||
|
||||
this.updateSchema(data);
|
||||
this.initMeta(meta);
|
||||
|
||||
this.editMode = this.meta.isNew;
|
||||
}
|
||||
|
||||
private initMeta(meta) {
|
||||
@@ -84,6 +88,7 @@ export class DashboardModel {
|
||||
}
|
||||
|
||||
this.meta = meta;
|
||||
this.editMode = this.meta.isNew;
|
||||
}
|
||||
|
||||
// cleans meta data and other non peristent state
|
||||
@@ -91,18 +96,27 @@ export class DashboardModel {
|
||||
// temp remove stuff
|
||||
var events = this.events;
|
||||
var meta = this.meta;
|
||||
var rows = this.rows;
|
||||
delete this.events;
|
||||
delete this.meta;
|
||||
|
||||
// prepare save model
|
||||
this.rows = _.map(this.rows, row => row.getSaveModel());
|
||||
events.emit('prepare-save-model');
|
||||
|
||||
var copy = $.extend(true, {}, this);
|
||||
|
||||
// restore properties
|
||||
this.events = events;
|
||||
this.meta = meta;
|
||||
this.rows = rows;
|
||||
return copy;
|
||||
}
|
||||
|
||||
addEmptyRow() {
|
||||
this.rows.push(new DashboardRow({isNew: true}));
|
||||
}
|
||||
|
||||
private ensureListExist(data) {
|
||||
if (!data) { data = {}; }
|
||||
if (!data.list) { data.list = []; }
|
||||
|
||||
@@ -19,7 +19,7 @@ export class DashRowCtrl {
|
||||
constructor(private $scope, private $rootScope, private $timeout, private uiSegmentSrv, private $q) {
|
||||
this.row.title = this.row.title || 'Row title';
|
||||
|
||||
if (this.dashboard.meta.isNew) {
|
||||
if (this.row.isNew) {
|
||||
this.dropView = 1;
|
||||
delete this.row.isNew;
|
||||
}
|
||||
@@ -200,13 +200,19 @@ coreModule.directive('panelDropZone', function($timeout) {
|
||||
}
|
||||
|
||||
if (indrag === true) {
|
||||
return showPanel(dropZoneSpan, 'Drop Here');
|
||||
var dropZoneSpan = 12 - scope.ctrl.dashboard.rowSpan(scope.ctrl.row);
|
||||
if (dropZoneSpan > 1) {
|
||||
return showPanel(dropZoneSpan, 'Drop Here');
|
||||
}
|
||||
}
|
||||
|
||||
hidePanel();
|
||||
}
|
||||
|
||||
scope.$watchGroup(['ctrl.row.panels.length', 'ctrl.dashboard.editMode', 'ctrl.row.span'], updateState);
|
||||
row.events.on('panel-added', updateState);
|
||||
row.events.on('span-changed', updateState);
|
||||
|
||||
//scope.$watchGroup(['ctrl.row.panels.length', 'ctrl.dashboard.editMode', 'ctrl.row.span'], updateState);
|
||||
|
||||
scope.$on("ANGULAR_DRAG_START", function() {
|
||||
indrag = true;
|
||||
@@ -220,6 +226,7 @@ coreModule.directive('panelDropZone', function($timeout) {
|
||||
});
|
||||
|
||||
scope.$on("ANGULAR_DRAG_END", function() {
|
||||
console.log('drag end');
|
||||
indrag = false;
|
||||
updateState();
|
||||
});
|
||||
|
||||
231
public/app/features/dashboard/row/row_ctrl.ts
Normal file
231
public/app/features/dashboard/row/row_ctrl.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import angular from 'angular';
|
||||
|
||||
import config from 'app/core/config';
|
||||
import {coreModule} from 'app/core/core';
|
||||
|
||||
import './options';
|
||||
import './add_panel';
|
||||
|
||||
export class DashRowCtrl {
|
||||
dashboard: any;
|
||||
row: any;
|
||||
dropView: number;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $scope, private $rootScope, private $timeout, private uiSegmentSrv, private $q) {
|
||||
this.row.title = this.row.title || 'Row title';
|
||||
|
||||
if (this.dashboard.meta.isNew) {
|
||||
this.dropView = 1;
|
||||
delete this.row.isNew;
|
||||
}
|
||||
}
|
||||
|
||||
onDrop(panelId, dropTarget) {
|
||||
var info = this.dashboard.getPanelInfoById(panelId);
|
||||
if (dropTarget) {
|
||||
var dropInfo = this.dashboard.getPanelInfoById(dropTarget.id);
|
||||
dropInfo.row.panels[dropInfo.index] = info.panel;
|
||||
info.row.panels[info.index] = dropTarget;
|
||||
var dragSpan = info.panel.span;
|
||||
info.panel.span = dropTarget.span;
|
||||
dropTarget.span = dragSpan;
|
||||
} else {
|
||||
info.row.panels.splice(info.index, 1);
|
||||
info.panel.span = 12 - this.dashboard.rowSpan(this.row);
|
||||
this.row.panels.push(info.panel);
|
||||
}
|
||||
|
||||
this.$rootScope.$broadcast('render');
|
||||
}
|
||||
|
||||
setHeight(height) {
|
||||
this.row.height = height;
|
||||
this.$scope.$broadcast('render');
|
||||
}
|
||||
|
||||
moveRow(direction) {
|
||||
var rowsList = this.dashboard.rows;
|
||||
var rowIndex = _.indexOf(rowsList, this.row);
|
||||
var newIndex = rowIndex;
|
||||
switch (direction) {
|
||||
case 'up': {
|
||||
newIndex = rowIndex - 1;
|
||||
break;
|
||||
}
|
||||
case 'down': {
|
||||
newIndex = rowIndex + 1;
|
||||
break;
|
||||
}
|
||||
case 'top': {
|
||||
newIndex = 0;
|
||||
break;
|
||||
}
|
||||
case 'bottom': {
|
||||
newIndex = rowsList.length - 1;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
newIndex = rowIndex;
|
||||
}
|
||||
}
|
||||
if (newIndex >= 0 && newIndex <= (rowsList.length - 1)) {
|
||||
_.move(rowsList, rowIndex, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
toggleCollapse() {
|
||||
this.dropView = 0;
|
||||
this.row.collapse = !this.row.collapse;
|
||||
}
|
||||
|
||||
showAddPanel() {
|
||||
this.row.collapse = false;
|
||||
this.dropView = this.dropView === 1 ? 0 : 1;
|
||||
}
|
||||
|
||||
showRowOptions() {
|
||||
this.dropView = this.dropView === 2 ? 0 : 2;
|
||||
}
|
||||
}
|
||||
|
||||
export function rowDirective($rootScope) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
templateUrl: 'public/app/features/dashboard/row/row.html',
|
||||
controller: DashRowCtrl,
|
||||
bindToController: true,
|
||||
controllerAs: 'ctrl',
|
||||
scope: {
|
||||
dashboard: "=",
|
||||
row: "=",
|
||||
},
|
||||
link: function(scope, element) {
|
||||
scope.$watchGroup(['ctrl.row.collapse', 'ctrl.row.height'], function() {
|
||||
element.find('.panels-wrapper').css({minHeight: scope.ctrl.row.collapse ? '5px' : scope.ctrl.row.height});
|
||||
});
|
||||
|
||||
$rootScope.onAppEvent('panel-fullscreen-enter', function(evt, info) {
|
||||
var hasPanel = _.find(scope.ctrl.row.panels, {id: info.panelId});
|
||||
if (!hasPanel) {
|
||||
element.hide();
|
||||
}
|
||||
}, scope);
|
||||
|
||||
$rootScope.onAppEvent('panel-fullscreen-exit', function() {
|
||||
element.show();
|
||||
}, scope);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
coreModule.directive('dashRow', rowDirective);
|
||||
|
||||
|
||||
coreModule.directive('panelWidth', function($rootScope) {
|
||||
return function(scope, element) {
|
||||
var fullscreen = false;
|
||||
|
||||
function updateWidth() {
|
||||
if (!fullscreen) {
|
||||
element[0].style.width = ((scope.panel.span / 1.2) * 10) + '%';
|
||||
}
|
||||
}
|
||||
|
||||
$rootScope.onAppEvent('panel-fullscreen-enter', function(evt, info) {
|
||||
fullscreen = true;
|
||||
|
||||
if (scope.panel.id !== info.panelId) {
|
||||
element.hide();
|
||||
} else {
|
||||
element[0].style.width = '100%';
|
||||
}
|
||||
}, scope);
|
||||
|
||||
$rootScope.onAppEvent('panel-fullscreen-exit', function(evt, info) {
|
||||
fullscreen = false;
|
||||
|
||||
if (scope.panel.id !== info.panelId) {
|
||||
element.show();
|
||||
}
|
||||
|
||||
updateWidth();
|
||||
}, scope);
|
||||
|
||||
scope.$watch('panel.span', updateWidth);
|
||||
|
||||
if (fullscreen) {
|
||||
element.hide();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
coreModule.directive('panelDropZone', function($timeout) {
|
||||
return function(scope, element) {
|
||||
var row = scope.ctrl.row;
|
||||
var indrag = false;
|
||||
var textEl = element.find('.panel-drop-zone-text');
|
||||
|
||||
function showPanel(span, text) {
|
||||
element.find('.panel-container').css('height', row.height);
|
||||
element[0].style.width = ((span / 1.2) * 10) + '%';
|
||||
textEl.text(text);
|
||||
element.show();
|
||||
}
|
||||
|
||||
function hidePanel() {
|
||||
element.hide();
|
||||
// element.removeClass('panel-drop-zone--empty');
|
||||
}
|
||||
|
||||
function updateState() {
|
||||
if (scope.ctrl.dashboard.editMode) {
|
||||
if (row.panels.length === 0 && indrag === false) {
|
||||
return showPanel(12, 'Empty Space');
|
||||
}
|
||||
|
||||
var dropZoneSpan = 12 - scope.ctrl.dashboard.rowSpan(scope.ctrl.row);
|
||||
if (dropZoneSpan > 1) {
|
||||
if (indrag) {
|
||||
return showPanel(dropZoneSpan, 'Drop Here');
|
||||
} else {
|
||||
return showPanel(dropZoneSpan, 'Empty Space');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (indrag === true) {
|
||||
return showPanel(dropZoneSpan, 'Drop Here');
|
||||
}
|
||||
|
||||
hidePanel();
|
||||
}
|
||||
|
||||
scope.row.events.on('panel-added', updateState);
|
||||
scope.row.events.on('span-changed', updateState);
|
||||
|
||||
scope.$watchGroup(['ctrl.row.panels.length', 'ctrl.dashboard.editMode', 'ctrl.row.span'], updateState);
|
||||
|
||||
scope.$on("ANGULAR_DRAG_START", function() {
|
||||
indrag = true;
|
||||
updateState();
|
||||
// $timeout(function() {
|
||||
// var dropZoneSpan = 12 - scope.ctrl.dashboard.rowSpan(scope.ctrl.row);
|
||||
// if (dropZoneSpan > 0) {
|
||||
// showPanel(dropZoneSpan, 'Panel Drop Zone');
|
||||
// }
|
||||
// });
|
||||
});
|
||||
|
||||
scope.$on("ANGULAR_DRAG_END", function() {
|
||||
indrag = false;
|
||||
updateState();
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
32
public/app/features/dashboard/row/row_model.ts
Normal file
32
public/app/features/dashboard/row/row_model.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
///<reference path="../../../headers/common.d.ts" />
|
||||
|
||||
import {Emitter, contextSrv} from 'app/core/core';
|
||||
import {assignModelProperties} from 'app/core/core';
|
||||
|
||||
export class DashboardRow {
|
||||
panels: any;
|
||||
title: any;
|
||||
showTitle: any;
|
||||
titleSize: any;
|
||||
events: Emitter;
|
||||
|
||||
defaults = {
|
||||
title: 'Dashboard Row',
|
||||
panels: [],
|
||||
showTitle: false,
|
||||
titleSize: 'h6',
|
||||
height: 250,
|
||||
isNew: false,
|
||||
};
|
||||
|
||||
constructor(private model) {
|
||||
assignModelProperties(this, model, this.defaults);
|
||||
this.events = new Emitter();
|
||||
}
|
||||
|
||||
getSaveModel() {
|
||||
assignModelProperties(this.model, this, this.defaults);
|
||||
return this.model;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ function(angular, _) {
|
||||
dash.time = 0;
|
||||
dash.refresh = 0;
|
||||
dash.schemaVersion = 0;
|
||||
dash.editMode = false;
|
||||
|
||||
// filter row and panels properties that should be ignored
|
||||
dash.rows = _.filter(dash.rows, function(row) {
|
||||
|
||||
@@ -199,8 +199,9 @@ function (angular, _, $) {
|
||||
}
|
||||
}
|
||||
|
||||
panelScope.$on('$destroy', function() {
|
||||
var unbind = panelScope.$on('$destroy', function() {
|
||||
self.panelScopes = _.without(self.panelScopes, panelScope);
|
||||
unbind();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user