Ionic + ngCordova + background geolocation + DeviceReady issue - angularjs

I'm trying to get the background geolocation plugin to work in my app; however, the page only sometimes loads on my device when I use the deviceready function. From my googling, it seems that I should be using $ionicPlatform.ready instead, but $cordovaBackgroundGeolocation is undefined when I try to do that. Similarly, the device is always undefined when I try to do anything with it.
I also tried manually bootstrapping angular, that didn't work; and I tried simply running the function without putting it inside deviceready or $ionicPlatform.ready or anything; that didn't work either.
The code in question:
Controller:
// Define the angular module
angular.module('testApp.controllers', ['ionic', 'ngCordova.plugins.geolocation', 'ngCordova.plugins.backgroundGeolocation'])
.controller('MapCtrl', ['$scope', '$ionicPopup', '$cordovaGeolocation', '$cordovaBackgroundGeolocation', '$timeout', '$http', '$ionicPlatform',
function ($scope, $ionicPopup, $cordovaGeolocation, $cordovaBackgroundGeolocation, $timeout, $http, $ionicPlatform) {
$scope.loaded = false;
var posOptions = { timeout: 5000, enableHighAccuracy: true, maximumAge: 5000 };
$cordovaGeolocation.getCurrentPosition(posOptions)
.then(function (location) {
$scope.currentLat = location.coords.latitude;
$scope.currentLong = location.coords.longitude;
$scope.loaded = true;
});
$ionicPlatform.ready(function() {
var bgGeo = $cordovaBackgroundGeolocation;
// BackgroundGeoLocation is highly configurable.
bgGeo.configure({
url: 'http://www.my_api_url_here/',
params: {
deviceId: "testApp",
"location": {
"latitude": "38.896339999999995",
"longitude": "-77.08521460000001"
}
},
desiredAccuracy: 10,
stationaryRadius: 20,
distanceFilter: 30,
notificationTitle: 'TestTitle', // <-- android only, customize the title of the notification
notificationText: 'TestText', // <-- android only, customize the text of the notification
activityType: 'OtherNavigation',
debug: true, // <-- enable this hear sounds for background-geolocation life-cycle.
stopOnTerminate: false // <-- enable this to clear background location settings when the app terminates
});
bgGeo.start();
});
}])
Directive:
.directive('bgeo', ['$cordovaGeolocation', '$cordovaBackgroundGeolocation', '$http',
function ($cordovaGeolocation, $cordovaBackgroundGeolocation, $http) {
return {
scope: {
lat: '=',
lng: '='
},
link: function (scope) {
console.log("directive: ", scope.lat, scope.lng);
myLatLng = new google.maps.LatLng(scope.lat, scope.lng);
mapOptions = {
zoom: 16,
center: myLatLng
};
map = new google.maps.Map(document.getElementById('map'), mapOptions);
marker = new google.maps.Marker({
position: myLatLng,
map: map,
draggable: false,
icon: 'small-orange-pin.png'
});
}
}
}])
Template:
<ion-scroll zooming="true" direction="xy" style="width:90%">
<div ng-if="loaded" bgeo lat="currentLat" lng="currentLong">
<div id="map" style="width: 600px; height: 500px;"></div>
</div>
</ion-scroll>
app.js run method:
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
if(window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
if(window.StatusBar) {
StatusBar.styleDefault();
}
if (window.cordova) {
if (window.plugins && window.plugins.backgroundGeoLocation) {
BackgroundGeolocation.configurePlugin(window.plugins.backgroundGeoLocation);
}
}
});
})
The full source code is up on github at https://github.com/sahiltalwar88/binding-geolocation-issue. Any help is much appreciated!

The main issue was that I had to run bower install; I was missing several packages. Once I did that, I could use the ionic ready function and onDeviceReady just fine. Then, in order to get the iOS callback functions working, I had to update my syntax to work with ngCordova (which uses Q and promises) rather than callback functions, as the examples showed.
Here's the structure of my final code:
$ionicPlatform.ready(function() {
if(window.StatusBar) {
StatusBar.styleDefault();
}
$location.path('/app');
$rootScope.$digest();
$rootScope.deviceReady = false;
document.addEventListener('deviceready', function () {
if(window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
window.cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
var bgGeo = $cordovaBackgroundGeolocation;
var deviceId = $cordovaDevice.getUUID();
var addVisitUrl = 'your-url-goes-here';
$rootScope.deviceId = deviceId;
$rootScope.deviceReady = true;
var posOptions = { timeout: 5000, enableHighAccuracy: true, maximumAge: 5000 };
$cordovaGeolocation.getCurrentPosition(posOptions)
.then(function (location) {
$rootScope.currentLat = location.coords.latitude;
$rootScope.currentLong = location.coords.longitude;
var yourAjaxCallback = function(response) {
bgGeo.finish();
};
var callbackFn = function(location) {
var data = {
deviceId: deviceId,
"location": {
"latitude": location.latitude,
"longitude": location.longitude
}
};
$http.post(addVisitUrl, data);
// Other code goes here
yourAjaxCallback.call(this);
};
var failureFn = function(error) {
alert('Background Geolocation Error: ' + error);
// Other code goes here
};
bgGeo.configure({
url: addVisitUrl,
params: {
deviceId: deviceId,
"location": {
"latitude": $rootScope.currentLat,
"longitude": $rootScope.currentLong
}
},
desiredAccuracy: 10,
stationaryRadius: 10,
distanceFilter: 10,
activityType: 'OtherNavigation',
debug: true,
stopOnTerminate: false
})
.then(callbackFn, failureFn, callbackFn);
bgGeo.start();
});
$rootScope.$digest();
});
});
})

Related

Videogular Angular video player problems with async data and `this.config` in controller

I'm using the Videogular video player for Angular. The HTML code looks like this:
<div ng-controller="ShowController as controller" class="videogular-container">
<videogular vg-theme="controller.config.theme.url">
<vg-media vg-src="controller.config.sources" vg-tracks="controller.config.tracks" vg-native-controls="true"></vg-media>
</videogular>
</div>
In the controller the code looks like this to play my video that is stored in Firebase Storage:
app.controller('ShowController', ['$sce', function($sce) {
this.config = {
preload: "auto",
sources: [
{src: $sce.trustAsResourceUrl("https://firebasestorage.googleapis.com/v0/b/myFirebaseApp.appspot.com/o/videos%2Fen%2Fkingfisher.webm?alt=media&token=b4840120-e531-4699-a757-4e0d999ce9d1"), type: "video/webm"}
],
theme: {
url: "http://www.videogular.com/styles/themes/default/latest/videogular.css"
}
};
}]);
That works great, as long I only want to play one video. But to dynamically select from an array of videos, I wrote this:
app.controller('ShowController', ['$scope', '$firebaseStorage', '$sce', function($scope, $firebaseStorage, $sce) {
var ref = firebase.database().ref();
var obj = $firebaseObject(ref.child($routeParams.id));
obj.$loaded(
function(data) {
console.log("Loaded!")
console.log(data === obj);
$scope.wordObject = data;
console.log($scope.wordObject.videos[0].videoURL);
console.log($scope.wordObject.videos[0].videoMediaFormat);
this.config = {
preload: "auto",
sources: [
{src: $sce.trustAsResourceUrl($scope.wordObject.videos[0].videoURL), type: "video/" + $scope.wordObject.videos[0].videoMediaFormat}
],
theme: {
url: "http://www.videogular.com/styles/themes/default/latest/videogular.css"
}
};
},
function(error) {
console.log("Error: ", error)
});
}]);
The videoURL and videoMediaFormat log just fine. But neither the video source nor the theme loads into the HTML view. The problem appears to be that moving this.config changed the object that this refers to. What is this.config doing?
Can I bind this to the controller using call or apply?
I figured it out:
app.controller('ShowController', ['$scope', '$firebaseObject', '$sce', function($scope, $firebaseObject, $sce) {
// Create Firebase reference
var ref = firebase.database().ref();
var obj = $firebaseObject(ref.child($routeParams.id));
$scope.wordObject = obj;
var controller = this;
obj.$loaded(
function(data) {
console.log(data === obj);
$scope.wordObject = data;
// video player
controller.config = {
preload: "auto",
sources: [
{src: $sce.trustAsResourceUrl($scope.wordObject.videos[0].videoURL), type: "video/" + $scope.wordObject.videos[0].videoMediaFormat},
],
theme: {
url: "http://www.videogular.com/styles/themes/default/latest/videogular.css"
}
};
},
function(error) {
console.log("Error: ", error)
});
}]);

Angular leaflet - Showing multiple marker issue

I am using the following code to add markers in leaflet:
.controller('MapController',
[ '$scope',
'$cordovaGeolocation',
'$stateParams',
'$ionicModal',
'$ionicPopup',
'$http',
function(
$scope,
$cordovaGeolocation,
$stateParams,
$ionicModal,
$ionicPopup,
$http
) {
$scope.$on("$stateChangeSuccess", function() {
$scope.map = {
defaults: {
tileLayer: 'http://{s}.tile.osm.org/{z}/{x}/{y}.png',
maxZoom: 18,
zoomControlPosition: 'bottomleft'
},
markers : {},
events: {
map: {
enable: ['context'],
logic: 'emit'
}
}
};
$scope.locate();
});
$scope.locate = function(){
$scope.map.center = {
lat : location.lat,
lng : location.lng,
zoom : 12,
};
var Location = function() {
if ( !(this instanceof Location) ) return new Location();
this.lat = "";
this.lng = "";
this.name = "";
};
$ionicModal.fromTemplateUrl('templates/addLocation.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.modal = modal;
});
$scope.map.markers.push=({
lat:35.654,
lng:73.244,
message:'demo 1'
})
$scope.map.markers.push=({
lat:38.654,
lng:73.244,
message:'demo 2'
})
$scope.$on('leafletDirectiveMap.click', function(event, locationEvent){
$scope.newLocation = new Location();
$scope.newLocation.lat = locationEvent.leafletEvent.latlng.lat;
$scope.newLocation.lng = locationEvent.leafletEvent.latlng.lng;
$scope.modal.show();
});
$scope.saveLocation = function(lat, lang) {
//LocationsService.savedLocations.push($scope.newLocation);
//alert(lat + " - " + lang);
var link = 'http://192.168.5.110/server/addLocation.php';
var json1 = {l1 : lat, l2 : lang , l3: sessionStorage.getItem('loggedin_phone')};
$http.post(link, { data: json1 })
.then(function (res){
$scope.response = res.data.result;
if($scope.response.created=="1"){
$scope.title="Thank You";
$scope.template="Mobile Toilet Added!";
//no back option
/*
$ionicHistory.nextViewOptions({
disableAnimate: true,
disableBack: true
});
$state.go('login', {}, {location: "replace", reload: true});
*/
}else if($scope.response.exists=="1"){
$scope.title="Duplication";
$scope.template="This Location is already added!";
}else{
$scope.title="Failed";
$scope.template="Contact Our Technical Team";
}
var alertPopup = $ionicPopup.alert({
title: $scope.title,
template: $scope.template
});
});
$scope.modal.hide();
};
$cordovaGeolocation
.getCurrentPosition()
.then(function (position) {
$scope.map.center.lat = position.coords.latitude;
$scope.map.center.lng = position.coords.longitude;
$scope.map.center.zoom = 18;
$scope.map.markers.now = {
lat:position.coords.latitude,
lng:position.coords.longitude,
focus: true,
draggable: false,
//message: ''
};
}, function(err) {
// error
console.log("Location error!");
console.log(err);
});
};
}]);
But only the demo2 marker is displaying.
Is there a way to show multiple markers on the leaflet map by using JSON data of latitudes and longitudes loaded from API?
<leaflet defaults="defaults" event-broadcast="events" lf-center="center" markers="markers" layers="layers" id="global-map" width="100%" height="240px"></leaflet>
<leaflet defaults="defaults2" event-broadcast="events2" lf-center="center2" markers="markers2" layers="layers2" id="global-map2" width="100%" height="240px"></leaflet>

ngMaterial error in Angular app: TypeError: Cannot read property 'confirm' of undefined

I am trying to use ngMaterial in my Angular app and getting the errors: ReferenceError: $event is not defined and TypeError: Cannot read property 'confirm' of undefined. I have looked at the ngMaterial docs and am using the code from this example: https://material.angularjs.org/latest/demo/dialog but can't find what I am doing wrong.
The ReferenceError refers to this line:
$timeout($scope.showConfirm($event), 500);
And the TypeError refers to:
var confirm = $mdDialog.confirm()
Below is my code:
html:
<div style="height: 100%">
<h1>TEST</h1> <!--NOT SHOWING ON PAGE WHICH MEANS THIS IS NOT BEING INVOKED WHEN
URL IS: '/getLocation' like it should-->
<ui-gmap-google-map
class="md-primary md-raised" ng-click="showConfirm($event)"
center='map.center'
zoom='map.zoom'
draggable='map.draggable'
dragging='map.dragging'
refresh='map.refresh'
options='map.options'
events='map.events'
pan='map.pan'>
<ui-gmap-marker
idKey='1'
events='map.events'
coords='map.center'
>
</ui-gmap-marker>
</ui-gmap-google-map>
<script src='//maps.googleapis.com/maps/api/js?key=AIzaSyD4_0KuPivZyV-1EwNGmBCgLc_Z0o8Dyw8'></script>
</div>
Controller:
(function (window, ng) {
ng.module('app', ['uiGmapgoogle-maps', 'ui.router', 'ngMaterial'])
.config(function ($stateProvider) { //had: , $stateChangeError included in the function parameters, but that caused error
$stateProvider.state('getLocation', {
url: '/getLocation',
templateUrl: 'getlocation.html',
controller: 'GetlocationCtrl',
});
})
.controller('GetlocationCtrl', ['$scope', "uiGmapLogger", "uiGmapGoogleMapApi", "$state", "$stateParams", "$timeout",
function ($scope, $log, GoogleMapApi, $state, $stateParams, $timeout, $mdDialog) {
$log.currentLevel = $log.LEVELS.debug;
center = { latitude: 49.22, longitude: -122.66 }; //default center
console.log($stateParams);
$scope.map = {
center: center,
pan: false,
zoom: 16,
refresh: false,
events:
{ click: function newCenter(mapModel, eventName, originalEventArgs)
{
$scope.$apply(function(){
var e = originalEventArgs[0];
$scope.map.center = { latitude: e.latLng.lat(), longitude: e.latLng.lng() };
$timeout($scope.showConfirm($event), 500);
});
}
}
}
$scope.map.circle = {
id: 1,
center: center,
radius: 500, //(current time - date lost)*km/hour
stroke: {
color: '#08B21F',
weight: 2,
opacity: 1
},
fill: {
color: '#08B21F',
opacity: 0.5
},
geodesic: false, // optional: defaults to false
draggable: false, // optional: defaults to false
clickable: true, // optional: defaults to true
editable: false, // optional: defaults to false
visible: true, // optional: defaults to true
events: {
dblclick: function () {
$log.debug("circle dblclick");
},
radius_changed: function (gObject) {
var radius = gObject.getRadius();
$log.debug("circle radius radius_changed " + radius);
}
}
};
$scope.showConfirm = function (ev) {
// Appending dialog to document.body to cover sidenav in docs app
var confirm = $mdDialog.confirm()
.title('Would you like to delete your debt?')
.textContent('All of the banks have agreed to forgive you your debts.')
.ariaLabel('Lucky day')
.targetEvent(ev)
.ok('Please do it!')
.cancel('Sounds like a scam');
$mdDialog.show(confirm).then(function() {
$scope.status = 'You decided to get rid of your debt.';
}, function() {
$scope.status = 'You decided to keep your debt.';
});
};
} ]); //end of controller
//THIS LISTENER DOES NOT DO ANYTHING
// $scope.map.event.addListener(map, 'click', function(event) {
// alert(event.latLng); // in event.latLng you have the coordinates of click
//});
//function confirmLocation() {
// alert('Use this location as {{petName}}\'s last location?')
//}
})(window, angular);
Your controller does not have mdDialog injected, and there is a issue in the way you are injecting, change like below,
controller('GetlocationCtrl', ['$scope', "uiGmapLogger", "uiGmapGoogleMapApi", "$state", "$stateParams", "$timeout","$mdDialog",
function ($scope,uiGmapLogger, uiGmapGoogleMapApi, $state, $stateParams, $timeout, $mdDialog) {

How to use Angular geolocation directive with google map ?

I am using Angular geolocation for get location. Its returning latitude and longitude of location. I want to show map using this latitude and longitude. Also want to show a circle with 5 km. take a look of my code index.html
<html>
<head>
<title>ngGeolocation</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.3/angular.js"></script>
<script src="./ngGeolocation.js"></script>
<script src="./index.js"></script>
</head>
<body ng-app="geolocationDemo">
<div ng-controller="AppController">
<h1>Basic (fetch once)</h1>
Latitude: {{location.coords.latitude}}
<br />
Longitude: {{location.coords.longitude}}
<br />
</div>
</body>
</html>
Index.js
angular
.module('geolocationDemo', ['ngGeolocation'])
.controller('AppController', function($scope, $geolocation){
$scope.$geolocation = $geolocation
// basic usage
$geolocation.getCurrentPosition().then(function(location) {
$scope.location = location
});
// regular updates
$geolocation.watchPosition({
timeout: 60000,
maximumAge: 2,
enableHighAccuracy: true
});
$scope.coords = $geolocation.position.coords; // this is regularly updated
$scope.error = $geolocation.position.error; // this becomes truthy, and has 'code' and 'message' if an error occurs
//console.log($scope.coords);
});
ngGeolocation.js
angular
.module('ngGeolocation', [])
.factory('$geolocation', ['$rootScope', '$window', '$q', function($rootScope, $window, $q) {
function supported() {
return 'geolocation' in $window.navigator;
}
var retVal = {
getCurrentPosition: function(options) {
var deferred = $q.defer();
if(supported()) {
$window.navigator.geolocation.getCurrentPosition(
function(position) {
$rootScope.$apply(function() {
retVal.position.coords = position.coords;
deferred.resolve(position);
});
},
function(error) {
$rootScope.$apply(function() {
deferred.reject({error: error});
});
}, options);
} else {
deferred.reject({error: {
code: 2,
message: 'This web browser does not support HTML5 Geolocation'
}});
}
return deferred.promise;
},
watchPosition: function(options) {
if(supported()) {
if(!this.watchId) {
this.watchId = $window.navigator.geolocation.watchPosition(
function(position) {
$rootScope.$apply(function() {
retVal.position.coords = position.coords;
delete retVal.position.error;
$rootScope.$broadcast('$geolocation.position.changed', position);
});
},
function(error) {
$rootScope.$apply(function() {
retVal.position.error = error;
delete retVal.position.coords;
$rootScope.$broadcast('$geolocation.position.error', error);
});
}, options);
}
} else {
retVal.position = {
error: {
code: 2,
message: 'This web browser does not support HTML5 Geolocation'
}
};
}
},
position: {}
//console.log(position);
};
return retVal;
}]);
How to i can do it ? Please suggest me some solutions.
Have a look at angular-google-maps, you should be able to create the map and circle:
http://angular-ui.github.io/angular-google-maps/

Plupload + AngularJS UI modal doesn't work in IE

I've already seen a lot of articles about plupload and modal dialogs in old versions of IE, but any of them had a solution for my problem. I'm using AngularJS UI to open modals which contain the container div of plupload, and I need to do this work in this way.
I've tried all the solutions: uploader.refresh(), I've used require.js to load the plupload script when the dialog was already opened, but I still haven't found one that works.
Here's the function of the controller that calls the modal dialog:
$scope.EnviarNovoAnexoClick = function () {
var modalInstance = $modal.open({
templateUrl: '/Dialog/EnviarAnexo',
controller: 'EnviarAnexoDialogController',
resolve: {
documentoId: function () {
return $scope.documentoId;
}
}
});
modalInstance.result.then(function (anexo) {
$scope.documento.anexos.push(anexo);
}, function () {//dismiss callback
});
}
Here's the function that calls the uploader:
require(["/Scripts/plupload.full.js"], function (util) {
$scope.anexoUploader = new plupload.Uploader({
runtimes: 'gears,html5,flash,silverlight,browserplus,html4',
browse_button: 'anexoBtUpload',
container: 'anexoUploadDiv',
unique_names: true,
multi_selection: false,
max_file_size: '150mb',
chunk_size: '64kb',
url: '/Documento/Upload',
flash_swf_url: '/Scripts/plupload.flash.swf',
silverlight_xap_url: '/Scripts/plupload.silverlight.xap',
resize: { width: 320, height: 240, quality: 90 },
filters: [
{ title: "PDFs ", extensions: "pdf" },
{ title: "Imagens", extensions: "jpg,gif,png" },
{ title: "Zips", extensions: "zip" },
{ title: "Todos", extensions: "*" }
],
init: {
FilesAdded: function (up, files) {
if ($scope.uploadDocumento == null) {
$scope.showOrigemAnexo = false;
$scope.novoAnexo.upload = {};
$scope.InicializaUpload($scope.novoAnexo.upload);
$scope.uploadDocumento = $scope.novoAnexo.upload;
}
var fileName = $scope.anexoUploader.files[$scope.anexoUploader.files.length - 1].name;
$scope.uploadDocumento.nome = fileName;
$scope.novoAnexo.descricao = dotlessName(fileName);
$scope.$apply();
up.refresh(); // Reposition Flash/Silverlight
up.start();
},
UploadProgress: function (up, file) {
$scope.uploadDocumento.size = file.size;
$scope.uploadDocumento.percentage = file.percent;
$scope.$apply();
},
FileUploaded: function (up, file, response) {
$scope.uploadDocumento.id = file.id;
$scope.uploadDocumento.size = file.size;
$scope.$apply();
}
}
});
$scope.anexoUploader.init();
});
The file dialog is opening in Chrome, IE10 and Firefox, but I need that it works on IE9 and 8.
Thanks (:
This has something to do with caching and dynamically loaded script tag.
Solution that worked for me:
Add this directive:
.directive('cachedTemplate', ['$templateCache', function ($templateCache) {
"use strict";
return {
restrict: 'A',
terminal: true,
compile: function (element, attr) {
if (attr.type === 'text/ng-template') {
var templateUrl = attr.cachedTemplate,
text = element.html();
$templateCache.put(templateUrl, text);
}
}
};
}])
Declare your modal content in
<div data-cached-template="myInnerModalContent.html" type="text/ng-template">
<!-- content -->
</div>
You may need this style as well:
*[type="text/ng-template"]{
display: none;
}
In controller:
$scope.open = function() {
var modalInstance = $modal.open({
templateUrl: 'ModalContent.html',
controller: modalInstanceCtrl
});
};
Reference: http://blog.tomaszbialecki.info/ie8-angularjs-script-cache-problem/

Resources