Hi,
I would instead try to figure out how to save the data to the property instead. Can you perhaps post the widget code so I can have a quick look? And how are you saving the data, as JSON?
This is the widget script. In the function _onAddedPolygon I call the API to save the property value. However, it does not always seems to trigger a page save (ie, the publish button does not appear). This I think is related to the focus of the control beeing lost or something along these lines. I have seen it before but never fully understood have to circumvent it.
define([ "dojo/on", // To connect events "dojo/_base/declare", // Used to declare the actual widget "dojo/_base/config", // Used to check if client code debugging is enabled "dojo/aspect", // Used to attach to events in an aspect-oriented way to inject behaviors "dijit/registry", // Used to get access to other dijits in the app "dijit/WidgetSet", // To be able to use 'byClass' when querying the dijit registry "dijit/_Widget", // Base class for all widgets "dijit/_TemplatedMixin", // Widgets will be based on an external template (string literal, external file, or URL request) "dijit/_WidgetsInTemplateMixin", // The widget will in itself contain additional widgets, "dijit/Dialog", "dijit/form/TextBox", "dijit/form/Textarea", "dijit/form/Button", "dojox/form/DropDownSelect", "epi/epi", // For example to use areEqual to compare property values "epi/shell/widget/_ValueRequiredMixin", // In order to check if the property is in a readonly state "epi/shell/widget/dialog/LightWeight", // Used to display the help message "epi-cms/widget/_HasChildDialogMixin", "epi-cms/_ContentContextMixin", "dojo/i18n!./nls/Labels", // Localization files containing translations 'xstyle/css!./WidgetTemplate.css', "googlemapseditor/Async!https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=places,drawing&key=AIzaSyCCkZI3w943tyqyZCkbbEB2Pl1W0eH1WPc" // Use async module to load external asyncronously loaded script for Google Maps ], function ( on, declare, config, aspect, registry, WidgetSet, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, Dialog, TextBox, Textarea, Button, DropDownSelect, epi, _ValueRequiredMixin, LightWeight, _HasChildDialogMixin, _ContentContextMixin, localized ) { return declare([_Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _ValueRequiredMixin, _ContentContextMixin], { intermediateChanges: true, // The Google Maps object of this widget instance map: null, // The map marker of this widget instance marker: null, // Load HTML template from the same location as the widget templateString: dojo.cache("advancedgooglemapseditor", "WidgetTemplate.html"), // Localizations able to be accessed from the template localized: localized, // Event used to notify EPiServer that the property value has changed onChange: function (value) { }, // Saves all shape coodinates the user has drawn shapes: null, // Saves the objects them self (for cleanup) trueShapes: null, marker: null, helpDialog: null, infoDialog: null, textarea: null, pageId: null, // Dojo event fired after all properties of a widget are defined, but before the fragment itself is added to the main HTML document postCreate: function () { // Call base implementation of postCreate, passing on any parameters this.inherited(arguments); // When the editor switches tabs in the UI we trigger a map resize to ensure it aligns properly // Note: byClass requires dot notation var that = this; registry.byClass("dijit.layout.TabContainer").forEach(function (tab, i) { aspect.after(tab, "selectChild", function () { that.alignMap(); }); }); // Display help when help icon is clicked on(this.helpIcon, "click", function (e) { e.preventDefault(); if (!that.helpDialog) { if (config.isDebug) { console.log('Creating help dialog for Google Maps editor'); } that.helpDialog = new LightWeight({ style: "width: 540px", closeIconVisible: true, showButtonContainer: false, onButtonClose: function () { that.helpDialog.hide(); }, _endDrag: function () { // HACK Avoid EPiServer bug, "Cannot read property 'userSetTransformId' of null" when close icon is clicked }, title: that.localized.help.dialogTitle, content: that.localized.help.dialogHtml }); } if (that.helpDialog.open) { that.helpDialog.hide(); } else { that.helpDialog.show(); } }); }, // Dojo event triggered after 'postCreate', for example when JS resizing needs to be done startup: function () { this.initializeMap(); var that = this; // HACK Give EPiServer UI some time to load the view before resizing the map to ensure it aligns even after being hidden/displayed // TODO Replace with event/aspect for when the edit view changes in the UI setTimeout(function () { that.alignMap(); }, 250); }, // Dojo event triggered when widget is removed destroy: function () { this.inherited(arguments); // Important to ensure inherited widgets are destroyed properly, failure to do this risks memory leaks // Clean up Google Maps (as much as possible, but there is a known issue with Google Maps: https://code.google.com/p/gmaps-api-issues/issues/detail?id=3803) if (this.marker) { this.marker.setMap(null); } if (this.map && this.map.parentNode) { google.maps.event.clearListeners(this.map, 'rightclick'); this.map.parentNode.removeChild(this.map); this.map = null; } }, // Checks if the current property value is valid (invoked by EPiServer) isValid: function () { return true; }, // Checks if the current value is valid coordinates hasCoordinates: function () { return true; }, // Setter for value property (invoked by EPiServer on load) _setValueAttr: function (value) { if (value === this.value) { return; } this._set("value", value); // If the value set is empty then clear the coordinates if (!value) { //this.clearCoordinates(); this.clearAllShapes(); return; } var location; if (value) { if (this.shapes == null) this.shapes = []; if (this.trueShapes == null) this.trueShapes = []; var json = JSON.parse(value); for (var i = 0; i < json.length; i++) { var shapePath = json[i].points; var type = json[i].type; var text = json[i].text; if (shapePath.length > 1) { if (type == 'polygon') var shape = new google.maps.Polygon({ path: shapePath }); else if (type == 'polyline') var shape = new google.maps.Polyline({ path: shapePath }); shape.setMap(this.map); var pointsWrapper = new Object(); pointsWrapper.points = shapePath; pointsWrapper.type = type pointsWrapper.text = text this.trueShapes.push(shape); this.shapes.push(pointsWrapper); } else { var pos = { lat: json[i].points[0].lat, lng: json[i].points[0].lng }; var shape = new google.maps.Marker({ position: pos }); var polygonPoints = []; polygonPoints.push(shape.getPosition()); var pointsWrapper = new Object(); pointsWrapper.points = polygonPoints; pointsWrapper.type = type pointsWrapper.text = text shape.setMap(this.map); this.trueShapes.push(shape); this.shapes.push(pointsWrapper); } } } }, _onAddedPolygon: function () { var c = getCurrentContent(); console.log(c); if (!this._started) { return; } var value = JSON.stringify(this.shapes); this.focus(); this._set("value", value); this.onChange(value); }, clearAllShapes: function () { // Clear the layers from the map for (var i = 0; i < this.trueShapes.length; i++) { var shape = this.trueShapes[i]; shape.setMap(null); shape = null; } // Delete the arrays trueShapes = null; shapes = null; // Null the EPiServer value this._set("value", null); this.onChange(null); //// Call the backend api //$.ajax({ // type: "POST", // url: "/Templates/Extern/Pages/Special/AdvancedMapSaver.aspx", // data: { "pageId": this.pageId, "shapesData": "" }, // success: function (d) { // console.log(d); // }, //}); }, saveShapes: function () { var value = JSON.stringify(this.shapes); // Call the backend api $.ajax({ type: "POST", url: "/Templates/Extern/Pages/Special/AdvancedMapSaver.aspx", data: { "pageId": this.pageId, "shapesData": value }, success: function (d) { console.log(d); }, }); }, // Setup the Google Maps canvas initializeMap: function () { if (config.isDebug) { console.log('Initializing map for Google Maps editor'); } defaultCoordinates = new google.maps.LatLng(59.3791034, 13.4994304); // Render the map, but disable interaction if property is readonly var mapOptions = { zoom: 11, disableDefaultUI: true, center: defaultCoordinates, disableDoubleClickZoom: this.readOnly, scrollwheel: !this.readOnly, draggable: !this.readOnly }; var drawingManager = new google.maps.drawing.DrawingManager({ drawingMode: google.maps.drawing.OverlayType.MARKER, drawingControl: true, drawingControlOptions: { position: google.maps.ControlPosition.TOP_CENTER, drawingModes: ['marker', 'polygon', 'polyline',] }, }); // Load the map this.map = new google.maps.Map(this.canvas, mapOptions); drawingManager.setMap(this.map); var that = this; google.maps.event.addListener(drawingManager, 'overlaycomplete', function (event) { if (that.shapes == null) that.shapes = []; if (that.trueShapes == null) that.trueShapes = []; if (event.type == 'polygon' || event.type == 'polyline') { var polygon = event.overlay; var polygonPoints = []; var pointsWrapper = new Object(); for (var i = 0; i < polygon.getPath().getLength(); i++) { var p = polygon.getPath().getAt(i); polygonPoints.push(p); } pointsWrapper.points = polygonPoints; pointsWrapper.type = event.type; pointsWrapper.text = that.descriptionTextArea.value; pointsWrapper.heading = that.descriptionTextbox.value; pointsWrapper.color = that.colorDropDown.value; that.widgetForm.reset(); that.shapes.push(pointsWrapper); that.trueShapes.push(polygon); that._onAddedPolygon(); } if (event.type == 'marker') { marker = event.overlay; var polygonPoints = []; var pointsWrapper = new Object(); polygonPoints.push(marker.getPosition()); pointsWrapper.points = polygonPoints; pointsWrapper.type = 'marker'; pointsWrapper.heading = that.descriptionTextbox.value; pointsWrapper.text = that.descriptionTextArea.value; pointsWrapper.color = that.colorDropDown.value; that.widgetForm.reset(); that.shapes.push(pointsWrapper); that.trueShapes.push(marker); that._onAddedPolygon(); } }); var searchBox = new google.maps.places.SearchBox(this.searchTextbox.textbox); // Remove Google Maps Searchbox default placeholder, as it won't recognize the placeholder attribute placed on the Textbox dijit this.searchTextbox.textbox.setAttribute('placeholder', ''); google.maps.event.addListener(searchBox, 'places_changed', function () { var places = searchBox.getPlaces(); if (places.length == 0) { return; } // Return focus to the textbox to ensure autosave works correctly and to also give a nice editor experience that.searchTextbox.focus(); var mapOptions = { zoom: 15, center: places[0].geometry.location, }; that.map.setOptions(mapOptions); }); }, // Triggers a map resize, for example when the map is hidden/displayed to ensure it aligns properly alignMap: function () { var center = this.map.getCenter(); google.maps.event.trigger(this.map, "resize"); this.map.setCenter(center); }, }); });
Hi again,
First of all, inherit from _HasChildDialogMixin and let me know the result. I have found this to be helpful in many cases with widget focus problems. And I think intermediateChanges property should be set to false in your case as well. It's helpful on text box widgets etc where you want to fire changes on every keystroke.
I did as you suggested, included the mixin in the declare function, and changed intermediateChanges to false. It did not solve the issue. The value is not saved to the page.
This solved the problem: https://world.episerver.com/documentation/developer-guides/CMS/user-interface/Context-sensitive-components/
Summary: one should use the _ContextMixin to enable automatic saving. It can also be used to get some information about the current page.
Or maybe not. It workes perfectly for a while but suddenly I'm back to square one. However, the mixin gives access to the page id, so I wil use this in my crude API where I call the server backend and lets it save the value to the page. It's ugly, but since EPiServer never have adressed these issues I feel I have no choice.
If anyone is interested, the code that extracts the page id looks as follows:
pageId: function () { var fullId = this.getCurrentContext().id; if (fullId.includes("_")) return fullId.split("_")[0]; return fullId; }
Hi!
Im developing a custom editor widget that lets editors create simple polygons on a map. This information should be saved on the current page so that the shapes can be rendered on the page in view mode.
However, since saving data to the current property via Dojo almost never work automatically (I mean, really? Why should it be so troublesome?), I thought I could call a web service that saves the shapes instead. That solution requires that the current page id (contentlink) is sent to the service along with the data.
So the question is - how to access the current page id from the widget?
It seems it can, somehow, be achived using this: https://world.episerver.com/documentation/Javascript-Library/?documentId=cms/7/epi/cms/_ContentContextMixin
But I think this documentation lacks some good solid examples on how to actually use the mixin within the widget.
I also found this post: https://world.episerver.com/Modules/Forum/Pages/Thread.aspx?id=87472, but to me it seems strange that the suggested answer was marked as the correct one - it does not answer the original question.
Has anyone got any idea how to accomplish this? It really should be simple.