How to refresh an angular ui-map (google-map) - angularjs

I'm hiding the map initially with an ng-hide. When the ng-hide-expression evaluates to true, the map is not shown correctly. It is only partially shown and behaviour is also strange on dragging.
When I remove the ng-show attribute the map is shown correctly.
HTML:
<div ng-controller="MapCtrl">
<button ng-click="showMap()">Show map</button>
<div ng-show="showMapVar">
<div ng-repeat="marker in myMarkers" ui-map-marker="myMarkers[$index]"
ui-event="{}">
</div>
<div id="map_canvas" ui-map="myMap" style="height:200px;width:300px"
ui-event="{}" ui-options="mapOptions">
</div>
</div>
</div>
Javascript:
angular.module('doc.ui-map', ['ui.map'])
.controller('MapCtrl', ['$scope', function ($scope) {
$scope.myMarkers = [];
$scope.mapOptions = {
center: new google.maps.LatLng(35.784, -78.670),
zoom: 15,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
$scope.showMap = function(){
$scope.showMapVar = true;
}
}]) ;

Using ng-show merely sets the display property to none when the object is not supposed to be visible. This is messing with the height/width calculations.
On the other hand, ng-if (Angular 1.2) removes and re-creates the DOM, forcing a recomputation of the height/width. That should fix the problem.

I have my google map in a hidden tab,this solution worked for me,
assuming you have your map initialized and referenced with $scope.map :
<div id="googleMap" style="height:600px;" ng-show="resizeMap()"></div>
in your controller :
$scope.resizeMap = function(){
google.maps.event.trigger($scope.map, 'resize');
$scope.map.setCenter(0);
}

https://github.com/allenhwkim/angularjs-google-maps/issues/15 says
This happens because the map is initialized in hidden status. so the map has the size of its minimum width and height although the container is bigger
http://plnkr.co/edit/zLl2pJEnLzcq1rm07hLq?p=preview
As you see on the plunkr, when the map is initialized on visible DOM, it has the proper size.
That's the difference.
If you want to hide the map at the initial status, I would recommend to redraw the map after it's shown. This may help, How do I force redraw with Google Maps API v3.0?
That solved it for me

Related

How to hide <img> using md-switch

angular-material newbie here. I'm trying to hide some elements in our site using a switch, as instructed by our client. This led me to angular-material's md-switch.
So I tried incorporating it like so...
<md-switch md-no-ink aria-label="switchView" ng-model="switchView">{{switchView}}</md-switch>
And called the value of the switch in my element like this:
<img ng-src="{{photoPath}}" class="profilePic" ng-hide="switchView"/>
After testing it though, it didn't hide my <img> even though my switchView changed its value. Am I missing something here?
Other methods I've tried:
Adding ng-change to my md-switch, which called a function that would equate another variable (e.g. $scope.toggleView = $scope.switchView) with switchView's value. $scope.toggleView would then be used in my ng-hide.
ng-hide = "switchView == true".
Any advice would be very much appreciated. Thank you.
UPDATE 1: To test it, I tried hiding the <div> beside my <md-switch> and it worked perfectly. However it's still not working with my <img>.
Further checking revealed that it was inside a <nav> element. However they're both using the same controller. I wonder if that's the problem? I assumed that it shouldn't be a problem because of this.
The structure is like this:
<nav ng-controller="MainController">
<!-- other navigation elements here -->
<img ng-src="{{photoPath}}" class="profilePic" ng-hide="switchView"/>
</nav>
<div ng-controller="MainController">
<div>Toggle Switch</div>
<md-switch md-no-ink aria-label="switchView" ng-model="switchView">{{switchView}}</md-switch>
</div>
UPDATE 2: I've added the following code in my JS file because there are plans to hide elements in other pages. It still didn't work.
$scope.onChange = function(value) {
$scope.$broadcast("view_mode", $scope.switchView);
}
$scope.$on("view_mode", function(event, switchValue) {
$scope.viewThis= switchValue;
});
My HTML now looks like this:
<img ng-src="{{photoPath}}" class="profilePic" ng-hide="viewThis"/>
As for the controller, ngMaterial was called in a separate JS file (our main), together with all our dependencies and configs. Hence, this wasn't called inside MainController.
mainApp.js
var app = angular.module('myAppModule', [
// other references here
'ngMaterial'
]);
mySample.js
angular
.module('myAppModule')
.controller('MainController', ['$scope', function ($scope) {
// functions etc. go here
Hope this helps clear things up. Thank you.
Try something like this, its a trivial example but hope it helps. Here's a link to working codepen.
There seems to be a couple ways you could handle this according to the docs:
Angular Material- MD-Switch
function exampleController($scope) {
$scope.secondModel = false;
$scope.onChangeEvent = function(value) {
$scope.imgSource = (value) ? 'http://www.fillmurray.com/300/200' : 'http://www.fillmurray.com/g/155/300';
};
// alternatively: you could set the ternary to empty string value here.
}
angular
.module('BlankApp', ['ngMaterial'])
.controller('exampleController', exampleController);
<md-switch ng-model="switchValue" ng-change="onChangeEvent(switchValue)">
<img ng-src="{{imgSource}}" alt="" />
</md-switch>
<md-switch ng-model="secondModel">
<img src="http://www.fillmurray.com/300/200" alt="" ng-hide="secondModel" />
</md-switch>
Thank you to everyone who gave their inputs. After some research, I managed to solve this problem using factories (source).
I'm sharing my solution so that it may help others who experience the same problem.
HTML:
<nav ng-controller="MainController">
<!-- other navigation elements here -->
<img ng-src="{{photoPath}}" class="profilePic" ng-hide="switchView"/>
</nav>
<div ng-controller="MainController">
<div>Toggle Switch</div>
<md-switch md-no-ink aria-label="switchView" ng-model="switchView" ng-change="onChange(switchView)">{{switchView}}</md-switch>
</div>
JS:
angular
.module('myAppModule')
.controller('MainController', ['$scope', 'ViewModeFactory', function ($scope, ViewModeFactory) {
// functions etc. go here
// For client presentation mode
$scope.onChange = function(value) {
ViewModeFactory.setViewMode($scope.switchView);
}
$scope.$watch(function (){
$scope.switchView = ViewModeFactory.getViewMode();
});
}])
.factory('ViewModeFactory', function () {
var data = {isViewMode: ''};
return {
getViewMode: function () {
return data.isViewMode;
},
setViewMode: function(value) {
data.isViewMode = value;
}
};
});
I used factories so that other controllers in our site can use the value passed by md-switch.
Hope this helps.

ng-map: How can I get the Marker object when I click it?

I am using the pretty cool ng-map library for angular, and I want to know how to access the underlying Marker Object referenced by ng-map marker directive
So, I have this markup:
<div id="map-search" data-tap-disabled="true">
<map zoom="15" disable-default-u-i="true">
<marker ng-repeat=" pos in positions" position="{{pos.latitude}}, {{pos.longitude}}" id="{{pos.index}}" on-click="pinClicked()">
</marker>
</map>
</div>
I haven't found a straight-forward way of accessing the Google Maps Marker Object during a on-click event in this case; From the controller:
app.controller('MapSearchController', function($scope) {
$scope.$on('mapInitialized', function(event, map) {
$scope.map = map;
//Assume existence of an Array of markers
$scope.positions = generatePinPositionsArray();
});
$scope.pinClicked = function(event, marker) {
console.log('clicked pin!');
//HOW CAN I GET THE MARKER OBJECT (The one that was clicked) HERE?
console.log('the marker ->', marker); //prints undefined
};
});
Thanks in advance,
this is the answer to access the market object. It is the exactly the same as Google maps api, nothing is different. Again use this inside on-click function.
------ EDIT -----
There are so many examples in testapp directory, those are useful when you find usage of ng-map.
$scope.foo = function(event, arg1, arg2) {
alert('this is at '+ this.getPosition());
alert(arg1+arg2);
}
Example:
https://rawgit.com/allenhwkim/angularjs-google-maps/master/testapp/events.html
You need to create an array of markers ! That's (in my opinion) the only way. See here
UPDATE: Does something like this plunkr works for you?
The idea is to pass the marker on the event on-click="pinClicked(this)". And then you can catch it later on the controller: $scope.pinClicked = function(events, marker) {...}

ng-scrollbar is not working with ng-repeat

In my app I want to use a custom scrollbar for a div. So I used ng-scrollbar, it is working fine with static data. But whenever I get the data using ng-repeat it is not working. Please help me in this regard. Thanks in advance.
myFile.html
<style>
.scrollme {
max-height: 300px;
}
</style>
<div ng-app="myapp">
<div class="container" ng-controller="myctrl">
<button class="btn btn-info" ng-click="add();">add</button>
<button class="btn btn-warning" ng-click="remove();">remove</button>
<div class="well" >
<div class="scrollme" ng-scrollbar bottom rebuild-on="rebuild:me">
<h1>Scroll me down!</h1>
<p ng-repeat="mi in me">{{mi.name}}</p>
</div>
</div>
</div>
</div>
myCtrl.js
var myapp = angular.module('myapp', ["ngScrollbar"]);
myapp.controller('myctrl', function ($scope) {
$scope.me = [];
for(var i=1;i<=20;i++){
$scope.me.push({"name":i});
}
var a = $scope.me.length;
$scope.add = function(){
$scope.me.push({"name":$scope.me.length+1});
$scope.$broadcast('rebuild:me');
}
$scope.remove = function(){
$scope.me.pop();
}
});
Try adding the broadcast call to the end of your controller so it fires on controller load. If that doesn't work, try adding:
$timeout(function () {
$scope.$broadcast('rebuild:me');
}, 0);
// 0 optional, without it the time is assumed 0 which means next digest loop.
at the end of your controller code, not inside the add function. If this works but the previous approach doesn't then that means ngRepeat didn't finish rendering it's dynamic content in time for the ngScrollbar to properly update.
UPDATE: in general, you might have to wrap the broadcast inside of the add() function in a timeout as well. The reason I say this is that I suspect what's going on is that you add data to the scope variable and then broadcast all in the same function call. What might be happening is that the broadcast event is caught and scrollbar recalculates before ngRepeat sees the updated scope data and adds its extra DOM elements. Btw, if you want to recalculate the scrollbar on add(), then you also want to do this on remove() as well.
So your add function would become:
$scope.add = function(){
$scope.me.push({"name":$scope.me.length+1});
// wait until next digest loop to send event, this way ngRepeat has enough time to update(?)
$timeout(function () {
$scope.$broadcast('rebuild:me');
});
}
please try ng-scroll... another plugin, but without need of manual adjust.
mentioned on:
AngularJS with ng-scroll and ng-repeat
If you use jQuery, you can try jQuery Scrollbar - it has more options and fully CSS customizable.
Example with ng-repeat is here
JavaScript
var demoApp = angular.module('demoApp', ['jQueryScrollbar']);
demoApp.controller('SimpleController', function($scope){
$scope.me = [];
for(var i=1;i<=20;i++){
$scope.me.push({"name":i});
}
$scope.add = function(){
$scope.me.push({"name":$scope.me.length+1});
}
$scope.remove = function(){
$scope.me.pop();
}
$scope.jqueryScrollbarOptions = {
"onUpdate":function(container){
setTimeout(function(){
// scroll to bottom. timeout required as scrollbar restores
// init scroll positions after calculations
container.scrollTop(container.prop("scrollHeight"));
}, 10);
}
};
});
HTML
<div data-ng-app="demoApp">
<div data-ng-controller="SimpleController">
<button class="btn btn-info" ng-click="add();">add</button>
<button class="btn btn-warning" ng-click="remove();">remove</button>
<div class="scrollbar-dynamic" data-jquery-scrollbar="jqueryScrollbarOptions">
<h1>Scroll me down!</h1>
<p ng-repeat="mi in me">{{mi.name}}</p>
</div>
</div>
</div>
CSS
.scrollbar-dynamic {
border: 1px solid #FCC;
max-height: 300px;
overflow: auto;
}
This might be a bit late.
The problem is even though you have added the content to scope variable, angular has not finished adding p tags to your DOM. If you try a simple console log like
console.log($('.well').find('p').length);
After pushing content to $scope.me, you will understand what is happening. (Need jQuery at least to debug)
The solution is far more complicated than you can imagine.
STEP 1:
Add a ng-controller to your ng-repeat (Yes. It is allowed)
<p ng-repeat="mi in me" ng-controller="loopController">{{mi.name}}</p>
STEP 2: Define loopController
demoApp.controller('loopController', function($scope) {
$scope.$watch('$last', function(new_val) {
new_val && $scope.$emit('loopLoaded', $scope.$index);
});
});
This controller function is triggered whenever ng-repeat manipulates DOM. I'm watching $last which is a scope variable for ng-repeat. This will be set to true whenever, ng-repeat loads last element in DOM. When $last is set to true I emit one event loopLoaded. Since you are pushing values into $scope.me using a loop, this event will be triggered for every push.
STEP 3: Event handling
In your SimpleController (not simple anymore, eh?)
$scope.$on('loopLoaded', function(evt, index) {
if (index == $scope.me.length-1) {
$scope.$broadcast('rebuild:me');
}
});
Once all the p elements are loaded, index sent to event will be equal to $scope.me.length-1. So you call scroll rebuild. That's it.
Here's a reference I used - AngularJS - Manipulating the DOM after ng-repeat is finished

Google maps for AngularJS

I am very new to AngularJS and I am trying to use https://github.com/angular-ui/angular-google-maps.
I am just trying to get the map to render on my page but I am getting an error message and I don't know what it means. Any help with understanding this error message would be appreciated.
I've created a plunk here:
github.com/twdean/plunk
Here is the Error in the browser:
Error: [$compile:multidir] Multiple directives [googleMap, markers] asking for new/isolated scope on: <google-map center="center" draggable="true" zoom="zoom" markers="markers" mark-click="true" style="height: 400px">
http://errors.angularjs.org/1.2.3/$compile/multidir?p0=googleMap&p1=markers&p2=new%2Fisolated%20scope&p3=%3Cgoogle-map%20center%3D%22center%22%20draggable%3D%22true%22%20zoom%3D%2
I suggest another alternative, ng-map.
I created a google maps angularjs directive called, ng-map, for my own project. This does not require any Javascript coding for simple functionality.
To Get Started
One. Download and include ng-map.js or ng-map.min.js
<script src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script src="dist/ng-map.min.js"></script>`
Two. use map tag, and optionally, control, marker, and shape tags
<ng-map zoom="11" center="[40.74, -74.18]">
<marker position="[40.74, -74.18]" />
<shape name="circle" radius="400" center="[40.74,-74.18]" radius="4000" />
<control name="overviewMap" opened="true" />
</ng-map>`
Examples
To use it in your app, simply include 'ngMap' as dependency to your app.
var myApp = angular.module('myApp', ['ngMap']);
Hope it helps
-------------- EDIT -------------------
For those who looks for angular2 version,
there is also ng2-map https://ng2-ui.github.io/#/google-map
It seems that you're including a "markers" attribute within your google-map element.
Indeed as #Tyler Eich mentions, the docs as of Dec 5, 2013 remain outdated. Removing the marker attribute seems to make it work.
Here is how to display markers:
index.html
<div ng-controller="MapCtrl">
<google-map id="mymap"
center="center"
zoom="zoom"
draggable="true"
mark-click="false">
<markers>
<marker ng-repeat="marker in markers" coords="marker">
</marker>
</markers>
</google-map>
</div>
controller.js
var controller = module.controller('MapCtrl', function($scope) {
$scope.myMarkers = [
{
"latitude":33.22,
"longitude":35.33
},
...
];
$scope.center = {
latitude: 33.895497,
longitude: 35.480347,
};
$scope.zoom = 13;
$scope.markers = $scope.myMarkers;
$scope.fit = true;
});
I have just today had a need to implement a google map into my angular application. Since my implementation required something quite simple I decided to do this myself and create a simple directive. I will leave my source below for anyone who might find it usefull.
Create a directive
Create HTML
Optionally pass the coordinates through directive/controller if
you need.
DIRECTIVE
angular.module('ngPortalApp')
.directive('googleMap', function () {
return {
template: '<iframe width="100%" height="350" frameborder="0" style="border:0"></iframe>',
restrict: 'E',
scope: {
pbcode: '='
},
link: function postLink(scope, element) {
var mapFrame = element.find("iframe");
if (scope.pbcode) {
mapFrame.attr('src', "https://www.google.com/maps/embed?pb=" + scope.pbcode);
}
else {
mapFrame.attr('src', '');
}
}
};
});
HTML
<div class="col-lg-12">
<google-map pbcode="'!1m0!3m2!1sen!2srs!4v1445332712674!6m8!1m7!1s34JkuBgIzmIJNmpFNThuUg!2m2!1d40.75816449978445!2d-73.98911289129973!3f175.51693470959802!4f7.069842517148402!5f0.7820865974627469'"></google-map>
</div>
Thats all there is to it. Remember that the google query string is called "pb" this is the code used in all embed codes you can grab from google. You may pass this to your directive straight up or if you need to via your controller or even through the directive itself. You can set any iframe map settings in the directive within the template.
To change between Street View or Map View you just send the corresponding code whichever source you are getting them from (db, json, etc).
For the above example:
Street Code:
!1m0!3m2!1sen!2srs!4v1445332712674!6m8!1m7!1s34JkuBgIzmIJNmpFNThuUg!2m2!1d40.75816449978445!2d-73.98911289129973!3f175.51693470959802!4f7.069842517148402!5f0.7820865974627469
Map Code:
!1m18!1m12!1m3!1d755.5452773113641!2d-73.98946157079955!3d40.75804119870795!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x0000000000000000%3A0x1952a6258d36ecc5!2sMcDonald's!5e0!3m2!1sen!2srs!4v1445332854795
Haven't had any issues with this as yet.
Here's a fiddle: jsFiddle
If you want to include markers you should do that in another element inside the google-maps directive.
You should do something like:
<google-maps options="someOptionsObject">
<markers cords="someArrayWithLatitudeAndLongitudeCords"></markers>
</google-maps>
For Angular2, there is angular2-google-maps. License as of 2016-05-14: MIT.

Angularjs UI always show last infowindow

I'm using angularjs UI-map, I loop the data from firebase.
So far so good, but when I click on the markers and it always display the last query data.
here is snipper code
In html
<!-- Setup Marker -->
<div ng-repeat="marker in markers"
ui-map-marker="markers[$index]"
ui-event="{'map-click': 'showMarkerInfo(marker)'}">
</div>
<!-- Setup Marker Info Window -->
<div ui-map-info-window="infoWindow">
<div>Marker - <input ng-model="full_name"></div>
<div>Message</div>
</div>
In JS
firebaseAuth.firebaseRef.child('/human/').on('child_added', function(snapshot) {
$scope.users = snapshot.val();
$scope.latLng = new google.maps.LatLng($scope.users.lat, $scope.users.lng);
$scope.markers.push(new google.maps.Marker({
map: $scope.map,
position: $scope.latLng
}));
});
$scope.showMarkerInfo = function(marker) {
$scope.currentMarkerLat = $scope.users.full_name;
$scope.infoWindow.open($scope.map, marker);
console.log(marker);
};
I haven't tried angular-ui, but this problem looks the same as the "Javascript infamous Loop problem", so you might want to try:
ui-event="{'map-click': 'showMarkerInfo(markers[$index])'}"
However, I guess it will suffer the same problem, but it's worth a try.

Resources