Description
The Traffic control options allows specifying isActive which should enable the traffic layers by default and set the control in enabled state (if map hasn't enabled traffic before with setTraffic).
However, when the traffic control is added inside map ready event callback:
map.events.add('ready', function () {
//Create a traffic control to let the user easily turn the traffic on an off.
map.controls.add(new atlas.control.TrafficControl({isActive: true, style: 'dark'}), {
position: 'top-right'
});
});
the control gets toggled on and then off thus resulting in traffic not being displayed and control not active.
Expected Behavior
isActive: true
should properly set control in enabled state and have the traffic displayed.
How to reproduce
In Azure Maps Traffic control codepan from official docs: https://codepen.io/azuremaps/pen/ZEWaeLJ just add isActive: true to traffic control init options
Explaination
The issue happens due to TrafficControl registering a map ready event callback itself:
TrafficControl.prototype.constructTrafficButton = function (map) {
var _this = this;
var trafficButton = document.createElement("button");
// some details removed out
trafficButton.addEventListener("click", function () {
var t = map.getTraffic();
_this.options.isActive = !(t.flow !== "none" || t.incidents);
if (_this.options.isActive) {
map.setTraffic({
flow: _this.options.flow,
incidents: _this.options.incidents
});
_this.container.classList.add("in-use");
} else {
map.setTraffic({
flow: "none",
incidents: false
});
_this.container.classList.remove("in-use");
}
});
map.events.add("ready", function () {
if (_this.options.isActive) {
trafficButton.dispatchEvent(new Event("click"));
}
});
return trafficButton;
};
So the ready event handler gets added in another ready event handler, causing the callbacks that are getting iterated upon mutated during the iteration in:
EventManager.prototype._invokeListeners = function (eventType, layer, args) {
var _this = this;
var callbacks = this.mapCallbackHandler.getEventCallbacks(eventType, layer);
if (callbacks) {
// event callbacks are mutated during the iteration
callbacks.forEach(function (_a, callback) { //... })
}
// details redacted
}
which results in TrafficControl ready handler triggered twice and thus resulting in enabled state toggled on and off.
Possible resolution
- Iterate over a copy of callback in EventManager._invokeListeners: something like
(new Map(callbacks)).forEach()
- Reconsider the logic of TrafficControl initialization: ready control handle fires map click event that derives isActive state based on flipping the current traffic state (this can lead to unexpected behavior for the user if the
map.setTraffic({incidents: true, flow: 'relative'})
was called and the TrafficControl was created with isActive, which results in traffic getting toggled off at init)