Address.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. <template>
  2. <div>
  3. <v-autocomplete
  4. v-model="model"
  5. :loading="loading"
  6. :items="items"
  7. :search-input.sync="search"
  8. hide-no-data
  9. hide-details
  10. return-object
  11. auto-select-first
  12. clearable
  13. :label="$t('where') + ' ?'"
  14. outlined
  15. append-icon="mdi-crosshairs-gps"
  16. @change="$emit('change', $event ? $event.value : '')"
  17. @click:append="geolocalizeUser"
  18. />
  19. <v-snackbar :value="errorMsg !== ''">
  20. {{ errorMsg }}
  21. <template #action="{ attrs }">
  22. <v-btn text v-bind="attrs" @click="errorMsg=''">
  23. {{ $t('close') }}
  24. </v-btn>
  25. </template>
  26. </v-snackbar>
  27. </div>
  28. </template>
  29. <script>
  30. export default {
  31. props: {
  32. value: {
  33. type: String,
  34. required: false,
  35. default: ''
  36. },
  37. type: {
  38. type: String,
  39. required: false,
  40. default: 'housenumber',
  41. validator (value) {
  42. return ['housenumber', 'street', 'locality', 'municipality'].includes(value)
  43. }
  44. },
  45. limit: {
  46. type: Number,
  47. required: false,
  48. default: 5
  49. },
  50. autocomplete: {
  51. type: Boolean,
  52. required: false,
  53. default: true
  54. }
  55. },
  56. data () {
  57. return {
  58. model: null,
  59. search: null,
  60. features: [],
  61. loading: false,
  62. errorMsg: ''
  63. }
  64. },
  65. computed: {
  66. items () {
  67. return this.features.map((f) => {
  68. return {
  69. text: f.properties.name + ' (' + f.properties.postcode + ')',
  70. value: { longitude: f.geometry.coordinates[0], latitude: f.geometry.coordinates[1] },
  71. disabled: false
  72. }
  73. })
  74. }
  75. },
  76. watch: {
  77. search (val) {
  78. if (!val) {
  79. this.features = []
  80. return
  81. }
  82. this.loading = true
  83. // Lazily load input items
  84. const apiUrl = 'https://api-adresse.data.gouv.fr/search/' +
  85. `?type=${this.type}` +
  86. `&autocomplete=${this.autocomplete ? 1 : 0}` +
  87. `&limit=${this.limit}` +
  88. `&q=${val}`
  89. fetch(encodeURI(apiUrl))
  90. .then(res => res.json())
  91. .then(({ features }) => {
  92. this.features = features
  93. })
  94. .catch((err) => {
  95. // eslint-disable-next-line no-console
  96. console.error(err)
  97. })
  98. .finally(() => {
  99. this.loading = false
  100. })
  101. }
  102. },
  103. methods: {
  104. clear () {
  105. this.model = null
  106. this.search = null
  107. this.features = []
  108. this.loading = false
  109. this.errorMsg = ''
  110. },
  111. geolocalizeUser () {
  112. if (navigator.geolocation) {
  113. navigator.geolocation.getCurrentPosition(
  114. (position) => {
  115. this.latitude = position.coords.latitude
  116. this.longitude = position.coords.longitude
  117. this.text = this.$t('my_position')
  118. },
  119. () => {
  120. this.errorMsg = this.$t('geolocation_error')
  121. }
  122. )
  123. } else {
  124. this.errorMsg = this.$t('geolocation_not_supported')
  125. }
  126. }
  127. }
  128. }
  129. </script>
  130. <style>
  131. .v-input__control {
  132. height: 56px;
  133. }
  134. </style>