Skip to content

Commit 840c9c3

Browse files
committed
Refactor angular-drag-drop.service to improve typings and replace service with factory
1 parent 91e3c3b commit 840c9c3

2 files changed

Lines changed: 104 additions & 88 deletions

File tree

zeppelin-web-angular/src/app/services/angular-drag-drop.service.ts

Lines changed: 104 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,27 @@
1212

1313
import { Injectable } from '@angular/core';
1414
import * as angular from 'angular';
15+
import * as JQuery from 'jquery';
16+
17+
interface CustomDragDropService {
18+
draggableScope: angular.IScope | null;
19+
dragData: unknown | null;
20+
dragIndex: number | null;
21+
dragModelPath: null | string;
22+
dragSettings: { placeholder?: string } | null;
23+
draggableElement: HTMLElement | null;
24+
25+
callEventCallback(scope: angular.IScope, callbackName: string, event: DragEvent, ui?: UI): void;
26+
updateModel(scope: angular.IScope, modelPath: string, newValue: unknown, index?: number): void;
27+
removeFromModel(scope: angular.IScope, modelPath: string, index?: number): void;
28+
}
29+
30+
interface UI {
31+
draggable: JQuery<HTMLElement> | null;
32+
helper: null;
33+
position: { top: number; left: number };
34+
offset: { top: number; left: number };
35+
}
1536

1637
@Injectable({
1738
providedIn: 'root'
@@ -21,97 +42,97 @@ export class AngularDragDropService {
2142

2243
addDragDropDirectives(module: angular.IModule): void {
2344
// Drag and drop service to maintain state across directives
24-
// tslint:disable-next-line:no-any
25-
module.service('customDragDropService', [
45+
module.factory('customDragDropService', [
2646
'$parse',
27-
// tslint:disable-next-line:no-any
28-
function($parse: any) {
29-
this.draggableScope = null;
30-
this.dragData = null;
31-
this.dragIndex = null;
32-
this.dragModelPath = null;
33-
this.dragSettings = null;
34-
this.draggableElement = null;
35-
36-
// tslint:disable-next-line:no-any
37-
this.callEventCallback = function(scope: any, callbackName: string, event: any, ui?: any) {
38-
if (!callbackName) {
39-
return;
40-
}
41-
42-
const objExtract = extract(callbackName);
43-
const callback = objExtract.callback;
44-
const constructor = objExtract.constructor;
45-
const args = [event, ui].concat(objExtract.args);
47+
function($parse: angular.IParseService): CustomDragDropService {
48+
return {
49+
draggableScope: null,
50+
dragData: null,
51+
dragIndex: null,
52+
dragModelPath: null,
53+
dragSettings: null,
54+
draggableElement: null,
55+
56+
callEventCallback: function(scope, callbackStr, event, ui) {
57+
if (!callbackStr) {
58+
return;
59+
}
4660

47-
// call either $scope method or constructor's method
48-
const targetScope = scope[callback] ? scope : scope[constructor];
49-
const targetCallback = scope[callback] || scope[constructor][callback];
61+
const { targetCallback, targetScope, args: extractedArgs } = extract(callbackStr);
62+
const fullArgs = [event, ui].concat(extractedArgs);
5063

51-
if (typeof targetCallback === 'function') {
52-
return targetCallback.apply(targetScope, args);
53-
}
64+
if (typeof targetCallback === 'function') {
65+
return targetCallback.apply(targetScope, fullArgs);
66+
}
5467

55-
function extract(_callbackName: string) {
56-
const atStartBracket =
57-
_callbackName.indexOf('(') !== -1 ? _callbackName.indexOf('(') : _callbackName.length;
58-
const atEndBracket =
59-
_callbackName.lastIndexOf(')') !== -1 ? _callbackName.lastIndexOf(')') : _callbackName.length;
60-
const argsString = _callbackName.substring(atStartBracket + 1, atEndBracket);
61-
let _constructor =
62-
_callbackName.indexOf('.') !== -1 ? _callbackName.substr(0, _callbackName.indexOf('.')) : null;
63-
_constructor =
64-
scope[_constructor] && typeof scope[_constructor].constructor === 'function' ? _constructor : null;
65-
66-
return {
67-
callback: _callbackName.substring((_constructor && _constructor.length + 1) || 0, atStartBracket),
68-
// tslint:disable-next-line:no-any
69-
args: argsString ? argsString.split(',').map((item: string) => $parse(item.trim())(scope)) : [],
70-
constructor: _constructor
71-
};
72-
}
73-
};
68+
function extract(_callbackStr: string) {
69+
const atStartBracket = _callbackStr.indexOf('(') !== -1 ? _callbackStr.indexOf('(') : _callbackStr.length;
70+
const atEndBracket =
71+
_callbackStr.lastIndexOf(')') !== -1 ? _callbackStr.lastIndexOf(')') : _callbackStr.length;
72+
73+
const argsString = _callbackStr.substring(atStartBracket + 1, atEndBracket);
74+
const identifierTokens = argsString ? argsString.split(',') : [];
75+
const args = identifierTokens.map(item => $parse(item.trim())(scope));
76+
77+
const constructorName =
78+
_callbackStr.indexOf('.') !== -1 ? _callbackStr.substr(0, _callbackStr.indexOf('.')) : null;
79+
// @ts-ignore
80+
const constructorCandid = constructorName && scope[constructorName];
81+
const constructor =
82+
constructorCandid && typeof constructorCandid.constructor === 'function' ? constructorCandid : null;
83+
84+
const callbackName = _callbackStr.substring((constructor && constructor.length + 1) || 0, atStartBracket);
85+
// @ts-ignore
86+
const callbackCandid = scope[callbackName];
87+
// If the expression is a method call, then the parsed constructor becomes its bound scope.
88+
const _scope = callbackCandid ? scope : constructor;
89+
const callback = callbackCandid || constructor[callbackName];
90+
91+
return {
92+
args,
93+
targetScope: _scope,
94+
targetCallback: callback
95+
};
96+
}
97+
},
7498

75-
// tslint:disable-next-line:no-any
76-
this.updateModel = function(scope: any, modelPath: string, newValue: any, index?: number) {
77-
const getter = $parse(modelPath);
78-
const setter = getter.assign;
79-
const modelValue = getter(scope);
99+
updateModel: function(scope, modelPath, newValue, index) {
100+
const getter = $parse(modelPath);
101+
const setter = getter.assign;
102+
const modelValue = getter(scope);
80103

81-
if (angular.isArray(modelValue)) {
82-
if (typeof index === 'number') {
83-
modelValue[index] = newValue;
104+
if (angular.isArray(modelValue)) {
105+
if (typeof index === 'number') {
106+
modelValue[index] = newValue;
107+
} else {
108+
modelValue.push(newValue);
109+
}
84110
} else {
85-
modelValue.push(newValue);
86-
}
87-
} else {
88-
if (setter) {
89-
setter(scope, newValue);
111+
if (setter) {
112+
setter(scope, newValue);
113+
}
90114
}
91-
}
92115

93-
scope.$apply();
94-
};
116+
scope.$apply();
117+
},
95118

96-
// tslint:disable-next-line:no-any
97-
this.removeFromModel = function(scope: any, modelPath: string, index: number) {
98-
const getter = $parse(modelPath);
99-
const modelValue = getter(scope);
119+
removeFromModel: function(scope, modelPath, index) {
120+
const getter = $parse(modelPath);
121+
const modelValue = getter(scope);
100122

101-
if (angular.isArray(modelValue) && typeof index === 'number') {
102-
modelValue.splice(index, 1);
103-
scope.$apply();
123+
if (angular.isArray(modelValue) && typeof index === 'number') {
124+
modelValue.splice(index, 1);
125+
scope.$apply();
126+
}
104127
}
105128
};
106129
}
107130
]);
108131

109132
// jqyoui-draggable directive
110-
// tslint:disable-next-line:no-any
111133
module.directive('jqyouiDraggable', [
112134
'customDragDropService',
113-
// tslint:disable-next-line:no-any
114-
function(dragDropService: any) {
135+
function(dragDropService: CustomDragDropService) {
115136
return {
116137
restrict: 'A',
117138
link: function(scope, element, attrs) {
@@ -134,7 +155,7 @@ export class AngularDragDropService {
134155
return scope.$eval(attrs.drag);
135156
}, updateDraggable);
136157

137-
el.addEventListener('dragstart', function(event: DragEvent) {
158+
el.addEventListener('dragstart', function(event) {
138159
if (!isDragEnabled()) {
139160
event.preventDefault();
140161
return;
@@ -176,7 +197,7 @@ export class AngularDragDropService {
176197

177198
// Call onStart callback if provided
178199
if (dragSettings.onStart) {
179-
const ui = {
200+
const ui: UI = {
180201
draggable: angular.element(el),
181202
helper: null,
182203
position: { top: event.clientY, left: event.clientX },
@@ -186,7 +207,7 @@ export class AngularDragDropService {
186207
}
187208
});
188209

189-
el.addEventListener('dragend', function(event: DragEvent) {
210+
el.addEventListener('dragend', function(event) {
190211
// Remove visual feedback
191212
el.style.opacity = '';
192213

@@ -209,23 +230,20 @@ export class AngularDragDropService {
209230
]);
210231

211232
// jqyoui-droppable directive
212-
// tslint:disable-next-line:no-any
213233
module.directive('jqyouiDroppable', [
214234
'customDragDropService',
215-
// tslint:disable-next-line:no-any
216-
function(dragDropService: any) {
235+
function(dragDropService: CustomDragDropService) {
217236
return {
218237
restrict: 'A',
219-
// tslint:disable-next-line:no-any
220-
link: function(scope: any, element: any, attrs: any) {
238+
link: function(scope, element, attrs) {
221239
const el = element[0];
222240

223241
// Check if dropping is enabled
224242
const isDropEnabled = function() {
225243
return attrs.drop === 'true' || scope.$eval(attrs.drop) === true;
226244
};
227245

228-
el.addEventListener('dragover', function(event: DragEvent) {
246+
el.addEventListener('dragover', function(event) {
229247
if (!isDropEnabled()) {
230248
return;
231249
}
@@ -239,12 +257,12 @@ export class AngularDragDropService {
239257
el.style.backgroundColor = '#f0f0f0';
240258
});
241259

242-
el.addEventListener('dragleave', function(event: DragEvent) {
260+
el.addEventListener('dragleave', function() {
243261
// Remove visual feedback
244262
el.style.backgroundColor = '';
245263
});
246264

247-
el.addEventListener('drop', function(event: DragEvent) {
265+
el.addEventListener('drop', function(event) {
248266
if (!isDropEnabled()) {
249267
return;
250268
}
@@ -283,16 +301,16 @@ export class AngularDragDropService {
283301

284302
// Remove from source if it's a move operation (not copy)
285303
// Check placeholder setting to determine if we should remove from source
286-
const dragSettings = dragDropService.dragSettings || {};
304+
const dragSettings: CustomDragDropService['dragSettings'] = dragDropService.dragSettings || {};
287305
const shouldRemoveFromSource =
288306
draggableScope &&
289307
typeof dragIndex === 'number' &&
290308
dragModelPath &&
291309
dragSettings.placeholder !== 'keep' &&
292310
!dropSettings.deepCopy;
293311

294-
if (shouldRemoveFromSource) {
295-
dragDropService.removeFromModel(draggableScope, dragModelPath, dragIndex);
312+
if (shouldRemoveFromSource && draggableScope) {
313+
dragDropService.removeFromModel(draggableScope, dragModelPath ?? '', dragIndex ?? undefined);
296314
}
297315

298316
// Call onDrop callback if provided

zeppelin-web-angular/src/app/services/table-data-adapter.service.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ export class TableDataAdapterService {
7373
rows: classicData.rows,
7474
comment: classicData.comment,
7575

76-
// Add any methods that classic visualizations might expect
7776
loadParagraphResult: paragraphResult => {
7877
// Delegate to modern TableData's method
7978
modernTableData.loadParagraphResult(paragraphResult);
@@ -85,7 +84,6 @@ export class TableDataAdapterService {
8584
proxy.comment = updatedClassicData.comment;
8685
},
8786

88-
// Refresh data from modern TableData
8987
refresh: () => {
9088
const updatedClassicData = this.convertToClassicFormat(modernTableData);
9189
proxy.columns = updatedClassicData.columns;

0 commit comments

Comments
 (0)