How do I remove a Quasar q-input prop on button click? - vue.js

I have a quasar q-input element that I want to enable and disable at the click of a button.
I have added a button to the input using v-slot:after but I don't know how to remove the disable prop when it is clicked.
P.S. I am using Quasar + Vue 3 + TypeScript.
<template>
<q-input
disable
type="text"
autocomplete="given-name"
label="First Name"
v-model="firstName"
>
<template v-slot:after>
<q-btn
label="Edit"
#click="handleClick"
/>
</template>
</q-input>
</template>
<script setup lang="ts">
import { ref, Ref } from 'vue';
const firstName: Ref<string | null> = ref(null);
const handleClick = () => {
//Remove the "disable" prop from the q-input element
}
</script>

In VUE you must think backwards.
Put in input :disable="myDisable"
Create the ref myDisable in the setup and put your initial value that you need true or false
In your handleClick change the value of myDisable =!myDisable

Related

Passing a prop to child component isn't working on Vue 3

I have this simple file:
<script setup>
import { ref } from 'vue'
import TheHeader from '#/components/_headerbar/TheHeader.vue'
import TheSidebar from '#/components/_sidebar/TheSidebar.vue'
const sidebarState = ref(false)
const onSidebarToggle = () => {
sidebarState.value = !sidebarState.value
}
</script>
<template>
<QLayout view="hHh lpR fFf">
<TheHeader #toggle-sidebar="onSidebarToggle" />
<TheSidebar :sidebar-state="sidebarState.value" />
<QPageContainer>
<RouterView v-slot="{ Component }">
<component :is="Component" />
</RouterView>
</QPageContainer>
</QLayout>
</template>
The sidebarState variable here updates just fine everytime the event toggle-sidebar is fired, but the prop that recieve its value never updates and I just don't know what is happening.
This is the TheSidebar.vue file:
<script setup>
const props = defineProps({
sidebarState: {
type: Boolean,
default: true
}
})
</script>
<template>
<QDrawer
:model-value="props.sidebarState"
show-if-above
side="left"
bordered
>
content
</QDrawer>
</template>
Debugging here I can tell the sidebarState prop from TheSidebar.vue file just never changes, even though the data prop of TheHeader.vue sidebarState changes just normally.
What am I doing wrong?
You shouldn't use .value in template with refs of top-level properties. The value is automatically unwrapped for you (note: make sure the toggle in the top left of the docs is switched from "Options" to "Composition" for link to correctly work).
Simply remove .value and your code should work
<TheSidebar :sidebar-state="sidebarState" />

How to control the visibility of a child component from the parent using a prop?

I'm trying to control the visibility of a child component from the parent using a prop.
My child component has a visible prop. To avoid mutating the visible prop I assigned it to a local isVisible ref, and I'm using that to conditionally show and hide a form.
But when I try to hide the form using a button with #click="isVisible = false" nothing happens, and I get a console error saying Set operation on key "visible" failed: target is readonly.
It's also confusing why the error messsage is referring to the visible prop because I am using the local isVisible variable instead.
This is the ChildForm.vue (child component):
<template>
<q-form v-if="isVisible">
<q-input v-model="modelValue" />
<q-btn label="Hide The Form" #click="isVisible = false" />
</q-form>
</template>
<script setup lang="ts">
import { ref, toRef } from 'vue';
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
});
const isVisible = toRef(props, 'visible');
const modelValue = ref('');
</script>
This is the ParentPage.vue (parent component):
<template>
<child-form :visible="showForm" />
<q-btn label="Show the Form" #click="showForm = true" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
import ChildForm from 'src/components/ChildForm.vue';
const showForm = ref(false);
</script>
toRef() retains the reactive connection to the original source, so modifying isVisible effectively modifies the original prop, which is supposed to be readonly, leading to the warning you observed.
But I think you're actually trying to keep the child's visible prop in sync with the parent's showForm prop, such that updates in the child are automatically reflected in the parent. v-model is the tool for that problem.
In the parent, bind showForm to v-model:visible:
<child-form v-model:visible="showForm">
In the child, emit an update:visible event with the desired value whenever you want to update the parent:
<template>
<q-form v-if="visible">
<q-input v-model="modelValue" /> 👇
<q-btn label="Hide The Form" #click="$emit('update:visible', false)" />
</q-form>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
})
const modelValue = ref('')
</script>
demo

How to correctly pass a v-model down to a Quasar q-input base component?

I am using Quasar to build my Vue app and I want to create a base component (a.k.a. presentational, dumb, or pure component) using q-input.
I have a created a SFC named VInput.vue as my base component, it looks like this:
<template>
<q-input
outlined
class="q-mb-md"
hide-bottom-space
/>
</template>
Then I created a SFC named TestForm.vue that looks like this:
<template>
<q-form>
<v-input label="Email" v-model="email" />
</q-form>
</template>
<script setup lang="ts">
import VInput from './VInput.vue';
import { ref } from 'vue';
const email = ref('john#example.com');
</script>
The label="Email" v-model="email" parts are passed down to my VInput.vue base component and correctly rendered on the page.
But there is a typescript error on q-input of the VInput.vue base component because q-input requires a v-model:
`Type '{ outlined: true; class: string; hideBottomSpace: true; "hide-bottom-space": boolean; }' is not assignable to type 'IntrinsicAttributes & VNodeProps & AllowedComponentProps & ComponentCustomProps & QInputProps'.`
`Property 'modelValue' is missing in type '{ outlined: true; class: string; hideBottomSpace: true; "hide-bottom-space": boolean; }' but required in type 'QInputProps'.ts(2322)`.
So how do I code the VInput.vue base component without knowing the v-model value head of time?
I have come up with the below solution, which seems to work because I think the v-model passed down is overiding the base component v-model.
But I wanted to ask to make sure I wasn't screwing something up.
Is this the correct way of doing things? It seems hacky.
<template>
<q-input v-model="inputText" outlined class="q-mb-md" hide-bottom-space />
</template>
<script setup lang="ts">
const inputText = '';
</script>
I found a couple of solutions:
Solution 1
It involves splitting the v-model into it seperate parts (:model-value and #update:model-value, and then passing in the text value as a prop.
Base component VInput.vue:
<template>
<q-input
outlined
class="q-mb-md"
hide-bottom-space
:model-value="text"
#update:model-value="(value) => emit('update:text', value)"
/>
</template>
<script setup lang="ts">
defineProps({
text: {
required: false,
type: String,
},
});
const emit = defineEmits(['update:text']);
</script>
Solution 2
Extracting the prop and using toRef on it.
<template>
<q-input outlined class="q-mb-md" hide-bottom-space v-model="textProp" />
</template>
<script setup lang="ts">
import { toRef } from 'vue';
const props = defineProps({
text: {
required: false,
type: String,
default: '',
},
});
const textProp = toRef(props, 'text');
</script>

Mouseover function wont toggle

I am trying to change the hover value on hover, but it doesn't seem to work whenever I hover over the flex element, what am I doing wrong?
html
template(#default="{ toggle, toggled, hover }")
.flex(#click="toggle" #mouseover="hover = !hover" )
box-icon(type="solid" name="chevron-down" v-if="hover")
component
<template lang="pug">
slot(:toggled="toggled" :toggle="toggle" :hover="hover")
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const toggled = ref(false)
const hover = ref(false)
function toggle() {
toggled.value = !toggled.value
}
return { toggled, toggle, hover }
}
}
</script>
#mouseover is not an event in vuejs.
You will have to use #mouseenter and #mouseleave.
Also it seems like you cannot edit a ref value, you will have to create a function in your template that changes that value. That is something I cannot explain myself right now. Nevertheless I made it work.
Here is a working example :
// Test.vue component
<script setup>
const toggled = ref(false)
const hovered = ref(false)
const hover = (value) => hovered.value = value
const toggle = () => toggled.value = ! toggled.value
</script>
<template>
<slot :toggled="toggled" :toggle="toggle" :hovered="hovered" :hover="hover" />
</template>
<Test>
<template #default="{toggle, toggled, hover, hovered}">
<button #click="toggle" #mouseenter="hover(true)" #mouseleave="hover(false)">
<p>Toggled : {{ toggled }}</p>
<p>Hovered : {{ hovered }}</p>
</button>
</template>
</Test>

Vuetify v-dialog do not show in spite of value attribute equal to true

I am using vuex store state to show/hide Vuetify v-dialog in my NuxtJS app. Following are the code excerpt:
Vuex Store:
export const state = () => ({
dialogOpen: false
});
export const mutations = {
setDialogToOpen(state) {
state.dialogOpen = true;
},
setDialogToClosed(state) {
state.dialogOpen = false;
}
};
export const getters = {
isDialogOpen: state => {
return state.dialogOpen;
}
};
Dialog Component:
<v-dialog
v-model="isDialogOpen"
#input="setDialogToClosed"
max-width="600px"
class="pa-0 ma-0"
>
...
</v-dialog>
computed: {
...mapGetters("store", ["isDialogOpen"])
},
methods: {
...mapMutations({
setDialogToClosed: "store/setDialogToClosed"
})
}
This all works fine but when I redirect from one page to another page like below it stops working.
this.$router.push("/videos/" + id);
I hit browser refresh and it starts working again. Using the Chrome Vue dev tools, I can see the state is set correctly in the store as well as in the v-dialog value property as shown below
In Vuex store
In v-dialog component property
Yet the dialog is not visible. Any clue what is happening?
I am using NuxtJS 2.10.2 and #nuxtJS/Vuetify plugin 1.9.0
Issue was due to v-dialog not being wrapped inside v-app
My code was structured like this
default layout
<template>
<div>
<v-dialog
v-model="isDialogOpen"
#input="setDialogToClosed"
max-width="600px"
class="pa-0 ma-0"
>
<nuxt />
</div>
</template>
Below is the code for index page which replaces nuxt tag above at runtime.
<template>
<v-app>
<v-content>
...
</v-content>
</v-app>
</template>
So, in the final code v-dialog was not wrapped inside v-app. Moving v-app tag to default layout fixed it
<template>
<v-app>
<v-dialog
v-model="isDialogOpen"
#input="setDialogToClosed"
max-width="600px"
class="pa-0 ma-0"
>
<nuxt />
</v-app>
</template>