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

Object.assign changes the original array object in place

I am facing a challenge while attempting to modify the value of a specific index in my state, specifically the property post_comments. The issue lies in the fact that even though I am only modifying a copy of the state, the actual state is also being alter ...

Completes a form on a separate website which then populates information onto a different website

Creating a website that allows users to login and view various complaint forms from government websites or other sources. When a user clicks on a link to file a complaint, they will be redirected to the respective page. However, I am looking for a way to ...

A versatile jQuery function for extracting values from a :input selector

Is there a universal method in jQuery to retrieve the value of any :input element? I pose this question because I have a webpage containing select and checkbox inputs, as seen in the code below: for (var i = 0; i < arguments.length; i++) { ...

Pusher authentication issue: socket ID not defined

I am currently facing an issue while trying to establish a private channel for users to transmit data to my node.js server. Upon making the request, I encounter an error where pusher:subscription_error is returned with the error code 500. Upon checking my ...

"The implementation of real-time data retrieval through Socket.io is currently not functioning as expected

As a beginner in mean-stack, I tried to implement real-time data following some instructions but encountered errors. Can someone guide me on how to correctly use socket.io? I have provided my code below for fetching users from mongodb, kindly review and co ...

Customizing SwiperJS to display portion of slides on both ends

I need some assistance with my SwiperJS implementation to replace existing sliders on my website. The goal is to have variable-width slides, showing a landscape slide in the center with a glimpse of the preceding and following slides on each side. If it&ap ...

Modifying arrays in ReactJS

Having trouble editing my array list, need some help. I can update a single input value successfully, but struggling with updating the entire array. Any suggestions on why the method isn't working and how to edit the array? When I try to store data ...

sending data from a callback to an express router

As I embark on learning node.js, I've encountered a challenging issue. In my passportAuth.js file, I create a user and have a callback to ensure the user is created successfully. The code snippet looks something like this: req.tmpPassport = {}; var ...

The expiration time and date for Express Session are being inaccurately configured

I'm experiencing an issue with my express session configuration. I have set the maxAge to be 1 hour from the current time. app.use( session({ secret: 'ASecretValue', saveUninitialized: false, resave: false, cookie: { secure ...

When you hover over the button, it seamlessly transitions to a

Previously, my button component was styled like this and it functioned properly: <Button component={Link} to={link} style={{ background: '#6c74cc', borderRadius: 3, border: 0, color: 'white', height: 48, padding: '0 ...

I'm encountering an issue with the error message [ng:areq] stating that the argument 'employeeObj' is not a function and is undefined. I am currently unable to identify the missing component causing this error

I encountered an Error: [ng:areq] Argument 'employeeObj' is not a function, got undefined, and I'm struggling to identify what I missed. Can anyone provide assistance? <html xmlns="http://www.w3.org/1999/xhtml"> <head> < ...

Learn how to display a web page in a tab view similar to the toggle device toolbar option

Is it possible to simulate the toggle device toolbar of a web page using JavaScript? https://i.stack.imgur.com/M9oB0.png For example, can we set up a webpage to always display in tab or mobile view like on YouTube? ...

Detecting changes in checkbox states

In my current scenario, I have a section of the screen that can be shown or hidden depending on whether a checkbox is checked. The user can change the state of the checkbox manually or programmatically. The challenge lies in detecting this change and upda ...

What strategies can be utilized to condense code when needing to adjust a className based on various props?

I am looking to condense this code, particularly the [if~else if] block, in order to dynamically change a className based on different props passed. export default function Button(props) { const { name, height, color, bgColor } = props; let className = ...

Achieving accurate JSON output from Elasticsearch's autosuggest feature can be

Running my node.js server involves sending queries to an elasticsearch instance. I have a JSON example of the query's output: { "took": 2, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 ...

Creating an AJAX request in Play 2.x by using a shared button

Although I have successfully utilized AJAX requests in the past using a form and adding return false to prevent page refresh, I recently encountered an issue when moving my JavaScript into a separate file. The @ commands now fail, making it difficult for m ...

Tips for managing and loading data into a dataGrid or table with the help of ReactJS and ReactHooks

Struggling to retrieve user input data from the form and display it in the table/Datagrid below, without success. Follow the process flow outlined below Once the user submits the form and clicks the send now button, the {handleSubmit} function is trigger ...

Facing compatibility issues with ng-view on Microsoft Edge

While testing my application on Microsoft Edge, I encountered an error: app.config( function ($routeProvider) { $routeProvider. when('/logout', { template: '<logout-page></logout-page>&a ...

How to modify the color of a div element with jQuery?

I need some help changing the color of a div with 'jQuery', but I am not sure how to do it. Below is the code I have been trying: function updateDivColor() { $('#OutlineX1').css("color","blue"); } ...

Is it possible to display a modal view when clicking on a textfield without the keyboard automatically popping up?

As a beginner in the realm of AngularJS and the Ionic framework, I am currently working on developing a hybrid app utilizing PhoneGap and Ionic. I have encountered a scenario where, upon clicking a UITextField, a modal should appear with a list of account ...