refactor(): dashboard row model and hunting down memory leak

This commit is contained in:
Torkel Ödegaard
2016-10-30 15:14:18 +01:00
parent 57cbefdf0a
commit 4d420a0c33
20 changed files with 364 additions and 45 deletions

View File

@@ -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();
});
};

View File

@@ -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 });

View File

@@ -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 = []; }

View File

@@ -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();
});

View 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();
});
};
});

View 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;
}
}

View File

@@ -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) {

View File

@@ -199,8 +199,9 @@ function (angular, _, $) {
}
}
panelScope.$on('$destroy', function() {
var unbind = panelScope.$on('$destroy', function() {
self.panelScopes = _.without(self.panelScopes, panelScope);
unbind();
});
};