В статье я опишу способ создания простого компонента карты с отображением мест (маркеров), который будет сделан на 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: {}
}
После добавления кода в компонент, карта будет выглядеть так:
Добавление маркеров
Карта видна, центрирована, но на ней ещё нет объектов!
Для добавления объектов создадим массив маркеров, в котором будут содержаться координаты. Для простоты, массив поместим в файл компонента, далее его можно вынести в массив 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)
Готово!
Карта теперь выглядит так:
Итоговый код компонента:
<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)
bohdan4ik
24.09.2017 10:59+1Не могу судить, насклько vue-матичен код в статье, но вместо
const element = document.getElementById(this.name)
лучше использоватьref
vuejs.org/v2/api/#ref
Как минимум, это позволить разместить более одного компонента на странице.BRAINKIT Автор
24.09.2017 11:34Строка выше — это получение div`a карты, у которого id является свойством компонента — name. Передаётся name из index файла при подключении компонента в строке:
<google-map :name="name"></google-map>
Соответственно, меняя параметр name мы можем разместить несколько компонентов карт на 1 странице. А что бы маркеры были разные, массив маркеров нужно вынести в свойства компонента props аналогично name и, аналогично name задать отдельные массивы маркеров. В статье я упомянул про эту возможность.bohdan4ik
24.09.2017 11:54+1Да, это я понимаю. Но зачем заставлять придумывать уникальный ID (пусть даже сгенерированный или полученый любым другим способом), если этого можно не делать? Тем более, что сам фреймворк (можете кидать тапками) предоставляет инструмент, который гарантированно будет работать в любых условиях?
letovlive
24.09.2017 12:22+1refs лучше вообще использовать в крайнем случае, так как нарушается сам принцип атомарности компонента, здесь же можно просто искать в самом компоненте через this.$el.
kleinmaximus
24.09.2017 11:05Наверное, лучше будет вынести поля options из mounted в props и указать там значения по умолчанию для координат и коэффициента увеличения.
И почему Вы подключаете API Гугл.Карт напрямую через script? Мне кажется, это должно автоматически происходить во время первого подключения компонента к странице (смотрим тут — github.com/vuejs/awesome-vue#map). Либо нужно передавать зависимость от API карт (google.map) через параметр компонента, а то у Вас появляется жесткая связанность.BRAINKIT Автор
24.09.2017 11:15Согласен со всем выше сказанным. Но! В рамках статьи я хотел описать максимально простой вариант компонента с минимальным количеством кода, что бы было общее понимание концепции. Если интересно — могу улучшить код с точки зрения правильности/стандартов написания и написать статью для динамической карты объектов в которой массив vue будет связан с маркерами карты, при изменении массива — будут сразу меняться маркеры на карте.
bjornd
24.09.2017 11:28Только не понятно при чем здесь вообще Vue, никаких особенностей фреймворка не продемонстрировано, а самые интересные вещи начинаются когда данные модифицируются. Как раз для обработки изменений в данных нужны современные client-сайдовые фреймворки. С помощью React например реализация динамических компонент на основе Google Maps или Leaflet с развесистой иерархией дочерних компонентов реализуется достаточно удобно. Если вкратце, то каждый слой, маркер, path это отдельный React-компонент, он создает необходимые DOM-элементы и добавляет события при инициализации, очищает все что нужно при удалении. Всю остальную работу (когда и что нужно создать, удалить, заменить) выполняет React. Получаются удобные в использовании компоненты для фреймворка, API которых не сильно отличается от родного (Google Maps, Leaflet). Интересно как бы все это было реализовано на Vue.
letovlive
24.09.2017 12:22+2чтобы eslint работал, необходимо в .eslintrc.js добавить google в globals.
zmeykas
24.09.2017 16:01интересно что в руководстве говорится о
VueJs2 с использованием es6
но синтаксис методов компонента в стиле es5:
data: function () {}
вместо
data() {}
BRAINKIT Автор
24.09.2017 16:34-1Синтаксис методов на основе:
data() {}
в es6 не обязателен, при желании можно использовать и синтаксис
data: function () {}
zmeykas
24.09.2017 16:45Ну в этом и была суть данного нововведения в ES6 — упростить синтаксис. Я не пытаюсь сказать что в вашей статье сделано не правильно, я говорю что можно лучше.
Kasheftin
24.09.2017 20:47-1Почему бы просто не взять vue-google-maps?
BRAINKIT Автор
25.09.2017 10:16Во-первых: в статье я хотел описать для новичков в vue как создать компонент или для профессионалов, которые хотят посмотреть как работает vue.
Во-вторых: на мой взгляд vue-google-maps избыточен, не всем и не всегда нужен весь функционал api google карт.
BlastPy
25.09.2017 10:02Такой вопрос а можно ли в vue google maps после перехода (маркер как линк) сделать центром карт. позицию маркера?
BRAINKIT Автор
25.09.2017 10:06Если имеется ввиду компонент из статьи, а не vue2-google-maps, то нет. Опишу в следующей статье как сделать данный функционал.
acupofspirt
А вы точно открывали в браузере этот компонент перед написанием статьи? 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."
BRAINKIT Автор
Открывал, конечно.
Для написания статьи я использовал сборку vue на основе nuxtjs.org. Подобной ошибки не было. Если возможно — дайте пруф линк на информацию, где описано, что тег script запрещён в шаблонах vue, интересно.
acupofspirt
medium.com/@lassiuosukainen/how-to-include-a-script-tag-on-a-vue-component-fe10940af9e8
acupofspirt
У меня не то что ошибка в консоли, даже вебпак отказывается билдить проект если тэги script/style использовать в шаблонах.