[Vue] how to encapsulate second time of ui components

Zhentiw發表於2024-10-06

how to encapsulate second time of ui components

For example we have follow code, with one UI component MyInput

// App.vue

<template>
  <div>
    <MyInput></MyInput>
  </div>    
</template>

<script setup>
import MyInput from './components/MyInput.vue'


</script>

// MyInput.vue
<template>
  <di class="my-input">
    <el-input></el-input>
  </div>    
</template>

<style scoped>
.my-input {
    transition: 0.3s;
}

.my-input:hover,
.my-input:focus-within {
    filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.45))
}
</style>   

1. binding the attributes

For example, <MyInput v-model="data" placeholder="Address"></MyInput>, input has lots of attributes, therefore we don't want to add everything into props;

In vue, we can use v-bind="$attrs"

// App.vue
<template>
  <div>
    <MyInput v-model="data" placeholder="Address"></MyInput>
  </div>    
</template>

<script setup>
import {ref} from "vue"
import MyInput from './components/MyInput.vue'
const data = ref("")
</script>

// MyInput.vue
<template>
  <di class="my-input">
    <el-input v-bind="$attrs"></el-input>
  </div>   
</template>
<script>
export default {}
</script>

<style scoped>
.my-input {
    transition: 0.3s;
}

.my-input:hover,
.my-input:focus-within {
    filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.45))
}
</style>   

2. Slots

input elements has many slots as well. We need to support slots as well in our component.

// App.vue

<template>
  <div>
    <MyInput>
      <template #prepend>
        <el-select placeholder="Select" style="width: 115px">
          <el-option label="Restaurant" value="1" />
          <el-option label="Order No." value="2" />
          <el-option label="Tel" value="3" />
        </el-select>
      </template>
      <template #append>
        <el-button :icon="Search" />
      </template>
    </MyInput>
  </div>    
</template>

// MyInput.vue
<template>
  <div class="my-input">
    <el-input v-bind="$attrs">
      <template v-for="(value, name) in $slots" #[name]="slotData">
        <slot :name="name" v-bind="slotData || {}"></slot>
      </template>
    </el-input>
  </div>   
</template>
<script>
export default {
}
</script>

3. ref

From MyInput, you might want to add a ref which should be able to access the methods of input element.

We can expose those mothods in MyInput component.

// App.vue

<template>
  <div>
    <MyInput ref="inputRef" v-model="data" placeholder="Address">
      <template #prepend>
        <el-select placeholder="Select" style="width: 115px">
          <el-option label="Restaurant" value="1" />
          <el-option label="Order No." value="2" />
          <el-option label="Tel" value="3" />
        </el-select>
      </template>
      <template #append>
        <el-button :icon="Search" />
      </template>
    </MyInput>
  </div>    
</template>

<script setup>
import {ref, onMounted} from "vue"
import MyInput from './components/MyInput.vue'
import {Search} from "@element-plus/icons-vue"
const data = ref("")
const inputRef = ref(null)

onMounted(() => {
    console.log(inputRef.value)
    inputRef.focus()
})
</script>

// MyInput.vue
<template>
  <div class="my-input">
    <el-input ref="inp" v-bind="$attrs">
      <template v-for="(value, name) in $slots" #[name]="slotData">
        <slot :name="name" v-bind="slotData || {}"></slot>
      </template>
    </el-input>
  </div>   
</template>
<script>
export default {
    mounted() {
        console.log(this.$refs.inp)
        const entries = Object.entries(this.$refs.inp)
        for (const [key, value] of entries) {
            this[key] = value
        }
    }
}
</script>

相關文章