Find a marker in a leaflet markercluster group - angularjs

I want to find a marker by it's marker.title property in a leaflet markercluster group. I'm using the angular-leaflet directive (v 0.7.5) with angular (1.2.8). The marker data is provided by a service. Here is the code I'm using:
$timeout(function(){
leafletData.getLayers().then(function(layers) {
$scope.markerClusterGrp = layers.overlays.locations;
var clusters = $scope.markerClusterGrp.getLayers();
for (var i in clusters){
if (marker.title == clusters[i].options.title) {
childMarker = clusters[i];
break;
}
}
});
},1000);
This code works. However after I apply a filter to $scope.markers, the list of options.titles returned by above function is different from the list of marker.titles that's in $scope.markers. I've created a jsfiddle to illustrate this behavior. Please look at the console.log statements to see it in action. I am not sure if this is a bug or some error in my code.
http://jsfiddle.net/mukhtyar/z2Ucr/
My end goal is to find the visible parent of the marker i'm interested in and highlight the parent cluster in a mouseover event. Any help in explaining this behavior or suggestions on a different way to approach this problem are much appreciated! Thank you.
Edit: I got around this by using the clusters latlng instead of title.
//Iterate through all the markers
for (var i in clusters){
if ((marker.lat == clusters[i]._latlng.lat) &&
(marker.lng == clusters[i]._latlng.lng)) {
childMarker = clusters[i];
break;
}
}

Related

Adding views using for loop to scrollable view in appcelerator

Can anyone please tell me how to add an array of views using a for loop to a scrollable view in android. The answers that I surfed online are not giving me a clear idea and are way too confusing. Any suggestions?
You have (at least) two options. If you have a lot of work going on for each list item (e.g. showing a thumbnail for a picture) then you may want to use the second approach. Otherwise, you may just use this simple approach (where I show statistics for a number of species - layout controlled in a separate controller). stats is my list:
function showSpecies(stats) {
_.each(stats, function(record){
$.form.add(Alloy.createController('viewStatsRow', {record:record}).getView());
});
}
In this example I have more work going on for "building" every item. So to avoid locking the thread I use a list as a "queue" and just handles the first item - and then call the function with the remaining list until it is empty:
var work = [];
function showNextItem(work,first){
if(work && work.length > 0){
// Progressively show list....
if($.boastList && $.boastList.sections[0]){
var list = [];
list.push(buildOneItem(work.shift())); // Take first element
if(first){
$.boastList.sections[0].items = list; // Replace list
}else{
$.boastList.sections[0].appendItems(list); // append item
}
// Free queue to allow other actions
setTimeout(function(){
showNextItem(work); // Call recursively...
},30);
}
}else{
// All boasts shown...
}
}
function showBoastlist(){
work = [];
DataFactory.boasts.find({}, {$sort:{sortTime:-1}}, function(result){
result.forEach(function(record) {
work.push(record);
});
});
showNextItem(work,true);
}
The buildOneItem function just returns an item ready to be added to the view.
Not sure if this was what you asked for - but hope you can use it ;-)
Happy coding!
/John

Ask to confirm when changing tabs in angular bootstrap

I have tabs with forms and I want ask the user to confirm or discard their changes when changing tabs. My current code works
<uib-tab heading="..." index="3" deselect="main.onDeselect($event)" ... >
this.onDeselect = function($event) {
if(...isDirty...) {
if($window.confirm("Do you want to discard?")) {
... discard (and go to new tab) ...
} else {
$event.preventDefault(); //stays on current tab
}
}
}
The problem is I want to change confirm to javascript dialog and I will get result in callback.
I planed to preventDefault() all and then switch manually, but I cannot figure out where to get new tab id.
Any solution is appreciated. Even if it is easier in other tab implementations.
I use AngularJS v1.4.7, ui-bootstrap-tpls-1.3.3.min.js
You can make use of $selectedIndex and the active property for that purpose. See this Plunk
One thing to be noted here is that when we manually change the active property, it again fires the deselect event which needed to be handled. Otherwise it seems to do what you wanted.
Edit
Indeed as noted in the comments, the deselect carries the HTML index rather than what is passed in in the tab index property. A workaround could be in this: Another Plunk. Here I'm pulling the actual index from the HTML index.
And a little research indicates that this issue might as well be fixed already with 3.0 bootstrap tpl See this.
I spent some time with different approaches and this one is stable for some time. What I do is to prevent deselect at the beginning and set the new tab in callback if confirmed to loose changes...
this.onDeselect = function($event, $selectedIndex) {
var me = this;
if(this.tabs.eventDirty || this.tabs.locationDirty || this.tabs.contractDirty) {
$event.preventDefault();
var alert = $mdDialog.confirm({
title: 'Upozornění',
textContent: 'Na záložce jsou neuložené změny. Přejete si tyto změny zrušit a přejít na novou záložku?',
ok: 'Přijít o změny',
cancel: 'Zůstat na záložce'
});
$mdDialog
.show( alert )
.then(function() {
$rootScope.$emit("discardChanges");
me.tabs.activeTab = $selectedIndex;
})
}
};

AngularFire - manage child_added and other events on $asArray

Hello guys !
I wanted to do something pretty simple : display the last 10 posts with the newer at the top, and when someone posts a new one, display a bar "click here to see x new posts" that when clicked displays the new ones.
My problem : when a new posts enters Firebase, it immediately displays on screen (using the ng-repeat on the array of the scope linked to the array from Firebase), and takes the older of the 10 elements out.
$firebase(ref.limitToLast(10)).$asArray().$loaded().then(function(messagesData) { ... }
I can detect the change using
messagesData.$watch(function(data) {
console.log("data changed!", data, messagesData);
if(data.event == "child_added") {
// work here
}
});
But I can't figure out how to do what I'm trying to, nor did I find it in the doc. Thanks for any help !
Okay, so, there is a solution. It's possible not to have everything in sync while still enjoying the use of AngularFire for the current elements. Here is how.
Start by getting the messages you want to display at the beginning :
$firebase(ref.orderByKey().limitToLast(nbMessages)).$asArray().$loaded().then(function(messagesData) { ......
Then link them to another array one by one using foreach :
angular.forEach(messagesData, function(value, key) {
messagesDataReturn[key] = value;
// get any other data you need for that message (user name, etc...
});
This will keep every element iterated in sync. So if there is something like a counter of likes, they will be updated live.
Finally add a watcher :
messagesData.$watch(function(data) {
if(data.event == "child_added") {
var newLine = messagesData.filter(function ( obj ) {
return obj.$id === data.key;
})[0];
// add any needed data for this new line
$rootScope.$broadcast('list:updated', newLine);
}
});
In your controller you just have to listen to the broadcast :
$scope.newData = [];
$scope.$on('list:updated', function (event, data) {
$scope.newData.push(data);
});
You can then use $scope.newData to display the way to show the new messages and onclick merge this array with your main one on the scope, so the new messages appears.
Hope this helps someone !

How to use Selenium (or Seleno) to detect if a DOM element is displayed by Angular

When my button is clicked, the ng-hide directive will turn a hidden div to be visible on page. I am using Seleno to write UI test for an Angular application.
I have checked the display css value on that element:
var cssValue = SelectById(elementId).GetCssValue("display");
This cssValue always returns a none.
Also checked is the class attribute.
var cls = SelectById(elementId).GetAttribute("class");
I am expecting ng-hide should be removed from the classes of this element.
return !SelectById(elementId).GetAttribute("class").Contains("ng-hide");
But every time the class still contains ng-hide!
In case someone may ask, here is my SelectById. Just to return a Web Element on the Selenium Page Object.
protected IWebElement SelectById(string id)
{
return Find.Element(By.Id(id));
}
As mentioned in the answer section, I probably did not wait out the class update by Angular in a correct way. What I did is just let the Thread Sleep a while.
public static void Pause(int durationInMilisecond = 2000)
{
if (SelenoSettings.EnablePausing)
Thread.Sleep(durationInMilisecond);
}
Anyone can give me some advice? Thanks.
Here is our solution, thanks to the input from ABucin and Arran. Thank you for pointing to the right direction for us. WebDriverWait is the thing we should look into in this case.
public bool Displayed(string elementId)
{
try
{
var wait=new WebDriverWait(BrowserFactory.Chrome(),new TimeSpan(0,2,0));
wait.Until(d => !SelectById(elementId).GetAttribute("class").Contains("ng-hide"));
// then there is all types of checking start to work:
var bySelenoDisplayed =SelectById(elementId).Displayed;
return bySelenoDisplayed;
var byCss = SelectById(elementId).GetCssValue("display");
return !byCss.Equals("hidden");
var byClass = SelectById(elementId).GetAttribute("class");
return !byClass.Contains("ng-hide");
}
catch (Exception)
{
// 2min timeout reached.
return false;
}
}
According to the Angular ngHide documentation (https://docs.angularjs.org/api/ng/directive/ngHide), "The element is shown or hidden by removing or adding the ng-hide CSS class onto the element.". So your best way of approaching this, is to:
click on button
wait for the class to be toggled off
check that class is not present
I believe your problem is that the class removal does not happen immediately, but after a certain period of time. I have had several issues regarding this with Selenium on Java, and I assume this is the problem in your case, as well.

Using ng-repeat with markers in ngMap

I want to use ngMap to add Google Maps to my app.
The demos are "static" in the sense that they have only hard coded HTML. I want my code to be "dynamic" in the sense that it will periodically ask a server to look in its database and return me a bunch of coordinates to plot, which will change with time. I hope that that is clear; if not, please ask for more detail.
I modified the ngmap markers demo to generate some random lat/long coordinates every two seconds (rather than going to my server, as my final app will). Please see the Plunk.
There are no errors in the console, and it seems that ngMap is trying to add my markers, as I see a lot of this sort of thing in the console ...
adding marker with options,
Object {ngRepeat: "myMarker in markers", position: O}
clickable: true
ngRepeat: "myMarker in markers"
position: O
A: 103.96749299999999
k: 1.387454
__proto__: O
visible: true
__proto__: Object
where K and A are the Lat/Long as I expect them to be.
BUT ... I don't see any markers on the map. What am I doing wrong?
[Update] An excellent answer, for which I gladly awarded a bounty afterwards. For anyone else reading this and wishing to use ngMap as #allenhwkim said in another stackoverflow question and, I think, on his blog, ngMap just creates the map for you & after that you manipulate it with the standard Google Maps API.
For instance, just before looping to add the markers, I declared
var bounds = new google.maps.LatLngBounds(); and in the loop, after adding the marker to the map, I bounds.extend(latlng); and, finally, after the loop, I
var centre = bounds.getCenter();
$scope.map.setCenter(centre);
I forked the answer and created a new Plunk to show this. Not the world's most useful functionality, but the point is just to show how to use $scope.map with the Google Maps API. Thanks again, Allen, for ngMap.
Answer is here
http://plnkr.co/edit/Widr0o?p=preview
Please remember that ngMap is not replacing Google Maps V3 API.
Let me know if you have further questions.
The following is code block of the controller.
// $scope.map .. this exists after the map is initialized
var markers = [];
for (var i=0; i<8 ; i++) {
markers[i] = new google.maps.Marker({
title: "Hi marker " + i
})
}
$scope.GenerateMapMarkers = function() {
$scope.date = Date(); // Just to show that we are updating
var numMarkers = Math.floor(Math.random() * 4) + 4; // betwween 4 & 8 of them
for (i = 0; i < numMarkers; i++) {
var lat = 1.280095 + (Math.random()/100);
var lng = 103.850949 + (Math.random()/100);
// You need to set markers according to google api instruction
// you don't need to learn ngMap, but you need to learn google map api v3
// https://developers.google.com/maps/documentation/javascript/marker
var latlng = new google.maps.LatLng(lat, lng);
markers[i].setPosition(latlng);
markers[i].setMap($scope.map)
}
$timeout(function() {
$scope.GenerateMapMarkers();
}, 2000); // update every 2 seconds
};
$scope.GenerateMapMarkers();
Why not do something like
<map zoom="2" center="[40.74, -74.18]">
<marker position="{{destination.position}}" ng-repeat="destination in destinations"></marker>
</map>
If you asking for ng-repeat that would work. And you would populate the destinations with a simple http call to your backend:
$http.get(url + '/destinations', config).success(function (data) {
if (data != null && data.total > 0) {
$scope.destinations = data.destinations;
} else {
$scope.destinations = []
}
});

Resources