1 - Introdução
Antes de falar sobre as técnicas para reutilização de código, vamos apresentar um problema para servir de exemplo:
No Exemplo abaixo, temos um QInput com uma mascara dinâmica, no caso, uma mascara para telefone.
Ou seja, temos uma propriedade computada para montar a mascara, e um watch, que é basicamente um workaround para resolver um problema de usabilidade.
Se por um acaso, precisamos adicionar mais um campo com telefone, temos que duplicar a propriedade computada e o watch.
Ou seja, não deve ser nada produtivo, e nem pratico para manter este código.
2 - Componente Utilitário
Acredito que você deva ter uma pasta cheia de funções utilitárias (utils
, helpers
, services
, etc). Mas, e se no lugar de temos funções, nós tivéssemos componentes? Sim, é isto que iremos fazer aqui.
Um componente utilitário é basicamente o inverso de um componente funcional, ele deve manipular os dados, manter a reatividade, mas não pode renderizar nada que altere a estética do front.
Aqui segue um componente utilitário para resolver o problema da mascara.:
export default {
name: 'MaskPhoneProviderComponent',
props: {
value: String
},
watch: {
mask () {
let input = this.$children[0].$refs.input
requestAnimationFrame(() => {
input.selectionStart = input.value.length
})
}
},
computed: {
mask () {
switch (true) {
case (this.value || '').length <= 8: return '####-#####'
case (this.value || '').length === 9: return '#####-#####'
case (this.value || '').length === 10: return '(##) ####-#####'
default: return '(##) #####-####'
}
}
},
render (h) {
return this.$scopedSlots.default({ mask: this.mask })[0]
}
}
E agora, um exemplo de como utiliza-lo
Vue.component('mask-phone-provider', () => import('components/MaskPhoneProvider.js'))
<mask-phone-provider :value="landline" v-slot="{ mask }">
<q-input v-model="landline" unmasked-value :mask="mask" outlined dark label="Tel. Fixo"></q-input>
</mask-phone-provider>
<mask-phone-provider :value="cellphone" v-slot="{ mask }">
<q-input v-model="cellphone" unmasked-value :mask="mask" outlined dark label="Celular"></q-input>
</mask-phone-provider>
E claro, não poderia deixar vocês sem um codepen completamente funcional:
3 - Transparent Wrappers
Transparent Wrappers, é basicamente um novo componente envelopando o componente original (no caso um QInput), passando os seus atributos, propriedades e [scoped] slots, e de brinde, adicionando novas funcionalidades
Agora iremos implementar um Transparent Wrapper para resolver o caso acima.:
<q-input ref="root" v-bind="$attrs" v-on="$listeners" unmasked-value :mask="mask" v-model="__value">
<slot v-for="(slot, key) in $slots" :name="key" :slot="key" />
<template v-for="(slot, key) in $scopedSlots" :slot="key" slot-scope="scope">
<slot :name="key" v-bind="scope"/>
</template>
</q-input>
export default {
name: 'QPhoneInput',
props: {
value: String
},
watch: {
mask () {
let input = this.$refs.root.$refs.input
requestAnimationFrame(() => {
input.selectionStart = input.value.length
})
}
},
computed: {
mask () {
switch (true) {
case (this.value || '').length <= 8: return '####-#####'
case (this.value || '').length === 9: return '#####-#####'
case (this.value || '').length === 10: return '(##) ####-#####'
default: return '(##) #####-####'
}
},
__value: {
get () { return this.value },
set (value) { this.$emit('input', value) }
}
}
}
Agora, um exemplo de como utilizar este transparent wrapper no código.:
Vue.component('q-phone-input', import('components/QPhoneInput'))
<q-phone-input v-model="landline" ref="landline" outlined dark label="Tel. Fixo"></q-phone-input>
<q-phone-input v-model="cellphone" ref="cellphone" outlined dark label="Celular"></q-phone-input>
E como de um praxe, um codepen:
4 - TobyMosque Utility Belt
Transparent Wrappers como implementados acima, vem com um custo, eles não repassam tudo para o componente original, por exemplo, a propriedade key
e os methods
não podem ser acessados diretamente:
por exemplo, o seguinte código resultaria em erro:
<div class="q-gutter-sm">
<q-phone-input v-model="landline" ref="landline" outlined dark label="Tel. Fixo"></q-phone-input>
<q-phone-input ref="cellphone" v-model="cellphone" ref="cellphone" outlined dark label="Celular"></q-phone-input>
</div>
export default {
data () {
return {
landline: '',
cellphone: ''
}
},
mounted () {
this.$refs.cellphone.focus()
}
}
Isto ocorre, por que focus
é um método do QInput
e não do QPhoneInput
, uma forma de fazer o código acima funcionar, seria:
this.$refs.cellphone.$refs.root.focus()
nada pratico, não é?
Para resolver este problema, eu criei uma extensão com alguns utilitários, um deles é responsável por criar Transparent Wrappers completos.
** Coming Soon **
Top comments (1)
Boaaaa @tobias , essa parte de Transparent Wrappers me ajudou muito, obrigado por compartilhar! Abç