Преглед на файлове

AutoComplete & AutoComplete Component

Vincent GUFFON преди 4 години
родител
ревизия
c8e04e6eb6
променени са 2 файла, в които са добавени 102 реда и са изтрити 68 реда
  1. 64 16
      components/Ui/Input/Autocomplete.vue
  2. 38 52
      components/Ui/Input/AutocompleteWithAPI.vue

+ 64 - 16
components/Ui/Input/Autocomplete.vue

@@ -7,21 +7,34 @@ Liste déroulante avec autocompletion
 <template>
   <main>
     <v-autocomplete
+      autocomplete="search"
+      :value="data"
       :items="itemsToDisplayed"
       :label="$t(label)"
-      item-text="textDisplay"
+      item-text="itemTextDisplay"
       :item-value="itemValue"
+      :no-data-text="$t('autocomplete_research')"
+      :no-filter="noFilter"
+      auto-select-first
       :multiple="multiple"
       :loading="isLoading"
       :return-object="returnObject"
-      @change="$emit('update', $event, field)"
-    />
+      :search-input.sync="search"
+      :prepend-icon="prependIcon"
+      @input="$emit('update', $event, field)"
+    >
+      <template v-if="slotText" v-slot:item="data">
+        <v-list-item-content v-text="data.item.slotTextDisplay"></v-list-item-content>
+      </template>
+    </v-autocomplete>
   </main>
 </template>
 
 <script lang="ts">
-import { computed, defineComponent, ComputedRef } from '@nuxtjs/composition-api'
+import { computed, defineComponent, ComputedRef, Ref, ref, watch, onUnmounted } from '@nuxtjs/composition-api'
 import { AnyJson } from '~/types/interfaces'
+import * as _ from 'lodash'
+import {$objectProperties} from "~/services/utils/objectProperties";
 
 export default defineComponent({
   props: {
@@ -36,7 +49,7 @@ export default defineComponent({
       default: null
     },
     data: {
-      type: String,
+      type: [String, Number, Object],
       required: false,
       default: null
     },
@@ -57,6 +70,11 @@ export default defineComponent({
       type: Array,
       required: true
     },
+    slotText: {
+      type: Array,
+      required: false,
+      default: null
+    },
     returnObject: {
       type: Boolean,
       default: false
@@ -68,28 +86,58 @@ export default defineComponent({
     isLoading: {
       type: Boolean,
       default: false
-    }
+    },
+    noFilter: {
+      type: Boolean,
+      default: false
+    },
+    prependIcon: {
+      type: String
+    },
   },
-  setup (props) {
+  setup (props, { emit }) {
+    const search:Ref<string|null> = ref(null)
+
     // On reconstruit les items à afficher car le text de l'Item doit être construit par rapport au itemText passé en props
     const itemsToDisplayed: ComputedRef<Array<AnyJson>> = computed(() => {
       return props.items.map((item: any) => {
-        const textDisplay: Array<string> = []
-        for (const text of props.itemText) {
-          textDisplay.push(item[text as string])
+        const slotTextDisplay: Array<string> = []
+        const itemTextDisplay: Array<string> = []
+
+        if(item){
+          item = $objectProperties.cloneAndFlatten(item)
+
+          //Si on souhaite avoir un texte différent dans les propositions que dans la sélection finale de select
+          if(props.slotText){
+            for (const text of props.slotText) {
+              slotTextDisplay.push(item[text as string])
+            }
+          }
+
+          for (const text of props.itemText) {
+            itemTextDisplay.push(item[text as string])
+          }
         }
-        return Object.assign({}, item, { textDisplay: textDisplay.join(' ') })
+
+        //On reconstruit l'objet
+        return Object.assign({}, item, { itemTextDisplay: itemTextDisplay.join(' '), slotTextDisplay: slotTextDisplay.join(' ') })
       })
     })
 
+    const unwatch = watch(search, _.debounce(async (newResearch, oldResearch) => {
+      if(newResearch !== oldResearch && oldResearch !== null)
+        emit('research', newResearch)
+    }, 500))
+
+    onUnmounted(() => {
+      unwatch()
+    })
+
     return {
       label_field: props.label ?? props.field,
-      itemsToDisplayed
+      itemsToDisplayed,
+      search
     }
   }
 })
 </script>
-
-<style scoped>
-
-</style>

+ 38 - 52
components/Ui/Input/AutocompleteWithAPI.vue

@@ -1,34 +1,31 @@
 <!--
 Liste déroulante avec autocompletion (les données sont issues
-de l'api Opentalent)
+d'une api)
 
 @see https://vuetifyjs.com/en/components/autocompletes/#usage
 -->
-
 <template>
   <main>
-    <v-autocomplete
-      v-model="model"
-      :value="data"
+    <UiInputAutocomplete
+      :field="field"
+      :label="label"
+      :data="data"
       :items="items"
-      :loading="isLoading"
-      :search-input.sync="search"
-      hide-no-data
-      hide-selected
-      item-text="textDisplay"
+      :isLoading="isLoading"
+      :item-text="itemText"
+      :slotText="slotText"
       :item-value="itemValue"
-      :label="$t(label_field)"
-      :placeholder="$t('start_your_research')"
-      prepend-icon="mdi-magnify"
+      prependIcon="mdi-magnify"
       :return-object="returnObject"
+      @research="search"
+      :no-filter="noFilter"
+      @update="$emit('update', $event, field)"
     />
   </main>
 </template>
 
 <script lang="ts">
-import { defineComponent, computed, watch, ref, useContext, onUnmounted, Ref } from '@nuxtjs/composition-api'
-import * as _ from 'lodash'
-import { QUERY_TYPE } from '~/types/enums'
+import {defineComponent, ref, Ref, watch, onUnmounted, toRefs} from '@nuxtjs/composition-api'
 
 export default defineComponent({
   props: {
@@ -42,8 +39,12 @@ export default defineComponent({
       required: false,
       default: null
     },
+    searchFunction: {
+      type: Function,
+      required: true
+    },
     data: {
-      type: String,
+      type: [String, Number, Object],
       required: false,
       default: null
     },
@@ -59,45 +60,36 @@ export default defineComponent({
       type: Array,
       required: true
     },
+    slotText: {
+      type: Array,
+      required: false
+    },
     returnObject: {
       type: Boolean,
       default: false
+    },
+    noFilter: {
+      type: Boolean,
+      default: false
     }
   },
-  setup (props) {
-    const { $dataProvider } = useContext()
-
-    const search:Ref<string|null> = ref(null)
-    const model = ref(null)
-    const count = ref(0)
-    const entries = ref([])
+  setup(props) {
+    const {data} = toRefs(props)
+    const items:Ref<Array<any>> = ref([data.value])
     const isLoading = ref(false)
 
-    const items = computed(() => {
-      return entries.value.map((entry) => {
-        const textDisplay:Array<string> = []
-        for (const text of props.itemText) {
-          textDisplay.push(entry[text as string])
-        }
-        return Object.assign({}, entry, { textDisplay: textDisplay.join(' ') })
-      })
-    })
-
-    const unwatch = watch(search, _.debounce(async (research) => {
-      // Items have already been requested
-      if (isLoading.value) { return }
-
+    const search = async (research:string) => {
       isLoading.value = true
 
-      const response = await $dataProvider.invoke({
-        type: QUERY_TYPE.DEFAULT,
-        url: `gps-coordinate-searching?city=${research}`
-      })
+      const func: Function = props.searchFunction
+      items.value = await func(research, props.field)
 
-      count.value = response.length
-      entries.value = response
       isLoading.value = false
-    }, 500))
+    }
+
+    const unwatch = watch(data,(data) => {
+      items.value = [data]
+    })
 
     onUnmounted(() => {
       unwatch()
@@ -105,16 +97,10 @@ export default defineComponent({
 
     return {
       label_field: props.label ?? props.field,
-      count,
       isLoading,
       items,
-      search,
-      model
+      search
     }
   }
 })
 </script>
-
-<style scoped>
-
-</style>