The map code fails with google is undefined because the google namespace does not exist in the browser when the returned inline script executes. The Maps API loader tag inside the server-side script's return string is never fetched and run before the map code that depends on it. The same markup pasted into a static HTML component renders correctly, so the map code is fine. The fault is where the code runs and the order in which it loads.
Execution context of the server-side script component
A server-side script component runs its body in the Mango server's JavaScript engine every time the bound point changes value. On the server it has the point value (value) and nothing else from the browser: no window, no document, and no way to load browser libraries. Its only product is a string. The view page receives that string through its periodic update mechanism and inserts it into the component's element.
That insertion step sets the timing. Browsers do not fetch or execute <script> elements inserted as HTML text. The Mango view page works around this for inline blocks by evaluating script content in component output. A minimal return of '<script type="text/javascript">alert('+ value +')</script>' fires correctly on the view page. That evaluation does not fetch an external src and wait for it to finish. The inline new google.maps.Map(...) runs at once, and the API is either never requested or arrives afterward.
The legacy loader URL used here, http://maps.google.com/maps/api/js?sensor=false, is a synchronous bootstrap. It pulls in the rest of the library with document.write, which works only while the page is still being parsed. When the tag is injected after page load, the bootstrap cannot complete even if the browser fetches it. This is why the loader must sit in content that exists when the page is first parsed, and why a static HTML component works.
Load-order limits and where to read them
One quantity decides this case: the value of typeof google at the instant the first map call executes in the browser. If it is 'undefined' at that instant, no fix to the map code helps. Read the related limits below before you change code.
| Quantity | Required state | Where to read it |
|---|---|---|
typeof google when map code runs |
'object' |
Browser developer console on the view page |
| Maps loader request | HTTP 200, requested during initial page parse | Browser Network tab, filtered on maps
|
Helper .js file request |
HTTP 200, not 404 | Network tab |
| Page scheme vs. loader scheme | Same scheme (an http:// script on an https:// page is blocked as mixed content) |
Address bar and console warnings |
| Page type | View page, not the view edit page | Browser URL |
| Server-side script syntax | No exception on evaluation | Component output and the Mango log |
Defects in the concatenated HTML string
Load order alone would stop the original script, but the return string also has defects that break it independently:
-
Unescaped quotes. Lines such as
s +=" <meta http-equiv="content-type" ..."end the string literal at the second double quote, so the server engine hits a syntax error before any HTML is returned. Wrap the literal in single quotes, or escape each inner quote as\". -
Malformed loader URL.
src=" http://maps.google.com/maps/api/js?sensor=false\ "carries a leading space and a trailing backslash-space. Even if the tag loaded, the URL is not clean. -
Nested document. Returning
<!DOCTYPE html><html><head>...<body>inside a component'sdivis invalid. The browser discards thehtml,head, andbodywrappers and keeps only fragments. -
Array indexing. Marker positions must index the row, as in
locations[i][1],locations[i][2], andlocations[i][0]. -
Legacy options.
navigationControlandNavigationControlStyleare v2-era options that the v3 API ignores. Thesensorparameter is also legacy, and the current Maps JavaScript API requires an API key. Check Google's current loader documentation before you deploy.
Four ways to get GPS point values onto the map
The HTML component is not bound to a point, so it cannot receive live values. The server-side script is bound to a point, but it cannot load the library. Every workable design splits the work between the two.
| Approach | Bound to point value | API loaded during page parse | Observed result | Verdict |
|---|---|---|---|---|
| Whole page in a static HTML component | No | Yes | Map and markers render | Static coordinates only |
| Whole page returned from a server-side script | Yes | No | google is undefined |
Cannot work |
| Loader tag in an HTML component, full map build in a server-side script | Yes | Yes, if the HTML component is defined first | Still google is undefined in this installation. The map is rebuilt on every value change. |
Fragile, depends on order |
Helper functions in an external .js file loaded by the HTML component, with the server-side script returning a one-line call |
Yes | Yes | Helper call pattern (addBold test) confirmed on the view page in Mango 1.13.7. It errors on the edit page. |
Recommended |
| JSP view | Yes, via page code | Yes | Suggested as better suited; not tested | Fallback for complex maps |
The helper-file pattern wins on two points. First, the map object is created once and survives every refresh, so each point update only moves a marker. Second, the server-side script output shrinks to a function call with numbers, which removes the quoting problem entirely.
Procedure for the helper-file map driven by server-side scripts
- Add an HTML component to the graphical view before any server-side script component. Give it the map container, the Maps loader, and the helper file, with no
html,head, orbodywrappers:
Match the loader scheme to the scheme Mango is served on. Replace the loader URL with the current keyed form from Google's documentation.<div id="map" style="width: 600px; height: 550px;"></div> <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script> <script type="text/javascript" src="/gpsmap.js"></script> - Add one server-side script component per GPS point, bound to that point. Return only a call to the helper:
Use a distinct id string for each point (var p = String(value).split(','); var lat = parseFloat(p[0]), lng = parseFloat(p[1]); if (isNaN(lat) || isNaN(lng)) return ''; return '<script type="text/javascript">gpsUpdate("position1",' + lat + ',' + lng + ');</script>';position1,position2, and so on) so that each component drives its own marker. - Save the view and open it from the view page, not the edit page. The edit page does not evaluate component scripts the way the view page does, so helper calls there report the function as not found.
Encoding the GPS position in an HTTP Retriever alphanumeric point
Store latitude and longitude together in a single alphanumeric point, written for example as httpds?__device=gpslocA&__point=position1&__value=40.123456,52.123456. This keeps both coordinates in one sample with one timestamp. Two separate numeric points for latitude and longitude avoid string parsing, but they update independently. A server-side script bound to one of them then renders a marker from a new latitude and a stale longitude until the second update arrives.
The comma-separated string has to be parsed in the server-side script, so validate it there, before anything reaches the browser:
- Reject values that parse to
NaN. The script above returns an empty string in that case. - Reject latitude outside ±90 and longitude outside ±180. A swapped field order usually fails the latitude check first.
- Keep the decimal point as the separator. A locale that writes decimals with commas collides with the field delimiter.
Symptoms versus causes for a blank view or undefined google
| Symptom | Cause | Check / fix |
|---|---|---|
google is undefined, whole page returned from the server-side script |
Loader tag in injected content is never loaded before the inline code runs | Move the loader into the HTML component |
google is undefined with the loader in the HTML component |
Script component renders before the HTML component, or the loader is injected after page parse | Reorder components. Confirm in the Network tab that the loader loads on first page load. Add the retry guard. |
| Helper function not found, edit page only | Edit page does not evaluate component scripts | Test on the view page |
| Blank view, no console errors | Helper file returns 404 (placed under WEB-INF or referenced by a relative path), map div missing or zero height, or empty script output |
Network tab status for the .js file. Inspect the #map element size. Log value. |
| No component output and a script error in Mango | Unescaped double quotes in the return string | Use single-quoted literals |
| Loader blocked in console |
http:// loader on an https:// Mango page |
Match the schemes |
These are all logic and load-order faults, not server load problems. If the view renders once and then stops updating, look at the point's data source polling and value history before you look at the page.
Confirming live marker updates on the view page
- Open the view page and run
typeof googleandtypeof gpsUpdatein the console. You should see'object'and'function'. - Check that the map tile layer renders inside the 600 × 550 px container and that one marker appears per bound point.
- Push a new value through the HTTP Retriever URL with changed coordinates. The marker should move on the next view refresh without the map resetting zoom or center. A reset means the map is being rebuilt, which means the full map build is still inside the server-side script.
- Compare the marker position with the point's latest value and timestamp in the Mango point details. They should match to the precision entered.
- Send a malformed value, such as a single number. The marker should hold its last position and the console should stay clean.
FAQ
How do I load the Google Maps API in a Mango graphical view?
Put the loader <script src=...> tag in an HTML component that is defined before any server-side script component, so that it loads during the initial page parse. A loader tag inside a server-side script's returned string never loads in time.
How do I pass a Mango point value into JavaScript on the view page?
Bind a server-side script component to the point and return an inline block, such as '<script type="text/javascript">gpsUpdate("position1",' + lat + ',' + lng + ');</script>'. The view page evaluates the inline script each time the value changes.
How do I choose where to put a custom .js file for a Mango view?
Place it in webapps/ROOT and reference it with an absolute path such as /gpsmap.js, or host it on a CDN. Files under WEB-INF are never served to the browser and return 404.
How do I fix 'function not defined' errors on the Mango view edit page?
Test on the view page instead. The edit page does not evaluate component scripts the way the view page does, so helper calls fail there even when they work correctly on the view page.
How do I know when to escalate a Mango map view problem to support?
Escalate when typeof google and typeof gpsUpdate both return correctly on the view page and the helper file loads with HTTP 200, but server-side script output still never executes. At that point, contact Mango's official support channel with your exact Mango version, the component order, and the script bodies. If the map needs more than per-point marker updates, ask them about building it as a JSP view instead.