CLIEN

본문 바로가기 메뉴 바로가기 폰트크기변경 색상반전보기
톺아보기 공감글 추천글
커뮤니티 C 모두의광장 F 모두의공원 I 사진게시판 Q 아무거나질문 D 정보와자료 N 새로운소식 T 유용한사이트 P 자료실 E 강좌/사용기 L 팁과강좌 U 사용기 · 체험단사용기 W 사고팔고 J 알뜰구매 S 회원중고장터 B 직접홍보 · 보험상담실 H 클리앙홈
소모임 소모임전체 임시소모임 ·굴러간당 ·주식한당 ·아이포니앙 ·자전거당 ·MaClien ·방탄소년당 ·일본산당 ·가상화폐당 ·골프당 ·나스당 ·노젓는당 ·바다건너당 ·개발한당 ·안드로메당 ·야구당 ·키보드당 ·소셜게임한당 ·콘솔한당 ·육아당 ·포뮬러당 ·땀흘린당 ·퐁당퐁당 ·캠핑간당 ·레고당 ·PC튜닝한당 ·냐옹이당 ·테니스친당 ·갖고다닌당 ·소시당 ·날아간당 ·클다방 ·걸그룹당 ·디아블로당 ·VR당 ·덕질한당 ·와인마신당 ·사과시계당 ·리눅서당 ·스팀한당 ·패스오브엑자일당 ·시계찬당 ·찰칵찍당 ·영화본당 ·IoT당 ·축구당 ·이륜차당 ·젬워한당 ·미드당 ·임시소모임 ·안경쓴당 ·그림그린당 ·개판이당 ·물고기당 ·심는당 ·활자중독당 ·소리당 ·방송한당 ·맛있겠당 ·도시어부당 ·테스트당 ·패셔니앙 ·헌팅한당 ·스타한당 ·WebOs당 ·여행을떠난당 ·트윗당 ·LOLien ·날아올랑 ·적는당 ·윈폰이당 ·심야식당 ·공대시계당 ·X세대당 ·빨콩이당 ·클래시앙 ·e북본당 ·창업한당 ·히어로즈한당 ·WOW당 ·요리한당 ·대구당 ·나혼자산당 ·터치패드당 ·3D메이킹 ·배드민턴당 ·농구당 ·블랙베리당 ·곰돌이당 ·뽀록이당(당구) ·블록체인당 ·보드게임당 ·볼링친당 ·문명하셨당 ·쿠키런당 ·DANGER당 ·동숲한당 ·이브한당 ·FM한당 ·차턴당 ·총쏜당 ·내집마련당 ·하스스톤한당 ·인스타한당 ·KARA당 ·어학당 ·가죽당 ·Mabinogien ·땅판당 ·오른당 ·MTG한당 ·노키앙 ·소풍간당 ·라즈베리파이당 ·품앱이당 ·리듬탄당 ·Sea마당 ·SimSim하당 ·윈태블릿당 ·미끄러진당 ·파도탄당
© CLIEN.NET
공지잠시후 21시부터 약 30분간 접속이 불안정 할 수있습니다.
l

팁과강좌

PC/모바일 Vue.js scopedSlot을 활용한 google maps api wrapper component 작성 1

봉진@porybong2
7,174
2019-01-17 15:24:41 211.♡.246.251

Vue.js 공식 예제에도 나와있는 부분입니다.

https://vuejs.org/v2/cookbook/practical-use-of-scoped-slots.html#ad


1. 개발환경 구성

간단히 vue/cli를 이용해 구성합니다.


> npm install -g @vue/cli


> vue create project-name

>> Manually select features

>> check Babel, Linter/Formatter

>> ESLint + Standard config

>> Lint on save

>> In dedicated config files

>> N

(아마도 전부 기본설정..)


> cd project-name

> npm run serve


http://localhost:8080/ 에 접속하시면 Welcome to Your Vue.js App 화면이 나옵니다.


디렉토리 구조는 다음과 같습니다.

project-name

ㄴpublic

ㄴsrc

ㄴㄴassets

ㄴㄴcomponents

ㄴㄴApp.vue

ㄴㄴmain.js


2. google maps api loader 구현


project-name/src/js/googleMapsApiLoader.js

let googleApi
const callback = 'googleMapsApiLoadCallback'

function googleMapsApiLoader (key) {
  const script = document.createElement('script')
  script.type = 'text/javascript'
  script.src = `https://maps.googleapis.com/maps/api/js?key=${key}&callback=${callback}`

  const head = document.querySelector('head')
  head.appendChild(script)
}

function load (key) {
  if (googleApi) return Promise.resolve(googleApi)
  return new Promise((resolve, reject) => {
    googleMapsApiLoader(key)
    window[callback] = function () {
      googleApi = window.google
      resolve(googleApi)
    }

    setTimeout(() => {
      if (!window.google) reject(new Error('Google maps api load failed'))
    }, 5000)
  })
}

export default load

npm 에 등록되어있는 googleMapsApiLoader의 코드입니다. 필요한 부분만 간단하게 발췌했습니다.


3. googleMap Higher-order component 작성


project-name/src/components/map.js

import googleMapsApiLoader from '@/js/googleMapsApiLoader'

export default (key) => {
  return {
    render (h) {
      const el = 'div'
      const options = { ref: 'map' }
      return h(el, options)
    },
    data () {
      return {
        google: null,
        map: null
      }
    },
    mounted () {
      googleMapsApiLoader(key)
        .then((google) => {
          this.google = google
          this.drawMap()
        })
    },
    methods: {
      drawMap () {
        const el = this.$refs.map
        const map = new this.google.maps.Map(el, {
          center: {
            lat: 37,
            lng: 127
          },
          zoom: 14
        })

        this.map = map
      }
    }
  }
}

api 키를 받아 api 를 로드하고 맵을 그리는 컴포넌트 입니다.


4. map component 를 전역으로 등록해주는 플러그인 작성


project-name/src/plugins/vue-google-map-wrapper.js

import googleMapFactory from '@/components/map'

function install (Vue, options) {
  const { apiKey } = options

  const googleMap = googleMapFactory(apiKey)

  Vue.component('googleMap', googleMap)
}

export default install


5. Vue.use(plugin) 으로 google map component 전역 등록

google maps api 키는 발급받으셔야합니다.


project-name/src/main.js

import Vue from 'vue'
import App from './App.vue'
import vueGoogleMapWrapper from '@/plugins/vue-google-map-wrapper'

Vue.config.productionTip = false

Vue.use(vueGoogleMapWrapper, { apiKey: 'YOUR_API_KEY' })

new Vue({
  render: h => h(App)
}).$mount('#app')


6. app.vue 를 편집해서 확인해봅시다.


project-name/src/app.vue

<template>
  <div id="app">
    <google-map class="googleMap"></google-map>
  </div>
</template>

<script>
export default {
  name: 'app'
}
</script>

<style>
  .googleMap {
    height: 500px
  }
</style>


http://localhost:8080 에 접속하면 구글맵이 떠있습니다.


7. scopedSlot 으로 google과 map 노출

맵에 마커등을 찍기위해서는 window.google 과 google.maps.Map 이 필요합니다. 

둘다 방금만든 구글맵 컴포넌트의 data에 등록되어있습니다.

구글맵 컴포넌트에 scopedSlot 을 생성해 google과 map을 노출하여 자식 컴포넌트가 사용할 수 있도록 만듭니다.


project-name/src/components/map.js

// ...
render (h) {
  const el = 'div'
  const options = { ref: 'map' }

  if (this.google && this.map && Object.keys(this.$scopedSlots).length) {
    const scopedSlot = this.$scopedSlots.default({
      google: this.google,
      map: this.map
    })
    return h(el, options, [scopedSlot])
  }
  return h(el, options)
},
// ...


project-name/src/app.js

// ...
<google-map class="googleMap">
  <div slot-scope="{ google, map }"></div>
</google-map>
// ...

앞으로 이런식으로 구글맵 컴포넌트의 google과 map 에 접근할 수 있습니다.


8. marker wrapper 컴포넌트 작성

구글맵 컴포넌트와 동일한 방식으로 만듭니다.


project-name/src/components/marker.js

export default {
  props: {
    google: { type: Object, required: true },
    map: { type: Object, required: true },
    position: { type: Object, required: true }
  },
  render (h) {
    return h()
  },
  mounted () {
    this.drawMarker()
  },
  destroyed () {
    this.clearMarker()
  },
  data () {
    return {
      marker: null
    }
  },
  methods: {
    drawMarker () {
      const marker = new this.google.maps.Marker({
        map: this.map,
        position: this.position
      })
      
      this.marker = marker
    },
    clearMarker () {
      this.marker.setMap(null)
    }
  }
}

google, map, position 을 props로 받고,  마운트 될때 해당 맵에 마커를 그립니다.

이 컴포넌트가 사라진다고해서 맵에 마커가 사라지는것은 아니기때문에, destroyed hook에 marker.setMap(null)을 걸어줍니다.

플러그인에도 등록합니다.


project-name/src/plugins/vue-google-map-wrapper.js

import googleMapFactory from '@/components/map'
import googleMapMarker from '@/components/marker'

function install (Vue, options) {
  const { apiKey } = options

  const googleMap = googleMapFactory(apiKey)
  
  const components = {
    googleMap,
    googleMapMarker
  }

  Object.entries(components).forEach(([componentName, component]) => {
    Vue.component(componentName, component)
  })
}

export default install


9. 마커 찍어보기


project-name/src/app.vue

<template>
  <div id="app">
    <google-map class="googleMap">
      <div slot-scope="{ google, map }">
        <google-map-marker
          :google="google"
          :map="map"
          :position="position">
        </google-map-marker>
      </div>
    </google-map>
  </div>
</template>

<script>
export default {
  name: 'app',
  data () {
    return {
      position: {
        lat: 37,
        lng: 127
      }
    }
  }
}
</script>

<style>
  .googleMap {
    height: 500px
  }
</style>


10. 이벤트 리스닝

맵과 마커에 이벤트 리스너를 등록하고 활용해봅니다.


project-name/src/components/map.js

// ...
methods: {
  drawMap () {
    const el = this.$refs.map
    const map = new this.google.maps.Map(el, {
      center: {
        lat: 37,
        lng: 127
      },
      zoom: 14
    })
    
    map.addListener('click', (e) => {
      this.$emit('click', e)
    })

    this.map = map
  }
}
// ...


project-name/src/components/marker.js

// ...
methods: {
  drawMarker () {
    const marker = new this.google.maps.Marker({
      map: this.map,
      position: this.position
    })
    
    marker.addListener('click', (e) => {
      this.$emit('click', e)
    })

    this.marker = marker
  },
  clearMarker () {
    this.marker.setMap(null)
  }
}
// ...


project-name/src/app.vue

<template>
  <div id="app">
    <google-map class="googleMap" @click="addMarker">
      <div slot-scope="{ google, map }">
        <google-map-marker
          v-for="(marker, index) in markers"
          :google="google"
          :map="map"
          :key="index"
          :position="marker.position"
          @click="panTo($event, map)"
        >
        </google-map-marker>
      </div>
    </google-map>
  </div>
</template>

<script>
export default {
  name: 'app',
  data () {
    return {
      markers: [
        { position: { lat: 37, lng: 126.98 } },
        { position: { lat: 37, lng: 126.99 } },
        { position: { lat: 37, lng: 127.00 } },
        { position: { lat: 37, lng: 127.01 } },
        { position: { lat: 37, lng: 127.02 } }
      ]
    }
  },
  methods: {
    addMarker (e) {
      const { lat, lng } = e.latLng.toJSON()
      this.markers.push({
        position: {
          lat, lng
        }
      })
    },
    panTo (e, map) {
      map.panTo(e.latLng)
    }
  }
}
</script>

<style>
  .googleMap {
    height: 500px
  }
</style>


11. googleMaps custom control 컴포넌트 작성

맵에 custom control 을 쉽게 추가할 수 있는 컴포넌트를 작성해봅니다.


project-name/src/components/custom-control.js

const name = 'customControl'

export default {
  props: {
    google: { type: Object, required: true },
    map: { type: Object, required: true },
    position: { type: String, default: 'LEFT_TOP' }
  },
  render (h) {
    const el = 'div'
    const options = { ref: name }
    const slot = this.$slots.default[0]

    return h(el, options, [slot])
  },
  mounted () {
    this.addControl()
  },
  methods: {
    addControl () {
      const el = this.$refs[name]
      this.map.controls[this.google.maps.ControlPosition[this.position]].push(el)
    }
  }
}


플러그인에도 등록합니다.

project-name/src/plugins/vue-google-map-wrapper.js

import googleMapFactory from '@/components/map'
import googleMapMarker from '@/components/marker'
import googleMapCustomControl from '@/components/custom-control'

function install (Vue, options) {
  const { apiKey } = options

  const googleMap = googleMapFactory(apiKey)

  const components = {
    googleMap,
    googleMapMarker,
    googleMapCustomControl
  }

  Object.entries(components).forEach(([componentName, component]) => {
    Vue.component(componentName, component)
  })
}

export default install


사용은 다음과같이 합니다.


project-name/src/app.vue

<template>
  <div id="app">
    <google-map class="googleMap" @click="addMarker">
      <div slot-scope="{ google, map }">
        <google-map-marker
          v-for="(marker, index) in markers"
          :google="google"
          :map="map"
          :key="index"
          :position="marker.position"
          @click="panTo($event, map)"
        >
        </google-map-marker>
        <google-map-custom-control
          :google="google"
          :map="map"
          position="BOTTOM_CENTER">
          <input type="text">
        </google-map-custom-control>
      </div>
    </google-map>
  </div>
</template>
// ...


github: https://github.com/bongjinpark1/vue-google-maps-wrapper


궁금증이나 더 좋은 패턴등 댓글달아주세요.

봉진@porybong2 님의 게시글 댓글
  • 주소복사
  • Facebook
  • Twitter
0명
댓글 • [1] 을 클릭하면 간단한 회원메모를 할 수 있습니다.
작돌이
LINK
#97630077
IP 211.♡.141.149
19-01-20 2019-01-20 15:11:12
·
잘 봤습니다 감사해요
새로운 댓글이 없습니다.
이미지 최대 업로드 용량 15 MB / 업로드 가능 확장자 jpg,gif,png,jpeg
지나치게 큰 이미지의 크기는 조정될 수 있습니다.
목록으로
글쓰기
글쓰기
목록으로 댓글보기 이전글 다음글
아이디·비번 찾기 회원가입
이용규칙 운영알림판 운영소통 재검토요청 도움말 버그신고
고객지원
  • 게시물 삭제 요청
  • 쪽지 신고
  • 닉네임 신고
  • 제보 및 기타 제안
개인정보처리방침 이용약관 책임의 한계와 법적고지 청소년 보호정책
©   •  CLIEN.NET
GIF 파일 다운로드
0 0 0 0
GIF 파일을 다운로드 받으려면
화면에 표시된 4자리 번호를 입력해주세요.