Unable to set table cell value from Angular controller - html

I have written below html and controller code for setting table cell value to geo code address but it is not working.
<tr ng-repeat="x in tabledata">
<td>{{ x.id }}</td>
<td>{{x.lat}}</td>
<td>{{x.lng}}</td>
<td ng-if="ctrl.getAddress(x.lat,x.lng)">{{addr}}</td>
</tr>
Controller method is as follows.
this.getAddress = function(lat,lng) {
console.log('x.lat ' + lat + ' x.lng ' + lng);
var p = new Promise(function(resolve,reject){
var address;
var geocoder = new google.maps.Geocoder;
geocoder.geocode({'location': {lat: parseFloat(lat), lng: parseFloat(lng)}}, function(results, status) {
if (status === 'OK') {
if (results[0]) {
resolve(results[0].formatted_address);
} else {
resolve('No results found');
}
} else {
reject('Geocoder failed due to: ' + status);
}
});
}).then(function(v){
console.log('Resolved - Geo coder address ' + v);
$scope.addr = v;
$scope.apply;
}).catch(function(v){
console.log('Catch - Geo coder address ' + v);
});
return true;
};
I am getting the address value but it is not getting assigned to addr variable.

I could solve the issue as follows.
1) Added one more property called addr to the tabledata in the controller function.
2) After getting the address value from geocode API I am setting its value in the tabledata.

Related

Google geocode with address returns one result

Geocoder with an existing address as a parameter returns only the street_address as a result. Why? The address is valid. It has been tested at https://google-developers.appspot.com/maps/documentation/utils/geocoder/
My call:
checkAddress(street, strNo, post_code, area, function (results) {
if (results) {
$.each(results, function (i, address) {
if (address.types[0] == "street_address") {
itemStreetNo = address.address_components[0].long_name;
itemStreet = address.address_components[1].long_name;
}
if (address.types[0] == "route") {
itemStreet = address.address_components[0].long_name;
}
if (address.types[0] == "postal_code") {
itemPostalCode = address.address_components[0].long_name;
}
if (address.types[0] == "country") {
itemCountry = address.address_components[0].long_name;
}
if (address.types[0] == "locality") {
itemRegion = address.address_components[0].long_name;
}
}
});
My function is:
function checkAddress(address, number, zipcode, area, callback) {
var geocoder = new google.maps.Geocoder;
var addr = address + ' ' + number + ' ' + zipcode + ' ' + area;
geocoder.geocode({ 'address': addr }, function (results, status) {
if (status === google.maps.GeocoderStatus.OK) {
if (results[0]) {
callback(results);
}
else {
callback(null);
}
}
else {
callback(null);
}
});
}
You only search in 1 component (address.address_components[0]), so you only get 1 result.
address.address_components[0] contains a component, for example "route". Then address.address_components[1] contains another component, for example "locality", then address.address_components[2] ... As far as I remember the order of the components is random.
So what you need, is to iterate. For every component you check the type, that lets you know where the value is supposed to go.
I wrote a function for a different Stack Overflow question that does this iteration, that goes "fishing" for whatever component.
See if it works for you; let me know.
Google API Address Components

get city name, address using latitude and longitude in appcelerator

I have a doubt in the mentioned Title. I can get current latitude and longitude. But when i am using reverse geoCoding to convert to corresponding city name or address, nothing shows. Does anybody have any idea about this ?. Here is my code
Titanium.Geolocation.getCurrentPosition(function(e) {
if (!e.success || e.error) {
alert('Could not find the device location');
return;
} else {
longitude = e.coords.longitude;
latitude = e.coords.latitude;
Titanium.Geolocation.reverseGeocoder(latitude, longitude, function(e) {
if (e.success) {
var places = e.places;
if (places && places.length) {
driverCity = places[0].city;
// Current city
driverState = places[0].address;
// Current State
annotation.title = e.places[0].displayAddress;
// Whole address
// Ti.API.info("\nReverse Geocode address == " + JSON.stringify(places));
} else {
// address = "No address found";
}
}
});
}
});
I would suggest firstly using different variables for your return parameters in the functions and use negative conditions to reduce indentation:
Titanium.Geolocation.getCurrentPosition(function(position) {
if (!position.success || position.error) {
alert('Could not find the device location');
return;
}
longitude = position.coords.longitude; // -88.0747875
latitude = position.coords.latitude; // 41.801141
Titanium.Geolocation.reverseGeocoder(latitude, longitude, function(result) {
if (!result.success || !result.places || result.places.length == 0) {
alert('Could not find any places.');
return;
}
var places = result.places;
Ti.API.info("\nReverse Geocode address == " + JSON.stringify(places));
driverCity = places[0].city;
// Current city
driverState = places[0].address;
// Current State
annotation.title = places[0].displayAddress;
// Whole address
// Ti.API.info("\nReverse Geocode address == " + JSON.stringify(places));
});
});
Then see what is being returned from your calls.

Which API method provides the drawing of a city's boundaries?

Using Google Maps' API v3, how can I geocode a city name, and have a map returned with the outline of the result? usually this is a political entity with strict boundaries, like a city, municipality, etc.
Which API method should be used?
There isn't any API (at present) that provides that data.
Enhancement request
I think you will need Tiger/Line files as referenced in this answer. You can use the data to generate polygons. Use Geocoder.geocode to search for city names. Here's a promise based function that I use:
function locateAddress(address) {
var deferred = $q.defer();
if(!address) { $q.resolve(null); return; }
var geocoder = new google.maps.Geocoder();
var a = address;
if(_.isObject(a)) {
a = (a.line1 || '') + ' ' + (a.city || '') + ' ' + (a.state || '') + ' ' + (a.zip || '');
}
geocoder.geocode({ 'address' : a}, function(results, status) {
if(status === 'ZERO_RESULTS') {
deferred.resolve(null);
return;
}
var c = results[0].geometry.location;
deferred.resolve({latitude: c.lat(), longitude: c.lng()});
}, function(err) {
deferred.reject(err);
});
return deferred.promise;
}

Catching exceptions thrown by Swagger

I'm new at fumbling with Swagger, so I might be asking a silly question. Is it in any way possible to prevent the site from crashing whenever it is "unable to read from api"?
My site is working most of the time, but if there for some reason is an api that is unreadable (or just unreachable) swagger just stop working. It still displays the api's it managed to reach, but all functionality is completely gone its not even able to expand a row.
To summarize:
How do I prevent swagger from crashing, when one or more API's is unreadable and returns something like this:
Unable to read api 'XXXX' from path
http://example.com/swagger/api-docs/XXXX (server
returned undefined)
Below is my initialization of Swagger:
function loadSwagger() {
window.swaggerUi = new SwaggerUi({
url: "/frameworks/swagger/v1/api.json",
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
onComplete: function (swaggerApi, swaggerUi) {
log("Loaded SwaggerUI");
if (typeof initOAuth == "function") {
initOAuth({
clientId: "your-client-id",
realm: "your-realms",
appName: "your-app-name"
});
}
$('pre code').each(function (i, e) {
hljs.highlightBlock(e);
});
},
onFailure: function (data) {
log("Unable to Load SwaggerUI");
},
docExpansion: "none",
sorter: "alpha"
});
$('#input_apiKey').change(function () {
var key = $('#input_apiKey')[0].value;
log("key: " + key);
if (key && key.trim() != "") {
log("added key " + key);
window.authorizations.add("api_key", new ApiKeyAuthorization('api_key', key, 'header'));
}
});
$('#apiVersionSelectID').change(function () {
var sel = $('#apiVersionSelectID').val();
window.swaggerUi.url = sel;
$('#input_baseUrl').val(sel);
$('#explore').click();
});
window.swaggerUi.load();
};
I was searching for a solution to this problem too but could not find one. Here is a quick hack i did to solve the problem. Hope it can be of help to someone who is having the same trouble.
In swagger-client.js Find the function error: function (response) {
I replaced the return api_fail with addApiDeclaration to make it draw the api with some limited information even when it fails. I send in a dummy api json object with the path set to "/unable to load ' + _this.url. I send in an extra parameter that can be true or false, where true indicates that this is a failed api.
Old code:
enter cerror: function (response) {
_this.api.resourceCount += 1;
return _this.api.fail('Unable to read api \'' +
_this.name + '\' from path ' + _this.url + ' (server returned ' +response.statusText + ')');
}
New code
error: function (response) {
_this.api.resourceCount += 1;
return _this.addApiDeclaration(JSON.parse('{"apis":[{"path":"/unable to load ' + _this.url + '","operations":[{"nickname":"A","method":" "}]}],"models":{}}'), true);
}
I modified the addApiDeclaration function in the same file to display a different message for a failed api by first adding a secondary parameter to it called failed and then an if statement that check if failed is true and then change the name of the api to "FAILED TO LOAD RESOURCE " + this.name. This adds the FAILED TO LOAD RESOURCE text before the failed api.
Old code
SwaggerResource.prototype.addApiDeclaration = function (response) {
if (typeof response.produces === 'string')
this.produces = response.produces;
if (typeof response.consumes === 'string')
this.consumes = response.consumes;
if ((typeof response.basePath === 'string') && response.basePath.replace(/\s/g, '').length > 0)
this.basePath = response.basePath.indexOf('http') === -1 ? this.getAbsoluteBasePath(response.basePath) : response.basePath;
this.resourcePath = response.resourcePath;
this.addModels(response.models);
if (response.apis) {
for (var i = 0 ; i < response.apis.length; i++) {
var endpoint = response.apis[i];
this.addOperations(endpoint.path, endpoint.operations, response.consumes, response.produces);
}
}
this.api[this.name] = this;
this.ready = true;
if(this.api.resourceCount === this.api.expectedResourceCount)
this.api.finish();
return this;
};
New code
SwaggerResource.prototype.addApiDeclaration = function (response, failed) {
if (typeof response.produces === 'string')
this.produces = response.produces;
if (typeof response.consumes === 'string')
this.consumes = response.consumes;
if ((typeof response.basePath === 'string') && response.basePath.replace(/\s/g, '').length > 0)
this.basePath = response.basePath.indexOf('http') === -1 ? this.getAbsoluteBasePath(response.basePath) : response.basePath;
this.resourcePath = response.resourcePath;
this.addModels(response.models);
if (response.apis) {
for (var i = 0 ; i < response.apis.length; i++) {
var endpoint = response.apis[i];
this.addOperations(endpoint.path, endpoint.operations, response.consumes, response.produces);
}
}
if (failed == true) {
this.name = "FAILED TO LOAD RESOURCE - " + this.name;
}
this.api[this.name] = this;
this.ready = true;
if(this.api.resourceCount === this.api.expectedResourceCount)
this.api.finish();
return this;
};

How do I integrate Salesforce with Google Maps?

How do I integrate Salesforce with Google Maps? I'm just looking for information on how to...
Search for contacts in Salesforce
Plot those on a google map.
EDIT:
Thanks to tggagne's comment I've realized that people still see this answer. The code that was here is over 2.5 years old. If you want to see it - check the history of edits.
A lot has changed in the meantime, more mashup examples were created. Not the least of them being "SF Bus Radar" (github, youtube) app by Cory Cowgill (created on Dreamforce'11 I think).
Nonetheless - here's my updated example with server-side geocoding, new field of type Geolocation and usage of JSON parsers.
It tries to cache the geocoding results in the contact records. Bear in mind it might not be 'production-ready' (no Google Business API key = as all our requests come out from same pool of Salesforce IP servers there might be error messages). That's why I've left the client-side geocoding too.
You'll need to make 2 changes in your environment before checking it out:
Add "Remote Site Setting" that points to https://maps.googleapis.com to enable callouts from Apex
Add field "Location" in Setup -> Customize -> Contacts -> fields. Type should be "Geolocation". I've selected display as decimals and precision of 6 decimal places.
public with sharing class mapController {
public String searchText {get;set;}
public List<Contact> contacts{get; private set;}
public static final String GEOCODING_URI_BASE = 'https://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=';
// For purposes of this demo I'll geocode only couple of addresses server-side. Real code can use the commented out value.
public static final Integer MAX_CALLOUTS_FROM_APEX = 3; // Limits.getLimitCallouts()
public mapController(){
searchText = ApexPages.currentPage().getParameters().get('q');
}
public void find() {
if(searchText != null && searchText.length() > 1){
List<List<SObject>> results = [FIND :('*' + searchText + '*') IN ALL FIELDS RETURNING
Contact (Id, Name, Email, Account.Name,
MailingStreet, MailingCity, MailingPostalCode, MailingState, MailingCountry,
Location__Latitude__s, Location__Longitude__s)
];
contacts = (List<Contact>)results[0];
if(contacts.isEmpty()){
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.INFO, 'No matches for "' + searchText + '"'));
} else {
serverSideGeocode();
}
} else {
if(contacts != null) {
contacts.clear();
}
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.INFO, 'Please provide at least 2 characters for the search.'));
}
}
public void clearGeocodedData(){
for(Contact c : contacts){
c.Location__Latitude__s = c.Location__Longitude__s = null;
}
Database.update(contacts, false);
contacts.clear();
}
public String getContactsJson(){
return JSON.serialize(contacts);
}
public String getDebugContactsJson(){
return JSON.serializePretty(contacts);
}
private void serverSideGeocode(){
List<Contact> contactsToUpdate = new List<Contact>();
Http h = new Http();
HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setTimeout(10000);
for(Contact c : contacts){
if((c.Location__Latitude__s == null || c.Location__Longitude__s == null)){
String address = c.MailingStreet != null ? c.MailingStreet + ' ' : '' +
c.MailingCity != null ? c.MailingCity + ' ' : '' +
c.MailingState != null ? c.MailingState + ' ' : '' +
c.MailingPostalCode != null ? c.MailingPostalCode + ' ' : '' +
c.MailingCountry != null ? c.MailingCountry : '';
if(address != ''){
req.setEndpoint(GEOCODING_URI_BASE + EncodingUtil.urlEncode(address, 'UTF-8'));
try{
HttpResponse res = h.send(req);
GResponse gr = (GResponse) JSON.deserialize(res.getBody(), mapController.GResponse.class);
if(gr.status == 'OK'){
LatLng ll = gr.results[0].geometry.location;
c.Location__Latitude__s = ll.lat;
c.Location__Longitude__s = ll.lng;
contactsToUpdate.add(c);
} else {
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Geocoding of "' + address + '" failed:' + gr.status));
}
}catch(Exception e){
ApexPages.addMessages(e);
}
}
// Bail out if we've reached limit of callouts (not all contacts might have been processed).
if(Limits.getCallouts() == MAX_CALLOUTS_FROM_APEX) {
break;
}
}
}
if(!contactsToUpdate.isEmpty()) {
Database.update(contactsToUpdate, false); // some data in Developer editions is invalid (on purpose I think).
// If update fails because "j.davis#expressl&t.net" is not a valid Email, I want the rest to succeed
}
}
// Helper class - template into which results of lookup will be parsed. Some fields are skipped!
// Visit https://developers.google.com/maps/documentation/geocoding/#Results if you need to create full mapping.
public class GResponse{
public String status;
public GComponents[] results;
}
public class GComponents{
public String formatted_address;
public GGeometry geometry;
}
public class GGeometry {
public LatLng location;
}
public class LatLng{
public Double lat, lng;
}
}
<apex:page controller="mapController" tabStyle="Contact" action="{!find}" id="page">
<head>
<style>
div #map_canvas { height: 400px; }
</style>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
</head>
<apex:sectionHeader title="Hello StackOverflow!" subtitle="Contact full text search + Google Maps integration" />
<apex:pageMessages />
<apex:form id="form">
<apex:pageBlock id="searchBlock">
<apex:inputText value="{!searchText}" />
<apex:commandButton value="Search" action="{!find}"/>
<p>Examples: "USA", "Singapore", "Uni", "(336) 222-7000". If it works in the global search box, it will work here.</p>
</apex:pageBlock>
<apex:pageBlock title="Found {!contacts.size} Contact(s)..." rendered="{!NOT(ISNULL(contacts)) && contacts.size > 0}" id="resultsBlock">
<apex:pageBlockButtons location="top">
<apex:commandButton value="Clear cached locations" title="Click if you want to set 'null' as geolocation info for all these contacts" action="{!clearGeocodedData}" />
</apex:pageBlockButtons>
<apex:pageBlockTable value="{!contacts}" var="c" id="contacts">
<apex:column headerValue="{!$ObjectType.Contact.fields.Name.label}">
<apex:outputLink value="../{!c.Id}">{!c.Name}</apex:outputLink>
</apex:column>
<apex:column headerValue="Address">
{!c.MailingStreet} {!c.MailingCity} {!c.MailingCountry}
</apex:column>
<apex:column value="{!c.Account.Name}"/>
<apex:column headerValue="Location (retrieved from DB or geocoded server-side)">
{!c.Location__Latitude__s}, {!c.Location__Longitude__s}
</apex:column>
</apex:pageBlockTable>
<apex:pageBlockSection columns="1" id="mapSection">
<div id="map_canvas" />
</apex:pageBlockSection>
<apex:pageBlockSection title="Click to show/hide what was geocoded server-side and passed to JS for further manipulation" columns="1" id="debugSection">
<pre>{!debugContactsJson}</pre>
</apex:pageBlockSection>
<pre id="log"></pre>
</apex:pageBlock>
</apex:form>
<script type="text/javascript">
twistSection(document.getElementById('page:form:resultsBlock:debugSection').childNodes[0].childNodes[0]); // initially hide the debug section
var contacts = {!contactsJson}; // Array of contact data, some of them might have lat/long info, some we'll have to geocode client side
var coords = []; // Just the latitude/longitude for each contact
var requestCounter = 0;
var markers = []; // Red things we pin to the map.
var balloon = new google.maps.InfoWindow(); // Comic-like baloon that floats over markers.
function geocodeClientSide() {
for(var i = 0; i < contacts.length; i++) {
if(contacts[i].Location__Latitude__s != null && contacts[i].Location__Longitude__s != null) {
coords.push(new google.maps.LatLng(contacts[i].Location__Latitude__s, contacts[i].Location__Longitude__s));
} else {
++requestCounter;
var address = contacts[i].MailingStreet + ' ' + contacts[i].MailingCity + ' ' + contacts[i].MailingCountry;
var geocoder = new google.maps.Geocoder();
if (geocoder) {
geocoder.geocode({'address':address}, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
coords.push(results[0].geometry.location);
} else {
var pTag = document.createElement("p");
pTag.innerHTML = status;
document.getElementById('log').appendChild(pTag);
}
if(--requestCounter == 0) {
drawMap();
}
});
}
}
}
// It could be the case that all was geocoded on server side (or simply retrieved from database).
// So if we're lucky - just proceed to drawing the map.
if(requestCounter == 0) {
drawMap();
}
}
function drawMap(){
var mapOptions = {
center: coords[0],
zoom: 3,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
for(var i = 0; i < coords.length; ++i){
var marker = new google.maps.Marker({map: map, position: coords[i], title:contacts[i].Name, zIndex:i});
google.maps.event.addListener(marker, 'click', function() {
var index = this.zIndex;
balloon.content = '<b>'+contacts[index].Name + '</b><br/>' + contacts[index].Account.Name + '<br/>' + contacts[index].Email;
balloon.open(map,this);
});
markers.push(marker);
}
}
geocodeClientSide();
</script>
</apex:page>
Another place to look is the force.com platform fundamentals book (or site if you don't have a developer account). They have a very good and detailed tutorial here showing how to integrate maps with Salesforce (they use Yahoo for the tutorial but it will work just as well with Google Maps).
Since Spring '15, we can also use apex:map with no extra Google API.
Also works when viewed in Lightning -- No personal experience specifically but that's what I read.
Example from Docs:
<apex:map width="600px" height="400px" mapType="roadmap" center="{!Account.BillingStreet}, {!Account.BillingCity}, {!Account.BillingState}">
<!-- Add a CUSTOM map marker for the account itself -->
<apex:mapMarker title="{! Account.Name }" position="{!Account.BillingStreet}, {!Account.BillingCity}, {!Account.BillingState}" icon="{! URLFOR($Resource.MapMarkers, 'moderntower.png') }"/>
<!-- Add STANDARD markers for the account's contacts -->
<apex:repeat value="{! Account.Contacts }" var="ct">
<apex:mapMarker title="{! ct.Name }" position="{! ct.MailingStreet }, {! ct.MailingCity }, {! ct.MailingState }"></apex:mapMarker>
</apex:repeat>
</apex:map>
In the example, {! Account.Contacts } is a list of Contacts which
is being iterated over. Each iteration, it's creating apex:mapMarker's to map all Contacts in a list. Though the OP is old, the "search results" could basically replace the {Account.Contacts} list being iterated over in example.
Documentation:
Docs that example was pulled from.
(I know this is old but was brought to top from an update so thought update not using API would be okay.)