Google Maps API — это сервис геолокации. С помощью этого сервиса мы можем отобразить карту и места на карте.

В статье я опишу способ создания простого компонента карты с отображением мест (маркеров), который будет сделан на vuejs.

Чтобы создать компонент на основе статьи, нужно самостоятельно подготовить «hello word» приложение на VueJs2 с использованием es6. Итак по порядку:

Установка доступа к API


Для начала нужно получить API ключ на странице. После получения ключа, мы добавим скрипт подключения google maps api в файл index.vue нашего приложения

<script src="https://maps.googleapis.com/maps/api/js?key=[YOUR API KEY]"></script>

В скрипте необходимо заменить "[YOUR API KEY]" на ваш ключ Google Maps API.
Скрипт даёт доступ к глобальному google объекту, который будет использоваться для создания карты.

Создание основной структуры файлов


Структура файлов будет состоять из папки components, в ней файл googleMap.vue для реализации компонента и файл index.vue — основной файл «hello word» приложения.

Начнём с создания шаблона в googleMap.vue:

<template>
   <div class="google-map" :id="name"></div>
</template>

Для возможности установки нескольких карт на одной странице, добавим атрибут id с параметром name — имя карты, будет задаваться в параметрах компонента.

Параметры компонента и логика работы карты храниться в части файла со скриптами. Подготовим основу для скриптов компонента в файле googleMap.vue:

<script>
export default {
        name: 'google-map',
        props: ['name'],
        data: function () {
            return {}
        },
        mounted: function () {
        },
        methods: {
        }
    }
</script>

В коде выше: название компонента google-map, свойство name, метод data в котором будут храниться параметры, метод mounted который вызывается при создании компонента и объект methods в котором будут вызываться методы работы с картой.

Теперь добавим немного стилей для карты:

<style scoped>
    .google-map {
        width: 640px;
        height: 480px;
        margin: 0 auto;
        background: gray;
    }
</style>

Бинго!

Основа файла компонента готова, далее вызовем компонент в файле index.vue:

<template>
    <div class="container">
        <google-map :name="name"></google-map>
        <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCpg_bVTl4WhiOION5V-wYT8OGhYg6uHTM"></script>
    </div>
</template>
<script>
    import googleMap from '~/components/googleMap.vue'

    export default {
        data: function () {
            return {
                name: 'map',
                  }
        },
        mounted: function () {
        },
        components: {googleMap}
    }
</script>

Компонент вызывается тегом google-map, который используется для названия компонента. Сам компонент загружается через метод import и присоединяется к index.vue в объекте components.

После создания структуры файлов и основного кода, компонент подключается, но карта не отображается, для отображения нужно создать новую карту с помощью new google.maps.Map

Отображение карты


Вернёмся к файлу компонента. Скрипт, который добавляет карту вызовем в методе mounted, этот метод вызывается после загрузки шаблона, соответственно доступен заготовленный пустой div. В методе mounted создадим новую карту с помощью new google.maps.Map. Карта будет дочерним объектом нашего div. Минимально для создания карты укажем zoom level и координаты центра:

export default {
        name: 'google-map',
        props: ['name'],
        data: function () {
            return {
                map: ''
            }
        },
        computed: {
            mapMarkers: function () {
                return this.markers
            }
        },
        mounted: function () {
            const element = document.getElementById(this.name)
            const options = {
                zoom: 14,
                center: new google.maps.LatLng(59.93, 30.32)
            }
            this.map = new google.maps.Map(element, options)
        },
        methods: {}
    }

После добавления кода в компонент, карта будет выглядеть так:

image

Добавление маркеров


Карта видна, центрирована, но на ней ещё нет объектов!

Для добавления объектов создадим массив маркеров, в котором будут содержаться координаты. Для простоты, массив поместим в файл компонента, далее его можно вынести в массив props:

data: function () {
   return {
       map: '',
       markers: [
           {
               position: {
                   latitude: 59.93,
                   longitude: 30.32
               }
           },
           {
               position: {
                   latitude: 59.928,
                   longitude: 30.32
               }
           }
       ]
   }
}

В массиве 2 объекта маркеров, в которых в position хранятся координаты маркеров. Координаты сохранены отдельно в position, чтобы в будущем было удобно добавить другие поля маркерам, например title.

После создания массива маркеров — нужно добавить их на карту, для этого внутри функции mounted обойдём массив, а в процессе обхода создадим маркер с помощью new google.maps.Marker:

mounted: function () {
            const element = document.getElementById(this.name)
            const options = {
                zoom: 14,
                center: new google.maps.LatLng(59.93, 30.32)
            }
            this.map = new google.maps.Map(element, options)
            this.markers.forEach((marker) => {
                const position = new google.maps.LatLng(marker.position.latitude, marker.position.longitude)
                marker.map = this.map
                marker.position = position
                new google.maps.Marker(marker)
            })
        }

В процессе обхода мы сначала создали объект position — который необходим для указания позиции маркера, далее привязали к маркеру объект карты map и позицию position, далее создали маркер с помощью new google.maps.Marker(marker)

Готово!

Карта теперь выглядит так:

image

Итоговый код компонента:

<template>
    <div class="container">
        <div class="google-map" :id="name"></div>
    </div>
</template>

<script>
    export default {
        name: 'google-map',
        props: ['name'],
        data: function () {
            return {
                map: '',
                markers: [
                    {
                        position: {
                            latitude: 59.93,
                            longitude: 30.32
                        }
                    },
                    {
                        position: {
                            latitude: 59.928,
                            longitude: 30.32
                        }
                    }
                ]
            }
        },
        computed: {
            mapMarkers: function () {
                return this.markers
            }
        },
        mounted: function () {
            /*eslint-disable */
            const element = document.getElementById(this.name)
            const options = {
                zoom: 14,
                center: new google.maps.LatLng(59.93, 30.32)
            }
            this.map = new google.maps.Map(element, options)

            this.markers.forEach((marker) => {
                const position = new google.maps.LatLng(marker.position.latitude, marker.position.longitude)
                marker.map = this.map
                marker.position = position
                new google.maps.Marker(marker)
            })
            /*eslint-enable */
        },
        methods: {
        }
    }
</script>

<style scoped>
    .google-map {
        width: 640px;
        height: 480px;
        margin: 0 auto;
        background: gray;
    }
</style>

Если установлен eslint, его нужно отключить командами eslint-disable/eslint-enable для секции mounted, чтобы он не ругался на объект google.maps.

Итоговый код index файла:

<template>
    <div class="container">
        <google-map :name="name"></google-map>
        <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCpg_bVTl4WhiOION5V-wYT8OGhYg6uHTM"></script>
    </div>
</template>

<script>
    import googleMap from '~/components/googleMap.vue'

    export default {
        data: function () {
            return {
                name: 'map'
            }
        },
        mounted: function () {
        },
        components: {googleMap}
    }
</script>

Статья создана по мотивам вот этой статьи.

Комментарии (24)


  1. acupofspirt
    24.09.2017 10:58

    А вы точно открывали в браузере этот компонент перед написанием статьи? Vue вроде как не разрешает использовать тэг script в своих шаблонах. Будет ошибка навроде: "Avoid placing tags with side-effects in your templates, such as script, as they will not be parsed."


    А если вы это каким-то образом заведёте, то при повторном монтировании такого компонента сама библиотека GMaps поругается: "You have included the Google Maps API multiple times on this page. This may cause unexpected errors."


    1. BRAINKIT Автор
      24.09.2017 11:05

      Открывал, конечно.
      Для написания статьи я использовал сборку vue на основе nuxtjs.org. Подобной ошибки не было. Если возможно — дайте пруф линк на информацию, где описано, что тег script запрещён в шаблонах vue, интересно.



      1. acupofspirt
        24.09.2017 11:25

        У меня не то что ошибка в консоли, даже вебпак отказывается билдить проект если тэги script/style использовать в шаблонах.


  1. bohdan4ik
    24.09.2017 10:59
    +1

    Не могу судить, насклько vue-матичен код в статье, но вместо

    const element = document.getElementById(this.name)
    

    лучше использовать ref

    vuejs.org/v2/api/#ref

    Как минимум, это позволить разместить более одного компонента на странице.


    1. BRAINKIT Автор
      24.09.2017 11:34

      Строка выше — это получение div`a карты, у которого id является свойством компонента — name. Передаётся name из index файла при подключении компонента в строке:

      <google-map :name="name"></google-map>
      

      Соответственно, меняя параметр name мы можем разместить несколько компонентов карт на 1 странице. А что бы маркеры были разные, массив маркеров нужно вынести в свойства компонента props аналогично name и, аналогично name задать отдельные массивы маркеров. В статье я упомянул про эту возможность.


      1. bohdan4ik
        24.09.2017 11:54
        +1

        Да, это я понимаю. Но зачем заставлять придумывать уникальный ID (пусть даже сгенерированный или полученый любым другим способом), если этого можно не делать? Тем более, что сам фреймворк (можете кидать тапками) предоставляет инструмент, который гарантированно будет работать в любых условиях?


        1. BRAINKIT Автор
          24.09.2017 12:18

          Согласен, с помощью ref логичнее сделать!


    1. letovlive
      24.09.2017 12:22
      +1

      refs лучше вообще использовать в крайнем случае, так как нарушается сам принцип атомарности компонента, здесь же можно просто искать в самом компоненте через this.$el.


  1. kleinmaximus
    24.09.2017 11:05

    Наверное, лучше будет вынести поля options из mounted в props и указать там значения по умолчанию для координат и коэффициента увеличения.

    И почему Вы подключаете API Гугл.Карт напрямую через script? Мне кажется, это должно автоматически происходить во время первого подключения компонента к странице (смотрим тут — github.com/vuejs/awesome-vue#map). Либо нужно передавать зависимость от API карт (google.map) через параметр компонента, а то у Вас появляется жесткая связанность.


    1. BRAINKIT Автор
      24.09.2017 11:15

      Согласен со всем выше сказанным. Но! В рамках статьи я хотел описать максимально простой вариант компонента с минимальным количеством кода, что бы было общее понимание концепции. Если интересно — могу улучшить код с точки зрения правильности/стандартов написания и написать статью для динамической карты объектов в которой массив vue будет связан с маркерами карты, при изменении массива — будут сразу меняться маркеры на карте.


      1. vintage
        24.09.2017 11:29

        Если вы берётесь писать обучающие статьи, то в них и надо показывать как делать правильно, а не абы как.


        1. BRAINKIT Автор
          24.09.2017 11:40

          Я уже ответил выше


  1. bjornd
    24.09.2017 11:28

    Только не понятно при чем здесь вообще Vue, никаких особенностей фреймворка не продемонстрировано, а самые интересные вещи начинаются когда данные модифицируются. Как раз для обработки изменений в данных нужны современные client-сайдовые фреймворки. С помощью React например реализация динамических компонент на основе Google Maps или Leaflet с развесистой иерархией дочерних компонентов реализуется достаточно удобно. Если вкратце, то каждый слой, маркер, path это отдельный React-компонент, он создает необходимые DOM-элементы и добавляет события при инициализации, очищает все что нужно при удалении. Всю остальную работу (когда и что нужно создать, удалить, заменить) выполняет React. Получаются удобные в использовании компоненты для фреймворка, API которых не сильно отличается от родного (Google Maps, Leaflet). Интересно как бы все это было реализовано на Vue.


    1. BRAINKIT Автор
      24.09.2017 11:50

      Опишу в следующей статье.


  1. letovlive
    24.09.2017 12:22
    +2

    чтобы eslint работал, необходимо в .eslintrc.js добавить google в globals.


  1. zmeykas
    24.09.2017 16:01

    интересно что в руководстве говорится о

    VueJs2 с использованием es6
    но синтаксис методов компонента в стиле es5:
    data: function () {}
    вместо
    data() {}


    1. BRAINKIT Автор
      24.09.2017 16:34
      -1

      Синтаксис методов на основе:

      data() {}

      в es6 не обязателен, при желании можно использовать и синтаксис
      data: function () {}


      1. zmeykas
        24.09.2017 16:45

        Ну в этом и была суть данного нововведения в ES6 — упростить синтаксис. Я не пытаюсь сказать что в вашей статье сделано не правильно, я говорю что можно лучше.


  1. Kasheftin
    24.09.2017 20:47
    -1

    Почему бы просто не взять vue-google-maps?


    1. BRAINKIT Автор
      25.09.2017 10:16

      Во-первых: в статье я хотел описать для новичков в vue как создать компонент или для профессионалов, которые хотят посмотреть как работает vue.
      Во-вторых: на мой взгляд vue-google-maps избыточен, не всем и не всегда нужен весь функционал api google карт.


  1. BlastPy
    25.09.2017 10:02

    Такой вопрос а можно ли в vue google maps после перехода (маркер как линк) сделать центром карт. позицию маркера?


    1. BRAINKIT Автор
      25.09.2017 10:06

      Если имеется ввиду компонент из статьи, а не vue2-google-maps, то нет. Опишу в следующей статье как сделать данный функционал.


      1. BlastPy
        25.09.2017 13:25

        Спасибо.