Create VueJS application with Google Map

In this article, we will learn the steps to make a VueJS application that uses some features of Google Map such as searching addresses, displaying a list of 1 store in the area on the map, Show store details on a map.

Frontend will use VueJS, and the API will use Ruby on Rails.

1. Install the Rails API

Start initializing the Rails project:

mkdir map_shop && cd map_shop 
rails new backend --api -d mysql

Because the Rails app and the VueJs app will be hosted on two separate servers, the first thing we need to handle is CORS (Cross-Origin Resource Sharing). In Rails, we find the following file config / initialzers / cors.rb and edit the file as follows:

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
 
    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

Next is to add the gem gem “rack-cors”and runbundle install

Create a Shop model containing basic information such as name, longitude, and latitude

rails generate scaffold Shops name:string address:string 'latitude:decimal{11,8}' 'longitude:decimal{11,8}'

#create database 
rake db:create
rake db:migrate

Thus, we have the Shop model, and our generated routes.rb file will resourse

Rails.application.routes.draw do
  resources :shops 
end

In the shop controller, we define the basic action index

def index
  @shops = Shop.all
 
  render json: @shops
end

Check api has worked, we create a shop data

Shop.create!(name: '', latitude: '21.0133334', longitude: '105.7778877', address: 'Ho Guom')

Run rails sto start the server and then click on the link localhost:3000/shops, it will display all the shops. So the API is OK.

2. Install VueJS

We will create a separate frontend folder to contain the VueJS code, using the tool vue-clito create it.


165/5000
cd map_shop


# if you have not installed vue-cli yet
npm install -g vue-cli

# initialize project frontend
vue init webpack frontend

# run frontend
cd frontend && npm run dev

Visit the link localhost:8080to enter Vue’s original interface

This is image title

Install the router if you didn’t add it when initializing

npm install vue-router
  1. Install the Google Map API
    To use Google Map, you need an API key generated by Google. You go to the tutorial page to get the key. With API key, we can use all Google services, as long as we pay for the service we need. There is currently a Google program offering $ 300 per user account for 1 year, so this is an opportunity for us to learn about Google services.

Here, we need to solve the problem of searching for an address in Google Map, so it is necessary to activate the service Maps JavaScript APIand Places APIin the list of services that Google provides, so that we can use Google Map to solve the above problems.

Add a library vue2-google-mapsto use google map in VueJS

npm install vue2-google-maps

In the src / main.js file on the frontend, we add config:

import * as VueGoogleMaps from "vue2-google-maps"

Vue.use(VueGoogleMaps, {
  load: {
    key: GOOGLE-API-KEY,
    libraries: "places"
  }
});

Thus, finished integrating google map into VueJS.

4. Create a Shop

Create the shop creation API in the backend:

# API create shop: POST /shops
def create 
  shop = Shop.new shop_params
 
 if shop.save 
    render json: {success: true}
 else
     render json: {errors: shop.errors.full_messages}, status: :unprocessable_entity
 end
end

private

def shop_params
  params.permit :name, :latitude, :longitude, :address
end

Form creates a shop inside Vue, creates file /src/CreateShop.vue

<template>
  <div class="--shop">
      <selected-address-google-map-modal
      :selectedAddress="selectedAddress"
      @onSelectedAddress="onSelectedAddress"
    />
    
      <form @submit.prevent="handleSubmit">
        <div class="form-group">
          <input type="text" v-model="name" name="name" placeholder="Name"
            class="form-control" :class="{ 'is-invalid': submitted && !name }" />
          <div v-show="submitted && !name" class="invalid-feedback">Name is required</div>
        </div>

        <div class="form-group form-group--address">
          <input type="text" v-bind:value="selectedPlace.address"
            placeholder="Please select a place"
            v-on:click="openSelectedAddressModal"
            class="form-control"
          >
        </div>

        <div class="form-group float-right">
          <button class="btn btn-primary">Create</button>
        </div>
      </form>
  </div>
</template>

<script>
  import axios from 'axios'
  import SelectedAddressGoogleMapModal from './SelectedAddressGoogleMapModal'

  export default {
    data () {
      return {
        submitted: false,
        name: '',
        selectedAddress: {
          lat: null,
          lng: null,
          address: ''
        }
      }
    },
    components: { SelectedAddressGoogleMapModal },
    methods: {
      handleSubmit () {
        this.submitted = true;
        const {
          name, selectedAddress           
        } = this;
        
   # request to API to create Shop
        axios.post('http://localhost:3000/shops', {
          name: name,
          latitude: selectedAddress.lat,
          longitude: selectedAddress.lng,
          address: selectedAddress.address 
        })
          .then(response => {
            # Restore form and add alert if needed
            this.name = ''
            this.selectedAddress = {} 
          })
          .catch(e => {
            this.error.push(e)
          })
      },
      openSelectedAddressModal() {
        this.$modal.show('selected-address-google-map-modal');
      },
      onSelectedAddress(address) {
        this.selectedAddress = address;
      },
  };
</script>

We have used Modal to display Map to select the address, to use Modal in VueJS, you add the library vue-js-modalwith npm install and add config in main.js file.

import VModal from 'vue-js-modal'

Vue.use(VModal)

Create Modal showing Map to select the address, we create file /src/SelectedAddressGoogleMapModal.vue

<template>
  <modal name="selected-address-google-map-modal" transition="pop-out" :width="900" :height="600" :reset="true">
    <div class="search-place-map">
      <h2>Search address</h2>
      <div class="d-flex">
        <gmap-autocomplete
          class="form-control"
          @place_changed="setAddress">
        </gmap-autocomplete>
        <button class="btn btn-primary" @click="addAddress">Set Address</button>
      </div>
    </div>
    <br>
    <gmap-map
      :center="center"
      :zoom="12"
      style="width:100%;  height: 400px;"
    >
      <gmap-marker
        :key="index"
        v-for="(m, index) in markers"
        :position="m.position"
        @click="center=m.position"
      ></gmap-marker>
    </gmap-map>
  </modal>
</template>

<script>
export default {
  name: "SelectedAddressGoogleMapModal",
  data() {
    return {
      center: { lat: 45.508, lng: -73.587 },
      markers: [],
      selectedAddress: null
    };
  },

  mounted() {
    this.geolocate();
  },

  methods: {
    setAddress(address) {
      this.selectedAddress = address;
    },
    addAddress() {
      if (this.selectedAddress) {
        const marker = {
          lat: this.selectedAddress.geometry.location.lat(),
          lng: this.selectedAddress.geometry.location.lng()
        };
        this.markers.push({ position: marker });
        this.center = marker;
        this.$emit('onSelectedAddress', {
          lat: marker.lat,
          lng: marker.lng,
          address: this.selectedAddress.formatted_address
        })
        this.onSelectedAddress = null;
      }
    },
    geolocate: function() {
      navigator.geolocation.getCurrentPosition(position => {
        this.center = {
          lat: position.coords.latitude,
          lng: position.coords.longitude
        };
      });
    }
  }
};
</script>

Modal components are used

gmap-autocomplete: allows displaying the list of address suggestions when typing
gmap-map: show map
gmap-marker: shows selected locations
The command this.$emit('onSelectedAddress', data)is a method to call back the parent component, here is to update the selected address.

Thus, we have created a shop with lat, lng selected from the map.

5. Display the list of shops in the Map

Suppose, the user wants to display a list of shops in a certain area, like

then we do the following:

First, we need to create an API to return the list of shops in an area, so that we can filter shops in the area, we will rely on searching for latitude and longitude. The idea is to query in the DB to get all shops located in a rectangular box of a part of the map currently displayed, each corner of the rectangle will be created latitude, longitude.

def index  
  shops = Shop.all.where(
    "latitude >= ? AND latitude <= ? AND longitude >= ? AND longitude <= ?",
      params[:bottom_x], params[:top_x], params[:bottom_y], params[:top_y]
  )
 
 render json: shops
end

where the parameters bottom_x, top_x, bottom_y, top_y are the latitude and longitude values ​​of the first 4 corners of the map in the xy coordinate direction

We create Component to display the shop listing in the map: / src / MapShops

<template>
  <div class="section-box map-search">
     <h4 class="section-box__heading">MAP</h4>
    <gmap-map
      ref="mapRef"
      :center="center"
      :zoom="12"
      style="width:100%;  height: 400px;"
      @idle="searchShopsInMap"
    >
      <gmap-marker
        :key="index"
        v-for="(shop, index) in shops"
        :position="shop.position"
      ></gmap-marker>
  </div>
</template>

<script>
  import axios from 'axios'

  export default {
    name: 'MapShops',
    data() {
      return {
        center: { lat: 35.652832, lng: 139.839478 },
        boundMap: {},
        shops: [] 
      };
    },
    components: { SimpleStatementDetail },
    mounted() {
      this.geolocate();
    },
    methods: {
      geolocate() {
        navigator.geolocation.getCurrentPosition(position => {
          this.center = {
            lat: position.coords.latitude,
            lng: position.coords.longitude
          };
        });
      },
      searchShopsInMap() {
        const map = this.$refs.mapRef;
       
        const boundCoordinate = this.$refs.mapRef.$mapObject.getBounds();

        if(!boundCoordinate){return}
        const northEast = boundCoordinate.getNorthEast();
        const southWest = boundCoordinate.getSouthWest();
        const lats = [northEast.lat(), southWest.lat()];
        const lngs = [northEast.lng(), southWest.lng()];

        this.boundMap = {
          top_x: _.max(lats),
          bottom_x: _.min(lats),
          top_y: _.max(lngs),
          bottom_y: _.min(lngs)
        }

        axios.get('http://localhost:3000/shops')
          .then(response => {
            this.shops = response.data.map(shop => {
              return {
                 ...shop, position: {
                   lat: shop.latitude, lng: shop.longitude
                 }
              }
            }) 
          })
          .catch(e => {
            this.error.push(e)
          })
      }
    }
  };
</script>

where the idlegmap-map event is called after we have moved the map to another area, then we will calculate the coordinates of the frame and call the API to return the shops in that new area. .

6. Combining components into the App

We added component to create shop and display shop in map /src/App.vue file to display

<template>
  <div id="app">
    <div class="create-shop">
       <create-shop />
    </div>
    
    <div class="list-shop-in-map">
       <map-shops />
    </div>
  </div>
</template>

<script>

import MapShops from './MapShops'
import CreateShop from './CreateShop'

export default {
  name: 'app',
  components: {
    MapShops, CreateShop
  }
}
</script>

Conclude

Above are the steps to build a VueJS app that uses Rails as an API, and in conjunction with Google map, allows easy integration of information into the map.

We can further improve the use vuexof state management, which helps solve the case when the shop is finished, will be able to immediately show that shop in the map containing the shop list. And add the shop information display when clicking on a marker in the map using gmap-info-window.

Thanks for watching.

#vuejs #javascript #vue-js

Create VueJS application with Google Map
1 Likes74.60 GEEK