This document describes step by step how to convert an existing Mapbender jQuery UI widget (e.g. mapbender.mbRuler) into a native ES6 JavaScript class (e.g. MbRuler). The basis for this are the files
- before:
mapbender.element.ruler.js - after:
MbRuler.js
The same principles apply to other elements.
Two adjustments are necessary in the PHP-Element-Class (e.g. Mapbender\CoreBundle\Element\Ruler):
getWidgetName()-> change return value frommapbender.mbRulertoMbRuler.getRequiredAssets()-> Modify the path and refer to the new class script (new file nameMbRuler.js). Delete the old path.../mapbender.element.ruler.js.
Example (simplified):
public static function getWidgetName() : ?string
{
return 'MbRuler';
}
public static function getRequiredAssets(Entity\Element $element) : array
{
return [
'js' => [
'@MapbenderCoreBundle/Resources/public/elements/MbRuler.js',
],
// ... css / translation as before
];
}| Old (widget) | New (native class) |
|---|---|
mapbender.element.ruler.js |
MbRuler.js |
Widget-Namespace: $.widget("mapbender.mbRuler", {...}) |
ES6 class: class MbRuler extends MapbenderElement {} |
Widget-Name (getWidgetName) = mapbender.mbRuler |
class name = MbRuler |
(function($) {
$.widget("mapbender.mbRuler", {
// methods + options
});
})(jQuery);(function() {
class MbRuler extends MapbenderElement {
// methods + constructor
}
window.Mapbender.Element = window.Mapbender.Element || {};
window.Mapbender.Element.MbRuler = MbRuler;
})();Important: The class must be registered globally under window.Mapbender.Element.<Name> so that the element loader can find it.
The old jQuery-Widget used _create(). In the class, we replace this with an ES6 constructor:
_create: function() {
var self = this;
if (this.options.type !== 'line' && ...) { throw ... }
Mapbender.elementRegistry.waitReady('.mb-element-map').then(function(mbMap){
self._setup(mbMap);
});
}constructor(configuration, $element) {
super(configuration, $element);
if (this.options.type !== 'line' && this.options.type !== 'area' && this.options.type !== 'both') {
throw Mapbender.trans('mb.core.ruler.create_error');
}
Mapbender.elementRegistry.waitReady('.mb-element-map').then((mbMap) => {
this._setup(mbMap);
}, function() {
Mapbender.checkTarget('mbRuler');
});
}The parameters configuration and $element are always passed. The parent constructor must first be called via super.
All function literals in the object (name: function (...) {}) become class methods (name(...) {}).
Before:
_setup: function(mbMap) {
this.mapModel = mbMap.getModel();
}After:
_setup(mbMap) {
this.mapModel = mbMap.getModel();
}No more commas between methods. The function keyword is no longer required.
Instead of this._trigger(‘ready’) (jQuery UI), the following call is now used:
Mapbender.elementRegistry.markReady(this);This usually happens at the end of _setup().
The widget received the root element in this.element from jQuery UI Core. In the new base class MapbenderElement, it is this.$element (jQuery object). Replace all occurrences:
| Old | New |
|---|---|
this.element |
this.$element |
Previously, popups were controlled manually via new Mapbender.Popup({...}); activation was often via defaultAction / activate / deactivate. Now MapbenderElement provides a standard mechanism:
- Continue to use
activate()/deactivate()for technical activation (layers, interactions, etc.). - For button-based activation (toolbar), use
activateByButton(callback)andcloseByButton() - Overwrite
getPopupOptions()to customize titles, sizes, buttons, etc.
getPopupOptions() {
return {
title: this.$element.attr('data-title'),
modal: false,
draggable: true,
resizable: true,
closeOnESC: true,
destroyOnClose: true,
content: this.$element,
width: 300,
height: 300,
buttons: [{
label: Mapbender.trans('mb.actions.close'),
cssClass: 'btn btn-sm btn-light popupClose'
}]
};
}- Method order can be freely adjusted; recommended: constructor -> private setup functions -> event handlers -> calculations -> formatting.
- Use arrow functions for callbacks if
thisis required by the class context (e.g.,geometry.on(‘change’, () => { ... })). - Where
var self = this;was previously required, this is no longer necessary thanks to arrow functions or direct method binding (.bind(this)).
Before:
_createControl: function() {
const source = this.layer.getNativeLayer().getSource();
const control = new ol.interaction.Draw({
type: this.options.type === 'line' ? 'LineString' : 'Polygon',
source: source,
stopClick: true,
style: this._getStyle.bind(this)
});
control.on('drawstart', function(event) { /* self */ });
}After:
_createControl() {
const source = this.layer.getNativeLayer().getSource();
const control = new ol.interaction.Draw({
type: this.options.type === 'line' ? 'LineString' : 'Polygon',
source: source,
stopClick: true,
style: this._getStyle.bind(this)
});
control.on('drawstart', (event) => { /* this */ });
}- Inheritance is achieved by referencing the complete path of the parent class namespace.
- Parent functions can be called using
super.parentFunction().
Example:
(function() {
class MbPrint extends Mapbender.Element.MbImageExport {
constructor(configuration, $element) {
super(configuration, $element);
// Your own code ...
}
_setup() {
super._setup();
// Your own code ...
}
activateByButton(callback) {
super.activateByButton(callback);
// Your own code ...
}
closeByButton() {
super.closeByButton();
// Your own code ...
}
}
window.Mapbender.Element = window.Mapbender.Element || {};
window.Mapbender.Element.MbPrint = MbPrint;
})();- PHP: Adjust
getWidgetName()-> class name. - PHP:
getRequiredAssets()– remove old JS file name, add new one. - Rename JS file (
mapbender.element.<name>.js->Mb<Name>.js). - Remove widget wrapper; introduce ES6 IIFE + class.
- Registration at the end:
window.Mapbender.Element.<class> = <class>. _create->constructor(configuration, $element)+super(...).- Convert method syntax (
name: function->name()). this._trigger(‘ready’)->Mapbender.elementRegistry.markReady(this).- Replace
this.element->this.$element. - Change popup handling to
getPopupOptions,activateByButton,closeByButton. - Use arrow functions, remove
var self=this;.
(function() {
class MyNewElement extends MapbenderElement {
constructor(configuration, $element) {
super(configuration, $element);
Mapbender.elementRegistry.waitReady('.mb-element-map').then((mbMap) => {
this._setup(mbMap);
});
}
_setup(mbMap) {
this.mapModel = mbMap.getModel();
// ... init code ...
Mapbender.elementRegistry.markReady(this);
}
activate() { /* optional override */ }
deactivate() { /* optional override */ }
getPopupOptions() {
return {
title: this.$element.attr('data-title'),
content: this.$element
};
}
}
window.Mapbender.Element = window.Mapbender.Element || {};
window.Mapbender.Element.MyNewElement = MyNewElement;
})();