Mastering the ng-submit directive for AngularJS

Having an issue with my form that submits a location to Google's Geocoder and updates the map with the lat/long. When using ng-click on the icon, it requires double clicking to work properly. And when using ng-submit on the form, it appends to the URL instead of performing the task as expected. I'm determined to figure this out, but feeling a bit lost about what exactly is going wrong.

Here is the structure of the form:

<li>
  <form action="" class="search-form" ng-submit="convertLatLonToAddress()">
      <div class="form-group has-feedback">
      <label for="search" class="sr-only">Search</label>
      <input type="text" class="form-control" name="search" id="search" placeholder="Search for an address or place name">
          <i class="fa fa-search form-control-indicator"></i>                        
    </div>
  </form>
</li> 

Below you'll find the function being used:

$scope.convertLatLonToAddress = function(){
  var address = $('#search').val();
  var geocoder = new google.maps.Geocoder();

  geocoder.geocode( { 'address': address}, function(results, status) {
    if (status == google.maps.GeocoderStatus.OK) {
      var latitude = results[0].geometry.location.lat();
      var longitude = results[0].geometry.location.lng();
      // console.log(latitude + ' and ' + longitude);
      $scope.center.lat = latitude;
      $scope.center.lon = longitude;
    } 
  }); 
};

Credit goes to @PSL for the fix! Here is the updated code:

<li>
  <form class="search-form" ng-submit="convertLatLonToAddress(searchText)">
    <div class="form-group has-feedback">
      <label for="search" class="sr-only">Search</label>
      <input type="text" class="form-control" name="search" id="search" placeholder="Search for an address or place name" ng-model="searchText">
      <button style="visibility: hidden"></button>
        <a ng-click="convertLatLonToAddress(searchText)">
          <i class="fa fa-search form-control-indicator"></i>                        
        </a>
    </div>
  </form>
</li> 

And here is the modified function:

$scope.convertLatLonToAddress = function(searchText){
  // var address = $('#search').val();
  var address = searchText;
  var geocoder = new google.maps.Geocoder();

  geocoder.geocode( { 'address': address}, function(results, status) {
    if (status == google.maps.GeocoderStatus.OK) {
      var latitude = results[0].geometry.location.lat();
      var longitude = results[0].geometry.location.lng();
      // console.log(latitude + ' and ' + longitude);
      $scope.center.lat = latitude;
      $scope.center.lon = longitude;
      $scope.$apply();
    } 
  }); 
};

Answer №1

It is important to manually trigger the digest cycle within the asynchronous call of geocode, as geocode does not execute within an Angular context.

geocoder.geocode( { 'address': address}, function(results, status) {
    if (status == google.maps.GeocoderStatus.OK) {
      var latitude = results[0].geometry.location.lat();
      var longitude = results[0].geometry.location.lng();
      // console.log(latitude + ' and ' + longitude);
      $scope.center.lat = latitude;
      $scope.center.lon = longitude;
      $scope.$apply();
    } 
  }); 

Whenever you click, ng-click initiates the digest cycle, causing the previous cycle to run the non-angular asynchronous call and update the scope which Angular is unaware of. Upon clicking again, the digest cycle runs once more with the previously set values being retrieved, hence requiring 2 clicks. To have ng-submit execute, a form element trigger such as a button or input type="submit" must be used to cause submit behavior on the form. Additionally, it is advisable to remove action from the form unless redirection is intended.

Furthermore, utilizing ng-model on the textbox to pass the value to your function rather than directly obtaining it from the DOM is recommended.

<input type="text" class="form-control" name="search" id="search" placeholder="Search for an address or place name" ng-model="searchText">

Pass the value via ng-click as

ng-click="convertLatLonToAddress(searchText)"
and utilize it inside your function.

To eliminate the need for scope.apply(); in your controller, consider abstracting out geoCoder into an Angular service that returns a promise by creating a deferred object.

myApp.service('geoCoderService', ['$q', function($q){
      this.getCoordinates = function(address){
          var defer = $q.defer();
           var geocoder = new google.maps.Geocoder();

          geocoder.geocode( { 'address': address}, function(results, status) {
           if (status == google.maps.GeocoderStatus.OK) {
             var latitude = results[0].geometry.location.lat();
             var longitude = results[0].geometry.location.lng();
             return defer.resolve({latitude: latitude, longitude: longitude});
           } 
          //failure
          defer.reject(status);
       }); 
          return defer.promise;
      }

});

Inject geoCoderService and retrieve data using:

 geoCoderService.getCoordinates(address).then(function(coordinates){
     //populate it
 }).catch(function(errorStatus){  /*oops Error*/ })

Answer №2

Give this a try

 var application = angular.module('myApp', []);
 application.controller('myCtrl', function($scope) {
   $scope.convertCoordinatesToLocation = function() {
     var locationName = $('#search').val();
     var geocoder = new google.maps.Geocoder();
     geocoder.geocode({
       'address': locationName
     }, function(results, status) {
       if (status == google.maps.GeocoderStatus.OK) {
         $scope.latitude = results[0].geometry.location.lat();
          $scope.longitude = results[0].geometry.location.lng();
         console.log($scope.latitude + ' and ' + $scope.longitude);
         setTimeout(function(){$scope.$apply();},0)
       }
     });
   };
 });
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<body>
  <div ng-app="myApp" ng-controller="myCtrl">
    <li>
      <div class="form-group has-feedback">
        <label for="search" class="sr-only">Search</label>
        <input type="text" class="form-control" name="search" id="search" placeholder="Enter a location to find its coordinates">
        <i class="fa fa-search form-control-indicator"></i> 
        <button ng-click="convertCoordinatesToLocation()">Find Location</button>
        <br>
       Latitude :  <input type="text" ng-model="latitude"><br>
       Longitude :  <input type="text" ng-model="longitude">
        
      </div>
    </li>
  </div>
</body>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Different Angular 2 components are resolved by routes

Consider this scenario: Upon navigating to the URL /product/123, the goal is to display the ProductComponent. This is how it's currently configured: RouterModule.forRoot([ { path: 'product/:productId', ...

Can you recommend a straightforward method in Vue.js for validating the format of user input?

Here is how I have implemented an email sending feature in Vue.js: <template> <button @click="sendMail(); $emit('close')">Send</button> </template> <script> methods: { sendMail () { axios ...

Error: React - Module not found. This issue arises when attempting to require the 'express' package from within the installed

Hello, I attempted to install the express package in a React project. However, when I try to import the package inside app.js using: const app = require("express"); I encounter 30 errors all stating: Module not found Error: Can't resolve 'x&ap ...

How should a string be properly converted to JSON format?

I am encountering an issue with converting the following string to JSON format const banner = " { "banners": [ { "startDate": "02/26/2021", "endDate": "12/25/2021","content": "Important ...

Issue with $_SERVER['PHP_SELF'] not functioning properly

Apologies if this question has been asked before, but after searching extensively I couldn't find a solution. Therefore, I am posting here... The issue is that I'm trying to submit values on the same page (using jQuery Mobile UI) and I have used ...

Mastering the art of handling errors with Javascript Promises

Through thorough research and some assistance, I have managed to create and annotate code that I believe will enhance the comprehension of Javascript Promises for individuals. However, I encountered a puzzling issue when attempting to trigger an error by ...

The functionality of getElementsByClassName and appendChild is not producing the desired outcome

First of all, the solutions must strictly adhere to VanillaJS. I am presenting a straightforward HTML code snippet below: <div class="x">X</div> <div class="x">Y</div> <div class="x">Z</div> Accompanied by a block of ...

Running JavaScript code when the route changes in Angular 6

Currently, I am in the process of upgrading a website that was originally developed using vanilla JS and JQuery to a new UI built with Angular and typescript. Our site also utilizes piwik for monitoring client activity, and the piwik module was created i ...

What could be causing the "Invalid Config file error" when Karma tests the ng-boilerplate app?

I recently obtained the ng-boilerplate application from Github and I am currently examining it to enhance my understanding of AngularJS and testing. One of the initial steps I took was to delve into how Karma testing functions. I accessed a command prompt ...

Change the x and y positions of several div elements as the mouse moves in JavaScript

I am aiming to create a webpage where multiple divs with text and other content move along the x and y axes of the mouse. The desired effect is similar to parallax scrolling, but I have found that existing parallax plugins are image-based and do not work w ...

Struggling to map JSON data (received from WCFRest) onto an HTML table

After creating a WCFRestful service that populates data in JSON format as shown below: {"GetEmployeesJSONResult":"[{\"Name\":\"Sumanth\",\"Id\":101,\"Salary\":5000},{\"Name\":\"Sumanth\",\"I ...

Invisible and Unrestricted automatic playback

Why is auto play muted in both Firefox and Chrome? How can we code it so that browsers don't block it? Here's the code I'm using: <audio id="audio1" src="https://notificationsounds.com/storage/sounds/file-sounds-1217-relax ...

The cookie appears in the callback URL, but does not get stored in the browser's cookie storage

I'm attempting to store the facebookPicUrl image in a cookie. Even though I can see it in the callback request, it's not showing up in the browser's cookie storage. Just to clarify, the session cookie is working fine. auth.route('/auth ...

What is the best method for storing a JavaScript widget with analytics - should it be done dynamically or statically?

My widget comes with a customizable boot loader that is used on websites. The boot loader file retrieves the settings for the widget and generates it accordingly. Normally, the content of the bootloader file remains static unless there are modifications ma ...

Understanding @@iterator in JavaScript: An in-depth look

Can someone shed some light on the mysterious @@iterator? It keeps popping up in tutorials but no one seems to provide a clear explanation of what it actually is. Is it a symbol literal or something else entirely? ...

Connect to content on the current page

I am facing an issue where the linked heading of a section on the same page is getting lost under the fixed navigation bar when scrolling down. This problem seems to only occur on desktop view as it works fine on mobile preview. Here is the code I am curre ...

Implement jQuery to toggle a class on click for added functionality

I am attempting to create a box that changes color when clicked. When the box is first clicked, it will turn red by adding the class red, and if clicked again, it will change to blue. The colors alternate with each click, but I am unsure of how to achieve ...

Retrieve the URL redirected by JavaScript without altering the current page using Selenium

Is there a way to extract the URL I am supposed to be redirected to upon clicking a button on a website, without actually being redirected? The button triggers a complex Javascript function and is not a simple hyperlink. click() method doesn't meet my ...

What is the best method for utilizing a single L.Shapefile/zip file Object and modifying the onEachFeature function for each layer?

I am currently facing an issue where I have multiple tileLayers each containing a shape file. These tile layers represent different datasets based on variables and adjust colors accordingly. I have been able to achieve this by creating three separate Obje ...

Sorting and filtering data using AngularJS filter and orderBy inside a controller or factory

I am currently working with a factory that looks like this: app.factory("ModuleFactory", function (api, $http, $q, filterFilter) { var moduleList = []; var categoryList = []; var moduleTypeList = []; var academyModuleTypeList = []; var mostUsed = []; var ...