How to asynchronously load a google map in AngularJS? - google-maps-api-3

Now that I have found a way to initialize Google Maps with the help of Andy Joslin in this SO initialize-google-map-in-angularjs, I am looking for a way to asynchronous load a Google Map Object.
I found an example of how to do this in the phonecat project.
Notice how the JS files are loaded in this example: index-async.html
In my Jade Scripts partial that is loaded into my program I tried:
script(src='js/lib/angular/angular.js')
script(src='js/lib/script/script.min.js')
script
$script([
'js/lib/angular/angular-resource.min.js',
'js/lib/jquery/jquery-1.7.2.min.js',
'http://maps.googleapis.com/maps/api/js?key=AIzaSyBTmi_pcXMZtLX5MWFRQgbVEYx-h-pDXO4&sensor=false',
'js/app.js',
'js/services.js',
'js/controllers.js',
'js/filters.js',
'js/directives.js',
'bootstrap/js/bootstrap.min.js'
], function() {
// when all is done, execute bootstrap angular application
angular.bootstrap(document, ['ofm']);
});
When I do this and go to load the map page I get:
A call to document.write() from an asycrononously-loaded
external script was ignored.
This is how Google Maps is being loaded now as a service:
'use strict';
var app = angular.module('ofm.services', []);
app.factory('GoogleMaps', function() {
var map_id = '#map';
var lat = 46.87916;
var lng = -3.32910;
var zoom = 15;
var map = initialize(map_id, lat, lng, zoom);
return map;
});
function initialize(map_id, lat, lng, zoom) {
var myOptions = {
zoom : 8,
center : new google.maps.LatLng(lat, lng),
mapTypeId : google.maps.MapTypeId.ROADMAP
};
return new google.maps.Map($(map_id)[0], myOptions);
}
It appears that this should be returning a promise from what I recall reading. But this AngularJS is very new to me.

here's my solution I came up without using jQuery:
(Gist here)
angular.module('testApp', []).
directive('lazyLoad', ['$window', '$q', function ($window, $q) {
function load_script() {
var s = document.createElement('script'); // use global document since Angular's $document is weak
s.src = 'https://maps.googleapis.com/maps/api/js?sensor=false&callback=initialize';
document.body.appendChild(s);
}
function lazyLoadApi(key) {
var deferred = $q.defer();
$window.initialize = function () {
deferred.resolve();
};
// thanks to Emil Stenström: http://friendlybit.com/js/lazy-loading-asyncronous-javascript/
if ($window.attachEvent) {
$window.attachEvent('onload', load_script);
} else {
$window.addEventListener('load', load_script, false);
}
return deferred.promise;
}
return {
restrict: 'E',
link: function (scope, element, attrs) { // function content is optional
// in this example, it shows how and when the promises are resolved
if ($window.google && $window.google.maps) {
console.log('gmaps already loaded');
} else {
lazyLoadApi().then(function () {
console.log('promise resolved');
if ($window.google && $window.google.maps) {
console.log('gmaps loaded');
} else {
console.log('gmaps not loaded');
}
}, function () {
console.log('promise rejected');
});
}
}
};
}]);

If you using jQuery in your AngularJS app, check out this function which returns a promise for when the Google Maps API has been loaded:
https://gist.github.com/gbakernet/828536
I was able to use this in a AngularJS directive to lazy-load Google Maps on demand.
Works a treat:
angular.module('mapModule') // usage: data-google-map
.directive('googleMap', ['$window', function ($window) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
// If Google maps is already present then just initialise my map
if ($window.google && $window.google.maps) {
initGoogleMaps();
} else {
loadGoogleMapsAsync();
}
function loadGoogleMapsAsync() {
// loadGoogleMaps() == jQuery function from https://gist.github.com/gbakernet/828536
$.when(loadGoogleMaps())
// When Google maps is loaded, add InfoBox - this is optional
.then(function () {
$.ajax({ url: "/resources/js/infobox.min.js", dataType: "script", async: false });
})
.done(function () {
initGoogleMaps();
});
};
function initGoogleMaps() {
// Load your Google map stuff here
// Remember to wrap scope variables inside `scope.$apply(function(){...});`
}
}
};
}]);

Take a look of this i think its more reliable
var deferred = $q.defer();
var script = document.createElement('script');
$window.initMap = function() {
//console.log("Map init ");
deferred.resolve();
}
script.src = "//maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places&callback=initMap";
document.body.appendChild(script);
return deferred.promise;

Related

Change infowindow marker based on variable in realtime

How do I change the icon of a marker based on if a value is true or false.
I created an if function that checks the value of CameraStatus. I set it to false on default but the marker still won't change to RedStatus. It does change to RedStatus when I try a timer like this:
setTimeout(function () { MiamiMarker.setIcon(RedStatus) }, 10 * 1000);
It doesn't change to RedStatus when I try this:
var CameraStatus = false;
function CheckStatus() {
if (CameraStatus === false) {
MiamiMarker.SetIcon(RedStatus)
}
}
How do I change the marker based on my if function?
Eventually I want to change all my markers with boolean values I get from a home controller. The value of the boolean should decide if the marker has a GreenStats or RedStatus icon. First I'm trying to change one marker based on a hardcoded value. (See code below)
My code:
<script>
var map;
function initMap() {
var CenterLoc = { lat: 51.34, lng: 5.53 };
map = new google.maps.Map(document.getElementById('map'),
{
center: CenterLoc,
disableDefaultUI: true,
zoom: 3,
});
google.maps.event.addDomListener(window, 'load', initMap);
var Miami = { lat: 25.774266, lng: -80.193659 };
var MiamiMarker = new google.maps.Marker
({
position: Miami,
map: map,
icon: GreenStatus
});
//Replace standard google maps markers with colored dots
var GreenStatus = "#ViewBag.GreenStatus";
var OrangeStatus = "#ViewBag.OrangeStatus";
var RedStatus = "#ViewBag.RedStatus";
var CameraStatus = false;
function CheckStatus() {
if (CameraStatus === false) {
MiamiMarker.SetIcon(RedStatus)
}
}
var MiamiInfoCard = new google.maps.InfoWindow
({
content: '<div id="map-dialog"><h3>Miami</h3></div>'
});
MiamiMarker.addListener('click', function () {
MiamiInfoCard.open(map, MiamiMarker);
});
var position = new google.maps.LatLng(52.2305, 5.9924);
}
</script>
You can use an if else statement to change your Icon. Please note that it is .setIcon() and not *.SetIcon() just like in your code.
if (CameraStatus == false) {
MiamiMarker.setIcon(RedStatus)
} else {
MiamiMarker.setIcon(GreenStatus)
}
You can check this sample code that reproduces what you want in your use-case.
I used a toggle switch to set values for CameraStatus and call the CheckStatus() function passing the variables CameraStatus and MiamiMarker to be processed in the function.
var switchStatus = document.getElementById("mySwitch");
var CameraStatus;
switchStatus.addEventListener('change', function() {
if (switchStatus.checked) {
CameraStatus = false;
} else {
CameraStatus = true;
}
CheckStatus(CameraStatus, MiamiMarker);
});
I put the CheckStatus() function outside the initMap() function and passed the CameraStatus and MiamiMarker to change the marker's icon base on the value of CameraStatus.
function CheckStatus(CameraStatus, MiamiMarker) {
if (CameraStatus == false) {
MiamiMarker.setIcon(RedStatus)
} else {
MiamiMarker.setIcon(GreenStatus)
}
Hope this helps!

Pagination with meteor and iron router not re-rendering

I'm really struggling to understand what's going on here. I have a template
<template name="gallery">
<div>
123
</div>
{{#each art}}
{{name}}
{{/each}}
</template>
Then I have routes like this
Router.map(function() {
this.route('/', {
template: 'gallery',
data: function() {
var page = 1;
return {
page: page,
};
},
});
this.route('/gallery/:_page', {
template: 'gallery',
data: function() {
var page = parseInt(this.params._page);
return {
page: page,
};
},
});
});
Following this article I'm using template subscriptions like this
var G_PAGE_SIZE = 10;
Template.gallery.onCreated(function() {
var instance = this;
var route = Router.current();
instance.loaded = new ReactiveVar(0);
instance.autorun(function() {
var pageId = parseInt(route.params._page);
var page = pageId - 1;
var skip = page * G_PAGE_SIZE;
var subscription = instance.subscribe('artForGrid', skip, G_PAGE_SIZE);
if (subscription.ready()) {
instance.loaded.set(1);
}
});
instance.art = function() {
return Art.find({});
};
});
Template.gallery.helpers({
art: function() {
return Template.instance().art();
},
});
It works but when I click one of the links changing the route the page doesn't re-render
So ... some other answer on here said that's because there's no reactive connection between data changing on the route through the router and the template.
So I tried use route.state which as a ReactiveDict (not sure where that came from). If it's part of iron:router or if it's part of reactive-var
Anyway I changed the code to
Router.map(function() {
this.route('/', {
template: 'gallery',
data: function() {
var page = 1;
this.state.set('page', page); // <------------ADDED
return {
page: page,
};
},
});
this.route('/gallery/:_page', {
template: 'gallery',
data: function() {
var page = parseInt(this.params._page);
this.state.set('page', page); // <------------ADDED
return {
page: page,
};
},
});
});
In the onCreated
Template.gallery.onCreated(function() {
var instance = this;
var route = Router.current();
instance.loaded = new ReactiveVar(0);
instance.autorun(function() {
var pageId = route.state.get('page'); // <--------- CHANGED
var page = pageId - 1;
var skip = page * G_PAGE_SIZE;
var subscription = instance.subscribe('artForGrid', skip, G_PAGE_SIZE);
if (subscription.ready()) {
instance.loaded.set(1);
}
});
instance.art = function() {
return Art.find({});
};
});
Template.gallery.helpers({
art: function() {
return Template.instance().art();
},
});
That doesn't work either except sometimes. In the interest of debugging I added a console.log to the route
Router.map(function() {
this.route('/', {
template: 'gallery',
data: function() {
var page = 1;
this.state.set('page', page);
console.log("/root"); // <------------ADDED
return {
page: page,
};
},
});
this.route('/gallery/:_page', {
template: 'gallery',
data: function() {
var page = parseInt(this.params._page);
this.state.set('page', page);
console.log("/gallery/" + page); // <------------ADDED
return {
page: page,
};
},
});
});
Suddenly it starts working !???!#!? I remove the console.logs and it stops
I also tried adding actions without the console.logs
Router.map(function() {
this.route('/', {
template: 'gallery',
data: function() {
var page = 1;
this.state.set('page', page);
return {
page: page,
};
},
action: function() { // <- added
var page = 1; // <- added
this.state.set('page', page); // <- added
this.render(); // <- added
}, // <- added
});
this.route('/gallery/:_page', {
template: 'gallery',
data: function() {
var page = parseInt(this.params._page);
this.state.set('page', page);
return {
page: page,
};
},
action: function() { // <- added
var page = parseInt(this.params._page); // <- added
this.state.set('page', page); // <- added
this.render(); // <- added
}, // <- added
});
});
That doesn't work either.
update
So using Router.current().data().??? seems to make it work. It's now this
Router.map(function() {
this.route('/', {
template: 'gallery',
data: function() {
var page = 1;
// <--- removed this.state() stuff
return {
page: page,
};
},
// <---- removed action stuff
});
this.route('/gallery/:_page', {
template: 'gallery',
data: function() {
var page = parseInt(this.params._page);
// <--- removed this.state() stuff
return {
page: page,
};
},
// <---- removed action stuff
});
});
helper
Template.gallery.onCreated(function() {
var instance = this;
instance.loaded = new ReactiveVar(0);
instance.autorun(function() {
var route = Router.current(); // <----- Moved from 3 lines up
var pageId = route.data().page; // <--------- CHANGED
var page = pageId - 1;
var skip = page * G_PAGE_SIZE;
var subscription = instance.subscribe('artForGrid', skip, G_PAGE_SIZE);
if (subscription.ready()) {
instance.loaded.set(1);
}
});
instance.art = function() {
return Art.find({});
};
});
Template.gallery.helpers({
art: function() {
return Template.instance().art();
},
});
No idea if this is now correct or if I'm just getting lucky like with the console.log editions
My guess is that your autorun function is not being re-run because you are not using any reactive variables. I'm not sure if Router.current() or Router.current().data() are reactive, but if they're not, then this explains the issue clearly for me. To test this, try putting some console logging in your autorun function.
Now, I would proposed some revisions that may work for you.
Routing code
Redirect to '/' URL to '/gallery/1' so you can re-use routing code
Router.route('/', function(){
this.redirect('/gallery/1');
});
Sending the page number in the data object to your template is important so that we can use this object in our autorun function
Router.route('/gallery/:_page', {
template: 'gallery',
data: function() {
return {
page: this.params._page,
};
}
});
Also, you don't need to use parseInt method unless you are trying to re-route or handle that exception in your routing code.
Gallery onCreated and helpers
There is a Template.subscriptionsReady helper provided by Meteor so you don't have to manually check if the subscription is loaded unless you really want it. Here's the Template.subscriptionsReady documentation
Here's a simplified version of your onCreated method using the template.data object which was built by the routing code above. Using the autorun wrapper is important so that it will re-run when template.data is changed by your router.
var G_PAGE_SIZE = 10;
Template.gallery.onCreated(function() {
var instance = this;
instance.autorun(function() {
// access the data object built by your route code
var pageId = instance.data.page;
var page = pageId - 1;
var skip = page * G_PAGE_SIZE;
instance.subscribe('artForGrid', skip, G_PAGE_SIZE);
});
});
Define access to your reactive data sources using the template.helpers method.
Template.gallery.helpers({
art: function() {
return Art.find({});
}
});
Note: Not necessary to define your helper in the onCreated and template.helpers

How to Call Pagemethod in Angular js Service

i am new in Angular js.
Static Method in Code Behind
[WebMethod]
public static string GetPracticeLocations()
{
return "[{ id: 1, name: Test AccountName}]";
}
Angular JS Service
appSettings.service('PracticeLocationsService', function () {
this.getPracticeLocations = function () {
var PracticeLocationsData = PageMethods.GetPracticeLocations();
return PracticeLocationsData;
};
});
i want to know how to implement page method in Angular JS.
Any helpful suggustein apricited
Thanks
Probably I'm too late, but you could use defer to call a page method and return a promise,
var getPracticeLocations = function () {
var deferred = $q.defer();
PageMethods.GetPracticeLocations(function (response) {
if (response) {
deferred.resolve({ result: angular.fromJson(response) });
} else {
deferred.resolve({ result: null });
}
},
function (msg, code) {
deferred.reject(msg);
});
return deferred.promise;
};
You want to look at factory() and $http
pseudo code:
angular.factory("PageMethods", ["$http", function($http){
return {
GetPracticeLocations : function(){
return $http.get("/someurl");
}
}
}]);

Initializing Google Maps as an AMD module

To initialize google.maps as an AMD module, compliant with twitter/flight and requirejs, use:
define([
'components/flight/lib/component',
'async!http://maps.google.com/maps/api/js?key=AIzaSyDp9D9Db1CWfeGUJ1bin45s2WKZN5sapuM&sensor=false'
], function(defineComponent){
return defineComponent(newMap);
function newMap () {
this.defaultAttrs({
// Selector
mapDiv: '#map',
// Map Canvas
mapCanvas: {},
// Initialized?
initializedMap: false
});
this.initializeMap = function () {
var mapCenter = new google.maps.LatLng(39.960664,-75.605488);
var mapOptions = {
zoom: 15,
center: mapCenter,
disableDefaultUI: true,
mapTypeId: google.maps.MapTypeId.ROADMAP,
};
this.attr.mapCanvas = new google.maps.Map(document.getElementById("map"), mapOptions);
if (this.attr.mapCanvas != {} ) {
this.attr.initializedMap = true;
this.trigger(document, 'mapInitialized', {
status: this.attr.initializedMap
});
};
// ### Map events
//-----------
// Mouse Up
google.maps.event.addListener(this.attr.mapCanvas, 'mouseup', function() {
this.trigger('mouseup');
});
// Zoom Changed
google.maps.event.addListener(this.attr.mapCanvas, 'zoom_changed', function() {
this.trigger('zoomChanged');
});
};
this.mouseup = function () {
console.log("what");
}
this.zoomChanged = function () {
console.log("is up");
}
this.after('initialize', function () {
this.on('mouseup', this.mouseup);
this.on('zoomChanged', this.zoomChanged);
this.on('initializeMap', this.initializeMap);
this.trigger('initializeMap');
});
}
});
I put together a Google Maps AMD loader plugin, which adds some functionality on top of the async! loader.
require.config({
googlemaps: {
params: {
key: 'abcd1234', // sets api key
libraries: 'geometry' // set google libraries
}
}
});
require(['googlemaps!'], function(gmaps) {
// google.maps available as gmaps
var map = new gmaps.Map('map-canvas');
});

this.get_element() of asp.net ajax is not working in functions I declare

I try to access the element in my own function through this.get_element() but it does not work.
Type.registerNamespace("LabelTimeExtender1");
LabelTimeExtender1.ClientBehavior1 = function(element) {
LabelTimeExtender1.ClientBehavior1.initializeBase(this, [element]);
var testelement=this.get_element();
var timestamp= this.get_element().attributes['TimeStamp'].value;
alert("in constructor");
},
LabelTimeExtender1.ClientBehavior1.prototype = {
initialize: function() {
LabelTimeExtender1.ClientBehavior1.callBaseMethod(this, 'initialize');
setInterval (this.timer,1000);
alert("after");
},
dispose: function() {
//Add custom dispose actions here
LabelTimeExtender1.ClientBehavior1.callBaseMethod(this, 'dispose');
},
timer: function(){
debugger;
var date= new Date(this.timestamp);
var datenow= new Date ();
this._element.innerText=" ";
if(date.getUTCFullYear<datenow.getUTCFullYear)
{
var myelement= this.get_element();
myelement .innerHTML= date.getUTCFullYear.toString();
}
if(date.getUTCMonth<datenow.getUTCMonth)
{
this.get_element().innerHTML=date.getUTCMonth.toString();
}
if(date.getUTCDay<datenow.getUTCDay)
{
this.get_element().innerHTML=date.getUTCDay.toString();
}
if(date.getUTCHours <datenow.getUTCHours )
{
this.get_element().innerHTML=date.getUTCHours .toString();
}
if(date.getUTCMinutes<datenow.getUTCMinutes)
{
this.get_element().innerHTML=date.getUTCMinutes.toString();
}
}
}
LabelTimeExtender1.ClientBehavior1.registerClass('LabelTimeExtender1.ClientBehavior1', Sys.UI.Behavior);
if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
Here I am trying to access the custom attribute 'TimeStamp' and calcutate the time and assign to the label to show.
Try to invoke your function through delegates.Then you will not have problem with [this] keyword
something like this:
setInterval (Function.createDelegate(this, this.timer),1000)

Resources